This commit is contained in:
2025-10-15 00:01:02 +08:00
parent 76ea87f60f
commit 3406e8f82d
5 changed files with 245 additions and 0 deletions

View File

@@ -32,6 +32,9 @@ service main {
@doc "wechat h5 auth"
@handler wxH5Auth
post /user/wxh5Auth (WXH5AuthReq) returns (WXH5AuthResp)
@handler getSignature
post /wechat/getSignature (GetSignatureReq) returns (GetSignatureResp)
}
type (
@@ -57,6 +60,18 @@ type (
}
)
type (
GetSignatureReq {
Url string `json:"url"`
}
GetSignatureResp {
AppId string `json:"appId"`
Timestamp int64 `json:"timestamp"`
NonceStr string `json:"nonceStr"`
Signature string `json:"signature"`
}
)
type (
WXH5AuthReq {
Code string `json:"code"`

View File

@@ -1050,6 +1050,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/user/wxh5Auth",
Handler: user.WxH5AuthHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/wechat/getSignature",
Handler: user.GetSignatureHandler(serverCtx),
},
},
rest.WithPrefix("/api/v1"),
)

View File

@@ -0,0 +1,29 @@
package user
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"hm-server/app/main/api/internal/logic/user"
"hm-server/app/main/api/internal/svc"
"hm-server/app/main/api/internal/types"
"hm-server/common/result"
"hm-server/pkg/lzkit/validator"
)
func GetSignatureHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetSignatureReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := user.NewGetSignatureLogic(r.Context(), svcCtx)
resp, err := l.GetSignature(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,185 @@
package user
import (
"context"
"crypto/sha1"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"sort"
"strings"
"time"
"hm-server/app/main/api/internal/svc"
"hm-server/app/main/api/internal/types"
"hm-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type GetSignatureLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetSignatureLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSignatureLogic {
return &GetSignatureLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetSignatureLogic) GetSignature(req *types.GetSignatureReq) (resp *types.GetSignatureResp, err error) {
// 1. 获取access_token
accessToken, err := l.getAccessToken()
if err != nil {
l.Errorf("获取access_token失败: %v", err)
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取access_token失败: %v", err)
}
// 2. 获取jsapi_ticket
jsapiTicket, err := l.getJsapiTicket(accessToken)
if err != nil {
l.Errorf("获取jsapi_ticket失败: %v", err)
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取jsapi_ticket失败: %v", err)
}
// 3. 生成签名
timestamp := time.Now().Unix()
nonceStr := l.generateNonceStr(16)
signature := l.generateSignature(jsapiTicket, nonceStr, timestamp, req.Url)
// 4. 返回完整的JS-SDK配置信息
return &types.GetSignatureResp{
AppId: l.svcCtx.Config.WechatH5.AppID,
Timestamp: timestamp,
NonceStr: nonceStr,
Signature: signature,
}, nil
}
// getAccessToken 获取微信公众号access_token
func (l *GetSignatureLogic) getAccessToken() (string, error) {
appID := l.svcCtx.Config.WechatH5.AppID
appSecret := l.svcCtx.Config.WechatH5.AppSecret
url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appID, appSecret)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
var result struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
if err = json.Unmarshal(body, &result); err != nil {
return "", err
}
if result.ErrCode != 0 {
return "", fmt.Errorf("获取access_token失败: errcode=%d, errmsg=%s", result.ErrCode, result.ErrMsg)
}
return result.AccessToken, nil
}
// getJsapiTicket 获取jsapi_ticket
func (l *GetSignatureLogic) getJsapiTicket(accessToken string) (string, error) {
url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi", accessToken)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
var result struct {
Ticket string `json:"ticket"`
ExpiresIn int `json:"expires_in"`
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
if err = json.Unmarshal(body, &result); err != nil {
return "", err
}
if result.ErrCode != 0 {
return "", fmt.Errorf("获取jsapi_ticket失败: errcode=%d, errmsg=%s", result.ErrCode, result.ErrMsg)
}
return result.Ticket, nil
}
// generateNonceStr 生成随机字符串
func (l *GetSignatureLogic) generateNonceStr(length int) string {
chars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
result := make([]byte, length)
for i := 0; i < length; i++ {
result[i] = chars[i%len(chars)]
}
return string(result)
}
// generateSignature 生成签名
func (l *GetSignatureLogic) generateSignature(jsapiTicket, nonceStr string, timestamp int64, urlStr string) string {
// 对URL进行解码避免重复编码
decodedURL, err := url.QueryUnescape(urlStr)
if err != nil {
decodedURL = urlStr
}
// 构建签名字符串
params := map[string]string{
"jsapi_ticket": jsapiTicket,
"noncestr": nonceStr,
"timestamp": fmt.Sprintf("%d", timestamp),
"url": decodedURL,
}
// 对参数进行字典序排序
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
// 拼接字符串
var signStr strings.Builder
for i, k := range keys {
if i > 0 {
signStr.WriteString("&")
}
signStr.WriteString(k)
signStr.WriteString("=")
signStr.WriteString(params[k])
}
// SHA1加密
h := sha1.New()
h.Write([]byte(signStr.String()))
signature := fmt.Sprintf("%x", h.Sum(nil))
return signature
}

View File

@@ -1465,6 +1465,17 @@ type GetRoleListResp struct {
Items []RoleListItem `json:"items"` // 列表
}
type GetSignatureReq struct {
Url string `json:"url"`
}
type GetSignatureResp struct {
AppId string `json:"appId"`
Timestamp int64 `json:"timestamp"`
NonceStr string `json:"nonceStr"`
Signature string `json:"signature"`
}
type GetWithdrawalReq struct {
Page int64 `form:"page"` // 页码
PageSize int64 `form:"page_size"` // 每页数据量