first-commit
This commit is contained in:
176
services/auth/wechat_qr.go
Normal file
176
services/auth/wechat_qr.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/services/context"
|
||||
|
||||
wechat_model "code.gitea.io/gitea/models/wechat"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
wechat_service "code.gitea.io/gitea/services/wechat"
|
||||
)
|
||||
|
||||
// Define a Wechat Error type message
|
||||
type WechatError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
// Implement the Error() method for the `WechatError` type
|
||||
func (e *WechatError) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
type ResponseData struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data wechat_service.WechatTempQRData `json:"data"`
|
||||
}
|
||||
|
||||
/**
|
||||
* GetWechatQRTicket 生成微信官方账户临时二维码
|
||||
*
|
||||
* @return string 生成的微信二维码的 ticket
|
||||
* @return string 生成的微信二维码图片URL
|
||||
* @return error 如果生成二维码过程中出现错误,则返回相应的错误信息
|
||||
*/
|
||||
func GetWechatQRTicket(ctx *context.Context) (wechatQrTicket string, QRImageURL string, errorGenerateQr error) {
|
||||
|
||||
sceneStr := setting.Domain
|
||||
qrExpireSeconds := setting.Wechat.TempQrExpireSeconds
|
||||
|
||||
// 构建请求的 URL
|
||||
url := fmt.Sprintf("https://%s/api/wechat/login/qr/generate?qrExpireSeconds=%d&sceneStr=%s", setting.Wechat.DefaultDomainName, qrExpireSeconds, sceneStr)
|
||||
|
||||
// 发送 GET 请求
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to send GET request: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to read response body: %v", err)
|
||||
}
|
||||
// 检查响应状态码
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", "", fmt.Errorf("received non-200 response status: %s", resp.Status)
|
||||
}
|
||||
|
||||
// 解析 JSON 响应
|
||||
var data ResponseData
|
||||
if err := json.Unmarshal(bodyBytes, &data); err != nil {
|
||||
return "", "", fmt.Errorf("failed to unmarshal JSON response: %v", err)
|
||||
}
|
||||
quit := make(chan bool) // 用来通知goroutine停止
|
||||
// 启动一个新的goroutine来进行轮询
|
||||
go func() {
|
||||
// 创建一个超时定时器通道
|
||||
timeout := time.After(time.Duration(qrExpireSeconds) * time.Second)
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
checkWechatQrTicketStatus(ctx, wechatQrTicket, quit)
|
||||
|
||||
case <-quit:
|
||||
log.Info("Stopping polling...")
|
||||
return
|
||||
|
||||
case <-timeout:
|
||||
log.Info("Polling timed out after", qrExpireSeconds, "seconds.")
|
||||
quit <- true // 发送停止信号给轮询goroutine
|
||||
}
|
||||
}
|
||||
}()
|
||||
return data.Data.Ticket, data.Data.QrImageSrcUrl, nil
|
||||
}
|
||||
|
||||
// Response represents the top-level JSON structure
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data wechat_service.WechatTempQRStatus `json:"data"`
|
||||
}
|
||||
|
||||
// 假设这是用于检查二维码状态的函数
|
||||
func checkWechatQrTicketStatus(ctx *context.Context, qrTicket string, quit chan bool) {
|
||||
|
||||
url := fmt.Sprintf("https://%s/api/wechat/login/qr/check-status?ticket=%s&_=%d",
|
||||
setting.Wechat.DefaultDomainName, qrTicket, time.Now().UnixMilli())
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
log.Error("There was a problem with the fetch operation:", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Error("Network response was not ok")
|
||||
return
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Error("Error reading response body:", err)
|
||||
return
|
||||
}
|
||||
// 解析 JSON 响应
|
||||
var data Response
|
||||
if err := json.Unmarshal(bodyBytes, &data); err != nil {
|
||||
log.Error("failed to unmarshal JSON response: %v", err)
|
||||
return
|
||||
}
|
||||
if data.Code == 0 && data.Data.IsScanned {
|
||||
log.Info("Caching WeChat QR Scanned Info: %s\n", bodyBytes)
|
||||
// {"code":0,"msg":"操作成功","data":{
|
||||
// "is_scanned":true,
|
||||
// "scene_str":"2d521f80047c42aba27ee9beade35985@p2Z6hfheDxg=",
|
||||
// "openid":"oQowJ6cD9WSuoxYaCc7mryfn-lVo",
|
||||
// "is_binded":true}}
|
||||
// 准备扫码状态VO对象
|
||||
qrStatus := wechat_service.WechatTempQRStatus{
|
||||
SceneStr: data.Data.SceneStr,
|
||||
IsScanned: data.Data.IsScanned,
|
||||
OpenId: data.Data.OpenId,
|
||||
}
|
||||
// 从微信服务器消息推送中解析扫码人的 OpenId
|
||||
user, err := wechat_model.QueryUserByOpenid(ctx, qrStatus.OpenId)
|
||||
if user == nil {
|
||||
// 未找到 OpenId 对应的 DevStar 用户信息,提示前端导向注册页
|
||||
qrStatus.IsBinded = false
|
||||
qrStatusString, err := qrStatus.Marshal2JSONString()
|
||||
if err == nil {
|
||||
// 将扫码人的微信公众号 OpenId 标记为等待注册,等待时间用户注册完成,默认24小时
|
||||
// key: qrScanResponseDigest.Ticket
|
||||
// value: JSON 字符串
|
||||
// TTL: setting.Wechat.TempQrExpireSeconds
|
||||
wechat_service.SetWechatQrTicketWithTTL(
|
||||
qrTicket,
|
||||
qrStatusString,
|
||||
setting.Wechat.RegisterationExpireSeconds)
|
||||
}
|
||||
quit <- true
|
||||
return
|
||||
}
|
||||
|
||||
qrStatus.IsBinded = true
|
||||
qrVOJsonString, err := qrStatus.Marshal2JSONString()
|
||||
if err == nil {
|
||||
wechat_service.SetWechatQrTicketWithTTL(
|
||||
qrTicket,
|
||||
qrVOJsonString,
|
||||
setting.Wechat.TempQrExpireSeconds)
|
||||
}
|
||||
quit <- true
|
||||
return
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user