Browse Source

#1494

add wechat event handler
tags/v1.22.2.2^2
Gitea 3 years ago
parent
commit
851d4be30a
12 changed files with 227 additions and 11 deletions
  1. +1
    -0
      models/models.go
  2. +3
    -0
      models/user.go
  3. +74
    -0
      models/wechat_bind.go
  4. +1
    -1
      modules/auth/wechat/access_token.go
  5. +11
    -0
      modules/auth/wechat/bind.go
  6. +5
    -0
      modules/auth/wechat/call.go
  7. +33
    -5
      modules/auth/wechat/client.go
  8. +6
    -2
      modules/auth/wechat/qr_code.go
  9. +19
    -1
      modules/redis/redis_client/client.go
  10. +4
    -0
      routers/api/v1/api.go
  11. +2
    -2
      routers/authentication/wechat.go
  12. +68
    -0
      routers/authentication/wechat_event.go

+ 1
- 0
models/models.go View File

@@ -136,6 +136,7 @@ func init() {
new(AiModelManage),
new(OfficialTag),
new(OfficialTagRepos),
new(WechatBindLog),
)

tablesStatistic = append(tablesStatistic,


+ 3
- 0
models/user.go View File

@@ -177,6 +177,9 @@ type User struct {
//BlockChain
PublicKey string `xorm:"INDEX"`
PrivateKey string `xorm:"INDEX"`

//Wechat
WechatOpenId string `xorm:"INDEX"`
}

// SearchOrganizationsOptions options to filter organizations


+ 74
- 0
models/wechat_bind.go View File

@@ -0,0 +1,74 @@
package models

import (
"code.gitea.io/gitea/modules/log"
"time"
)

type WechatBindAction int

const (
WECHAT_BIND WechatBindAction = iota + 1
WECHAT_UNBIND
)

type WechatBindLog struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"INDEX"`
WechatOpenId string `xorm:"INDEX"`
Action int
CreateTime time.Time `xorm:"INDEX created"`
}

func BindWechatOpenId(userId int64, wechatOpenId string) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}

param := &User{WechatOpenId: wechatOpenId}
n, err := sess.Where("ID = ?", userId).Update(param)
if err != nil {
log.Error("update wechat_open_id failed,e=%v", err)
return err
}
if n == 0 {
log.Error("update wechat_open_id failed,user not exist,userId=%d", userId)
return nil
}

logParam := &WechatBindLog{
UserID: userId,
WechatOpenId: wechatOpenId,
Action: int(WECHAT_BIND),
}
sess.Insert(logParam)
return sess.Commit()
}

func UnbindWechatOpenId(userId int64) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}

param := &User{WechatOpenId: ""}
n, err := x.Where("ID = ?", userId).Update(param)
if err != nil {
log.Error("update wechat_open_id failed,e=%v", err)
return err
}
if n == 0 {
log.Error("update wechat_open_id failed,user not exist,userId=%d", userId)
return nil
}
//todo 是否记录原有微信openId
logParam := &WechatBindLog{
UserID: userId,
Action: int(WECHAT_UNBIND),
}
sess.Insert(logParam)
return sess.Commit()
}

+ 1
- 1
modules/auth/wechat/access_token.go View File

@@ -14,7 +14,7 @@ func GetWechatAccessToken() string {
if token == EMPTY_REDIS_VAL {
return ""
}
live, _ := redis_client.TTL(token)
live, _ := redis_client.TTL(redis_key.WechatAccessTokenKey())
//refresh wechat access token when expire time less than 5 minutes
if live > 0 && live < 300 {
refreshAccessTokenCache()


+ 11
- 0
modules/auth/wechat/bind.go View File

@@ -0,0 +1,11 @@
package wechat

import "code.gitea.io/gitea/models"

func BindWechat(userId int64, wechatOpenId string) error {
return models.BindWechatOpenId(userId, wechatOpenId)
}

func UnbindWechat(userId int64) error {
return models.UnbindWechatOpenId(userId)
}

+ 5
- 0
modules/auth/wechat/call.go View File

@@ -0,0 +1,5 @@
package wechat

type WechatCall interface {
call()
}

+ 33
- 5
modules/auth/wechat/client.go View File

@@ -4,7 +4,9 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"encoding/json"
"fmt"
"github.com/go-resty/resty/v2"
"strconv"
"time"
)

@@ -17,6 +19,9 @@ const (
ACCESS_TOKEN_PATH = "/cgi-bin/token"
QR_CODE_Path = "/cgi-bin/qrcode/create"
ACTION_QR_STR_SCENE = "QR_STR_SCENE"

ERR_CODE_ACCESSTOKEN_EXPIRE = 42001
ERR_CODE_ACCESSTOKEN_INVALID = 40001
)

type AccessTokenResponse struct {
@@ -44,6 +49,11 @@ type Scene struct {
Scene_str string
}

type ErrorResponse struct {
Errcode int
Errmsg string
}

func getWechatRestyClient() *resty.Client {
if client == nil {
client = resty.New()
@@ -69,7 +79,7 @@ func callAccessToken() *AccessTokenResponse {
return &result
}

func callQRCodeCreate(sceneStr string) *QRCodeResponse {
func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) {
client := getWechatRestyClient()

body := &QRCodeRequest{
@@ -85,11 +95,29 @@ func callQRCodeCreate(sceneStr string) *QRCodeResponse {
SetBody(bodyJson).
SetResult(&result).
Post(setting.WechatApiHost + QR_CODE_Path)
if err != nil || result.Url == "" {
if err != nil {
log.Error("create QR code failed,e=%v", err)
return nil
return nil, false
}
errCode := getErrorCodeFromResponse(r)
if errCode == ERR_CODE_ACCESSTOKEN_EXPIRE || errCode == ERR_CODE_ACCESSTOKEN_INVALID {
return nil, true
}
if result.Url == "" {
return nil, false
}
//todo 识别token失效的错误码,重试机制
log.Info("%v", r)
return &result
return &result, false
}

func getErrorCodeFromResponse(r *resty.Response) int {
a := r.Body()
resultMap := make(map[string]interface{}, 0)
json.Unmarshal(a, &resultMap)
code := resultMap["errcode"]
if code == nil {
return -1
}
c, _ := strconv.Atoi(fmt.Sprint(code))
return c
}

+ 6
- 2
modules/auth/wechat/qr_code.go View File

@@ -1,6 +1,10 @@
package wechat

func GetWechatQRCode4Bind(sceneStr string) *QRCodeResponse {
r := callQRCodeCreate(sceneStr)
return r
result, retryFlag := callQRCodeCreate(sceneStr)
if retryFlag {
refreshAccessTokenCache()
result, _ = callQRCodeCreate(sceneStr)
}
return result
}

+ 19
- 1
modules/redis/redis_client/client.go View File

@@ -3,6 +3,7 @@ package redis_client
import (
"code.gitea.io/gitea/modules/labelmsg"
"fmt"
"github.com/gomodule/redigo/redis"
"math"
"strconv"
"time"
@@ -36,7 +37,24 @@ func Get(key string) (string, error) {
if reply == nil {
return "", err
}
return fmt.Sprint(reply), nil
s, _ := redis.String(reply, nil)
return s, nil

}

func Del(key string) (int, error) {
redisClient := labelmsg.Get()
defer redisClient.Close()

reply, err := redisClient.Do("DEL", key)
if err != nil {
return 0, err
}
if reply == nil {
return 0, err
}
s, _ := redis.Int(reply, nil)
return s, nil

}



+ 4
- 0
routers/api/v1/api.go View File

@@ -59,6 +59,7 @@
package v1

import (
"code.gitea.io/gitea/routers/authentication"
"net/http"
"strings"

@@ -995,6 +996,9 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/topics", func() {
m.Get("/search", repo.TopicSearch)
})
m.Group("/from_wechat", func() {
m.Get("/event", authentication.AcceptWechatEvent)
})
}, securityHeaders(), context.APIContexter(), sudo())
}



+ 2
- 2
routers/authentication/wechat.go View File

@@ -27,14 +27,14 @@ func GetQRCode4Bind(ctx *context.Context) {
r, err := createQRCode4Bind(userId)
if err != nil {
ctx.JSON(200, map[string]interface{}{
"code": 9,
"code": "9999",
"msg": "Get QR code failed",
})
return
}

ctx.JSON(200, map[string]interface{}{
"code": 0,
"code": "00",
"msg": "success",
"data": r,
})


+ 68
- 0
routers/authentication/wechat_event.go View File

@@ -0,0 +1,68 @@
package authentication

import (
"code.gitea.io/gitea/modules/auth/wechat"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/redis/redis_client"
"code.gitea.io/gitea/modules/redis/redis_key"
"encoding/xml"
"io/ioutil"
"strconv"
"time"
)

//<xml>
// <ToUserName><![CDATA[toUser]]></ToUserName>
// <FromUserName><![CDATA[FromUser]]></FromUserName>
// <CreateTime>123456789</CreateTime>
// <MsgType><![CDATA[event]]></MsgType>
// <Event><![CDATA[SCAN]]></Event>
// <EventKey><![CDATA[SCENE_VALUE]]></EventKey>
// <Ticket><![CDATA[TICKET]]></Ticket>
//</xml>
type WechatEvent struct {
ToUserName string
FromUserName string
CreateTime int64
MsgType string
Event string
EventKey string
Ticket string
}

type Xml struct {
ToUserName string
FromUserName string
CreateTime int64
MsgType string
Content string
}

// AcceptWechatEvent
func AcceptWechatEvent(ctx *context.Context) {
b, _ := ioutil.ReadAll(ctx.Req.Request.Body)
we := WechatEvent{}
xml.Unmarshal(b, &we)

log.Info("accept wechat event= %v", b)
key := redis_key.WechatBindingUserIdKey(we.EventKey)
val, _ := redis_client.Get(key)
if val == "" {
log.Error("sceneStr is not exist,sceneStr=%s", we.EventKey)
ctx.XML(200, "")
return
}
userId, _ := strconv.ParseInt(val, 10, 64)
//更新微信openId和流水
wechat.BindWechat(userId, we.EventKey)
reply := &Xml{
ToUserName: we.FromUserName,
FromUserName: we.ToUserName,
CreateTime: time.Now().Unix(),
MsgType: "text",
Content: "启智账号认证微信成功",
}
redis_client.Del(key)
ctx.XML(200, reply)
}

Loading…
Cancel
Save