Files
devstar-create-from-template/services/auth/wechat_qr.go

177 lines
5.1 KiB
Go
Raw Normal View History

2025-08-25 15:46:12 +08:00
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
}
}