Compare commits
3 Commits
0cba6fd6d5
...
fa554c3930
| Author | SHA1 | Date | |
|---|---|---|---|
| fa554c3930 | |||
| 1125701329 | |||
| ed1facc1b8 |
@@ -132,5 +132,6 @@ func initHandlers(svc *services, deps *Dependencies) *Handlers {
|
||||
WechatConfig: admin.NewWechatConfigHandler(svc.WechatConfig),
|
||||
AgentRecharge: admin.NewAgentRechargeHandler(svc.AgentRecharge, validate),
|
||||
Refund: admin.NewRefundHandler(svc.Refund),
|
||||
ClientWechat: app.NewClientWechatHandler(svc.WechatConfig, deps.Redis, deps.Logger),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ type Handlers struct {
|
||||
WechatConfig *admin.WechatConfigHandler
|
||||
AgentRecharge *admin.AgentRechargeHandler
|
||||
Refund *admin.RefundHandler
|
||||
ClientWechat *app.ClientWechatHandler
|
||||
}
|
||||
|
||||
// Middlewares 封装所有中间件
|
||||
|
||||
78
internal/handler/app/client_wechat.go
Normal file
78
internal/handler/app/client_wechat.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
wechatConfigSvc "github.com/break/junhong_cmp_fiber/internal/service/wechat_config"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/logger"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/wechat"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var clientWechatValidator = validator.New()
|
||||
|
||||
// ClientWechatHandler C 端微信相关处理器
|
||||
type ClientWechatHandler struct {
|
||||
wechatConfigService *wechatConfigSvc.Service
|
||||
wechatCache kernel.CacheInterface
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewClientWechatHandler 创建 C 端微信处理器
|
||||
func NewClientWechatHandler(
|
||||
wechatConfigService *wechatConfigSvc.Service,
|
||||
rdb *redis.Client,
|
||||
log *zap.Logger,
|
||||
) *ClientWechatHandler {
|
||||
var cache kernel.CacheInterface
|
||||
if rdb != nil {
|
||||
cache = wechat.NewRedisCache(rdb)
|
||||
}
|
||||
return &ClientWechatHandler{
|
||||
wechatConfigService: wechatConfigService,
|
||||
wechatCache: cache,
|
||||
logger: log,
|
||||
}
|
||||
}
|
||||
|
||||
// GetJSSDKConfig 获取微信 JSSDK 签名配置
|
||||
// GET /api/c/v1/wechat/jssdk-config
|
||||
func (h *ClientWechatHandler) GetJSSDKConfig(c *fiber.Ctx) error {
|
||||
var req dto.JSSDKConfigRequest
|
||||
if err := c.QueryParser(&req); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
if err := clientWechatValidator.Struct(&req); err != nil {
|
||||
logger.GetAppLogger().Warn("获取 JSSDK 配置参数校验失败", zap.Error(err))
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
|
||||
wechatConfig, err := h.wechatConfigService.GetActiveConfig(c.UserContext())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oaApp, err := wechat.NewOfficialAccountAppFromConfig(wechatConfig, h.wechatCache, h.logger)
|
||||
if err != nil {
|
||||
h.logger.Error("初始化微信公众号应用失败", zap.Error(err))
|
||||
return errors.New(errors.CodeWechatConfigUnavailable)
|
||||
}
|
||||
|
||||
oaService := wechat.NewOfficialAccountService(oaApp, h.logger)
|
||||
jssdkConfig, err := oaService.GetJSSDKConfig(c.UserContext(), req.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return response.Success(c, &dto.JSSDKConfigResponse{
|
||||
AppID: jssdkConfig.AppID,
|
||||
Timestamp: jssdkConfig.Timestamp,
|
||||
NonceStr: jssdkConfig.NonceStr,
|
||||
Signature: jssdkConfig.Signature,
|
||||
})
|
||||
}
|
||||
@@ -54,3 +54,17 @@ type WechatPayH5Params struct {
|
||||
ID uint `path:"id" description:"订单ID" required:"true"`
|
||||
WechatPayH5Request
|
||||
}
|
||||
|
||||
// JSSDKConfigRequest 获取 JSSDK 签名配置请求
|
||||
type JSSDKConfigRequest struct {
|
||||
URL string `query:"url" validate:"required" required:"true" description:"当前页面完整 URL(含协议和路径,不含 # 及其后内容)"`
|
||||
}
|
||||
|
||||
// JSSDKConfigResponse JSSDK 签名配置响应
|
||||
// 前端使用这些字段调用 wx.config() 初始化微信 JS-SDK
|
||||
type JSSDKConfigResponse struct {
|
||||
AppID string `json:"app_id" description:"公众号 AppID"`
|
||||
Timestamp int64 `json:"timestamp" description:"时间戳(秒)"`
|
||||
NonceStr string `json:"nonce_str" description:"随机字符串"`
|
||||
Signature string `json:"signature" description:"签名(SHA1)"`
|
||||
}
|
||||
|
||||
@@ -22,6 +22,15 @@ func RegisterPersonalCustomerRoutes(router fiber.Router, doc *openapi.Generator,
|
||||
authBasePath := "/auth"
|
||||
|
||||
// === 公开路由(无需认证)===
|
||||
Register(router, doc, basePath, "GET", "/wechat/jssdk-config", handlers.ClientWechat.GetJSSDKConfig, RouteSpec{
|
||||
Summary: "获取微信 JSSDK 签名配置",
|
||||
Description: "前端调用 wx.config() 初始化微信 JS-SDK 时所需的签名参数,需传入当前页面完整 URL",
|
||||
Tags: []string{"个人客户 - 微信"},
|
||||
Auth: false,
|
||||
Input: &dto.JSSDKConfigRequest{},
|
||||
Output: &dto.JSSDKConfigResponse{},
|
||||
})
|
||||
|
||||
Register(router, doc, basePath, "POST", authBasePath+"/verify-asset", handlers.ClientAuth.VerifyAsset, RouteSpec{
|
||||
Summary: "资产验证",
|
||||
Tags: []string{"个人客户 - 认证"},
|
||||
|
||||
@@ -62,5 +62,6 @@ func BuildDocHandlers() *bootstrap.Handlers {
|
||||
WechatConfig: admin.NewWechatConfigHandler(nil),
|
||||
AgentRecharge: admin.NewAgentRechargeHandler(nil, nil),
|
||||
Refund: admin.NewRefundHandler(nil),
|
||||
ClientWechat: app.NewClientWechatHandler(nil, nil, nil),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,37 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// GetJSSDKConfig 获取 JSSDK 签名配置
|
||||
// url 为当前页面完整 URL(含协议和路径,不含 # 及其后内容)
|
||||
func (s *OfficialAccountService) GetJSSDKConfig(ctx context.Context, url string) (*JSSDKConfig, error) {
|
||||
if url == "" {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "页面 URL 不能为空")
|
||||
}
|
||||
|
||||
signMap, err := s.app.JSSDK.ConfigSignature(ctx, url, "", 0)
|
||||
if err != nil {
|
||||
s.logger.Error("获取 JSSDK 签名失败", zap.String("url", url), zap.Error(err))
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取 JSSDK 签名失败")
|
||||
}
|
||||
|
||||
appID, _ := (*signMap)["appId"].(string)
|
||||
nonceStr, _ := (*signMap)["nonceStr"].(string)
|
||||
timestamp, _ := (*signMap)["timestamp"].(int64)
|
||||
signature, _ := (*signMap)["signature"].(string)
|
||||
|
||||
s.logger.Debug("获取 JSSDK 签名配置成功",
|
||||
zap.String("app_id", appID),
|
||||
zap.String("url", url),
|
||||
)
|
||||
|
||||
return &JSSDKConfig{
|
||||
AppID: appID,
|
||||
Timestamp: timestamp,
|
||||
NonceStr: nonceStr,
|
||||
Signature: signature,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// OfficialAccountService 微信公众号服务实现
|
||||
type OfficialAccountService struct {
|
||||
app *officialAccount.OfficialAccount
|
||||
|
||||
@@ -10,11 +10,23 @@ type Service interface {
|
||||
GetUserInfo(ctx context.Context, code string) (openID, unionID string, err error)
|
||||
}
|
||||
|
||||
// JSSDKConfig JSSDK 签名配置
|
||||
// 用于前端调用 wx.config() 初始化微信 JS-SDK
|
||||
type JSSDKConfig struct {
|
||||
AppID string // 公众号 AppID
|
||||
Timestamp int64 // 时间戳(秒)
|
||||
NonceStr string // 随机字符串
|
||||
Signature string // 签名(SHA1)
|
||||
}
|
||||
|
||||
// OfficialAccountServiceInterface 微信公众号服务接口
|
||||
type OfficialAccountServiceInterface interface {
|
||||
Service
|
||||
GetUserInfoDetailed(ctx context.Context, code string) (*UserInfo, error)
|
||||
GetUserInfoByToken(ctx context.Context, accessToken, openID string) (*UserInfo, error)
|
||||
// GetJSSDKConfig 获取 JSSDK 签名配置,用于前端调用 wx.config() 初始化
|
||||
// url 为当前页面完整 URL(含协议和路径,不含 # 及其后内容)
|
||||
GetJSSDKConfig(ctx context.Context, url string) (*JSSDKConfig, error)
|
||||
}
|
||||
|
||||
// PaymentServiceInterface 微信支付服务接口
|
||||
|
||||
Reference in New Issue
Block a user