Files
junhong_cmp_fiber/pkg/wechat/config.go
huang 4879d9bf07
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m29s
fix: 修复公众号登录初始化失败(PowerWeChat 日志写入权限)
NewOfficialAccountAppFromConfig 未设置 Log.Stdout=true,PowerWeChat SDK
默认尝试在工作目录创建 wechat/info.log 文件,容器环境无写权限导致
NewOfficialAccount 返回 permission denied,触发 1181 配置不可用错误。

支付配置已有此修复(NewPaymentAppFromConfig),公众号配置漏掉了。

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-13 14:39:54 +08:00

161 lines
5.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package wechat
import (
"fmt"
"os"
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount"
"github.com/ArtisanCloud/PowerWeChat/v3/src/payment"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
)
// NewRedisCache 使用项目现有的 Redis 客户端创建 PowerWeChat 的 Redis Cache
func NewRedisCache(rdb *redis.Client) kernel.CacheInterface {
return kernel.NewRedisClient(&kernel.UniversalOptions{
Addrs: []string{rdb.Options().Addr},
Password: rdb.Options().Password,
DB: rdb.Options().DB,
})
}
// NewOfficialAccountAppFromConfig 从数据库配置创建微信公众号应用实例
func NewOfficialAccountAppFromConfig(wechatConfig *model.WechatConfig, cache kernel.CacheInterface, logger *zap.Logger) (*officialAccount.OfficialAccount, error) {
if wechatConfig == nil {
return nil, fmt.Errorf("微信配置不能为空")
}
if wechatConfig.OaAppID == "" || wechatConfig.OaAppSecret == "" {
return nil, fmt.Errorf("微信公众号配置不完整:缺少 oa_app_id 或 oa_app_secret")
}
userConfig := &officialAccount.UserConfig{
AppID: wechatConfig.OaAppID,
Secret: wechatConfig.OaAppSecret,
Cache: cache,
// 禁止 PowerWeChat SDK 在当前目录创建 wechat/ 日志文件夹
// 容器内工作目录通常无写权限,会导致 "mkdir wechat: permission denied"
Log: officialAccount.Log{
Stdout: true,
},
}
if wechatConfig.OaToken != "" {
userConfig.Token = wechatConfig.OaToken
}
if wechatConfig.OaAesKey != "" {
userConfig.AESKey = wechatConfig.OaAesKey
}
app, err := officialAccount.NewOfficialAccount(userConfig)
if err != nil {
logger.Error("创建微信公众号应用失败", zap.Error(err))
return nil, fmt.Errorf("创建微信公众号应用失败: %w", err)
}
logger.Info("微信公众号应用初始化成功", zap.String("app_id", wechatConfig.OaAppID))
return app, nil
}
// NewMiniAppServiceFromConfig 从数据库配置创建小程序服务实例
func NewMiniAppServiceFromConfig(wechatConfig *model.WechatConfig, logger *zap.Logger) (*MiniAppService, error) {
if wechatConfig == nil {
return nil, fmt.Errorf("微信配置不能为空")
}
if wechatConfig.MiniappAppID == "" || wechatConfig.MiniappAppSecret == "" {
return nil, fmt.Errorf("小程序配置不完整:缺少 miniapp_app_id 或 miniapp_app_secret")
}
return NewMiniAppService(wechatConfig.MiniappAppID, wechatConfig.MiniappAppSecret, logger), nil
}
// NewPaymentAppFromConfig 从数据库配置创建微信支付应用实例
func NewPaymentAppFromConfig(wechatConfig *model.WechatConfig, appID string, cache kernel.CacheInterface, logger *zap.Logger) (*payment.Payment, error) {
if wechatConfig == nil {
return nil, fmt.Errorf("微信配置不能为空")
}
if appID == "" {
return nil, fmt.Errorf("appID 不能为空")
}
if wechatConfig.WxMchID == "" || wechatConfig.WxAPIV3Key == "" || wechatConfig.WxSerialNo == "" {
return nil, fmt.Errorf("微信支付配置不完整:缺少 wx_mch_id/wx_api_v3_key/wx_serial_no")
}
certPath, err := writeWechatPemTempFile("wechat_cert_*.pem", wechatConfig.WxCertContent)
if err != nil {
return nil, fmt.Errorf("写入微信支付证书失败: %w", err)
}
keyPath, err := writeWechatPemTempFile("wechat_key_*.pem", wechatConfig.WxKeyContent)
if err != nil {
return nil, fmt.Errorf("写入微信支付私钥失败: %w", err)
}
userConfig := &payment.UserConfig{
AppID: appID,
MchID: wechatConfig.WxMchID,
MchApiV3Key: wechatConfig.WxAPIV3Key,
Key: wechatConfig.WxAPIV2Key,
CertPath: certPath,
KeyPath: keyPath,
SerialNo: wechatConfig.WxSerialNo,
Cache: cache,
NotifyURL: wechatConfig.WxNotifyURL,
// 禁止 PowerWeChat SDK 在当前目录创建 wechat/ 日志文件夹
// 容器内工作目录通常无写权限,会导致 "mkdir wechat: permission denied"
Log: payment.Log{
Stdout: true,
},
}
app, err := payment.NewPayment(userConfig)
if err != nil {
logger.Error("创建微信支付应用失败", zap.Error(err))
return nil, fmt.Errorf("创建微信支付应用失败: %w", err)
}
logger.Info("微信支付应用初始化成功",
zap.String("app_id", appID),
zap.String("mch_id", wechatConfig.WxMchID),
)
return app, nil
}
// NewPaymentV2ServiceFromConfig 从数据库配置创建微信支付 v2 服务实例
// v2 仅需 APIv2Key不要求证书序列号和 v3 密钥
func NewPaymentV2ServiceFromConfig(wechatConfig *model.WechatConfig, appID string, logger *zap.Logger) (*PaymentV2Service, error) {
if wechatConfig == nil {
return nil, fmt.Errorf("微信配置不能为空")
}
if appID == "" {
return nil, fmt.Errorf("appID 不能为空")
}
if wechatConfig.WxMchID == "" || wechatConfig.WxAPIV2Key == "" {
return nil, fmt.Errorf("微信支付 v2 配置不完整:缺少 wx_mch_id 或 wx_api_v2_key")
}
return NewPaymentV2Service(appID, wechatConfig.WxMchID, wechatConfig.WxAPIV2Key, wechatConfig.WxNotifyURL, logger), nil
}
func writeWechatPemTempFile(pattern, content string) (string, error) {
if content == "" {
return "", fmt.Errorf("证书内容不能为空")
}
file, err := os.CreateTemp("", pattern)
if err != nil {
return "", err
}
if _, err = file.WriteString(content); err != nil {
file.Close()
return "", err
}
if err = file.Close(); err != nil {
return "", err
}
return file.Name(), nil
}