feat: 新增微信参数配置模块(Model、DTO、Store、Service)

- wechat_config.go: WechatConfig GORM 模型,含 ProviderTypeWechat/Fuiou 常量
- wechat_config_dto.go: Create/Update/List 请求 DTO,响应 DTO 含脱敏逻辑
- wechat_config_store.go: CRUD、GetActive、ActivateInTx(事务内唯一激活)、软删除保护查询
- service.go: 业务逻辑,按渠道校验必填字段、Redis 缓存管理(wechat:config:active)、删除保护、审计日志

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-03-16 23:29:11 +08:00
parent aa41a5ed5e
commit c86afbfa8f
4 changed files with 860 additions and 0 deletions

View File

@@ -0,0 +1,186 @@
package dto
import (
"fmt"
"github.com/break/junhong_cmp_fiber/internal/model"
)
// CreateWechatConfigRequest 创建微信参数配置请求
type CreateWechatConfigRequest struct {
Name string `json:"name" validate:"required,min=1,max=100" required:"true" minLength:"1" maxLength:"100" description:"配置名称"`
Description string `json:"description" validate:"omitempty,max=500" maxLength:"500" description:"配置描述"`
ProviderType string `json:"provider_type" validate:"required,oneof=wechat fuiou" required:"true" description:"支付渠道类型 (wechat:微信直连, fuiou:富友)"`
OaAppID string `json:"oa_app_id" validate:"omitempty,max=100" maxLength:"100" description:"公众号AppID"`
OaAppSecret string `json:"oa_app_secret" validate:"omitempty,max=200" maxLength:"200" description:"公众号AppSecret"`
OaToken string `json:"oa_token" validate:"omitempty,max=200" maxLength:"200" description:"公众号Token"`
OaAesKey string `json:"oa_aes_key" validate:"omitempty,max=200" maxLength:"200" description:"公众号AES加密Key"`
OaOAuthRedirectURL string `json:"oa_oauth_redirect_url" validate:"omitempty,max=500" maxLength:"500" description:"OAuth回调地址"`
MiniappAppID string `json:"miniapp_app_id" validate:"omitempty,max=100" maxLength:"100" description:"小程序AppID"`
MiniappAppSecret string `json:"miniapp_app_secret" validate:"omitempty,max=200" maxLength:"200" description:"小程序AppSecret"`
WxMchID string `json:"wx_mch_id" validate:"omitempty,max=100" maxLength:"100" description:"微信商户号"`
WxAPIV3Key string `json:"wx_api_v3_key" validate:"omitempty,max=200" maxLength:"200" description:"微信APIv3密钥"`
WxAPIV2Key string `json:"wx_api_v2_key" validate:"omitempty,max=200" maxLength:"200" description:"微信APIv2密钥"`
WxCertContent string `json:"wx_cert_content" validate:"omitempty" description:"微信支付证书内容(PEM格式)"`
WxKeyContent string `json:"wx_key_content" validate:"omitempty" description:"微信支付密钥内容(PEM格式)"`
WxSerialNo string `json:"wx_serial_no" validate:"omitempty,max=200" maxLength:"200" description:"微信证书序列号"`
WxNotifyURL string `json:"wx_notify_url" validate:"omitempty,max=500" maxLength:"500" description:"微信支付回调地址"`
FyInsCd string `json:"fy_ins_cd" validate:"omitempty,max=50" maxLength:"50" description:"富友机构号"`
FyMchntCd string `json:"fy_mchnt_cd" validate:"omitempty,max=50" maxLength:"50" description:"富友商户号"`
FyTermID string `json:"fy_term_id" validate:"omitempty,max=50" maxLength:"50" description:"富友终端号"`
FyPrivateKey string `json:"fy_private_key" validate:"omitempty" description:"富友私钥(PEM格式)"`
FyPublicKey string `json:"fy_public_key" validate:"omitempty" description:"富友公钥(PEM格式)"`
FyAPIURL string `json:"fy_api_url" validate:"omitempty,max=500" maxLength:"500" description:"富友API地址"`
FyNotifyURL string `json:"fy_notify_url" validate:"omitempty,max=500" maxLength:"500" description:"富友支付回调地址"`
}
// UpdateWechatConfigRequest 更新微信参数配置请求
type UpdateWechatConfigRequest struct {
Name *string `json:"name" validate:"omitempty,min=1,max=100" minLength:"1" maxLength:"100" description:"配置名称"`
Description *string `json:"description" validate:"omitempty,max=500" maxLength:"500" description:"配置描述"`
ProviderType *string `json:"provider_type" validate:"omitempty,oneof=wechat fuiou" description:"支付渠道类型 (wechat:微信直连, fuiou:富友)"`
OaAppID *string `json:"oa_app_id" validate:"omitempty,max=100" maxLength:"100" description:"公众号AppID"`
OaAppSecret *string `json:"oa_app_secret" validate:"omitempty,max=200" maxLength:"200" description:"公众号AppSecret"`
OaToken *string `json:"oa_token" validate:"omitempty,max=200" maxLength:"200" description:"公众号Token"`
OaAesKey *string `json:"oa_aes_key" validate:"omitempty,max=200" maxLength:"200" description:"公众号AES加密Key"`
OaOAuthRedirectURL *string `json:"oa_oauth_redirect_url" validate:"omitempty,max=500" maxLength:"500" description:"OAuth回调地址"`
MiniappAppID *string `json:"miniapp_app_id" validate:"omitempty,max=100" maxLength:"100" description:"小程序AppID"`
MiniappAppSecret *string `json:"miniapp_app_secret" validate:"omitempty,max=200" maxLength:"200" description:"小程序AppSecret"`
WxMchID *string `json:"wx_mch_id" validate:"omitempty,max=100" maxLength:"100" description:"微信商户号"`
WxAPIV3Key *string `json:"wx_api_v3_key" validate:"omitempty,max=200" maxLength:"200" description:"微信APIv3密钥"`
WxAPIV2Key *string `json:"wx_api_v2_key" validate:"omitempty,max=200" maxLength:"200" description:"微信APIv2密钥"`
WxCertContent *string `json:"wx_cert_content" validate:"omitempty" description:"微信支付证书内容(PEM格式)"`
WxKeyContent *string `json:"wx_key_content" validate:"omitempty" description:"微信支付密钥内容(PEM格式)"`
WxSerialNo *string `json:"wx_serial_no" validate:"omitempty,max=200" maxLength:"200" description:"微信证书序列号"`
WxNotifyURL *string `json:"wx_notify_url" validate:"omitempty,max=500" maxLength:"500" description:"微信支付回调地址"`
FyInsCd *string `json:"fy_ins_cd" validate:"omitempty,max=50" maxLength:"50" description:"富友机构号"`
FyMchntCd *string `json:"fy_mchnt_cd" validate:"omitempty,max=50" maxLength:"50" description:"富友商户号"`
FyTermID *string `json:"fy_term_id" validate:"omitempty,max=50" maxLength:"50" description:"富友终端号"`
FyPrivateKey *string `json:"fy_private_key" validate:"omitempty" description:"富友私钥(PEM格式)"`
FyPublicKey *string `json:"fy_public_key" validate:"omitempty" description:"富友公钥(PEM格式)"`
FyAPIURL *string `json:"fy_api_url" validate:"omitempty,max=500" maxLength:"500" description:"富友API地址"`
FyNotifyURL *string `json:"fy_notify_url" validate:"omitempty,max=500" maxLength:"500" description:"富友支付回调地址"`
}
// WechatConfigListRequest 微信参数配置列表查询请求
type WechatConfigListRequest struct {
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量"`
ProviderType *string `json:"provider_type" query:"provider_type" validate:"omitempty,oneof=wechat fuiou" description:"支付渠道类型 (wechat:微信直连, fuiou:富友)"`
IsActive *bool `json:"is_active" query:"is_active" description:"是否激活 (true:已激活, false:未激活)"`
}
// WechatConfigResponse 微信参数配置响应
type WechatConfigResponse struct {
ID uint `json:"id" description:"配置ID"`
Name string `json:"name" description:"配置名称"`
Description string `json:"description" description:"配置描述"`
ProviderType string `json:"provider_type" description:"支付渠道类型 (wechat:微信直连, fuiou:富友)"`
IsActive bool `json:"is_active" description:"是否激活"`
OaAppID string `json:"oa_app_id" description:"公众号AppID"`
OaAppSecret string `json:"oa_app_secret" description:"公众号AppSecret(已脱敏)"`
OaToken string `json:"oa_token" description:"公众号Token(已脱敏)"`
OaAesKey string `json:"oa_aes_key" description:"公众号AES加密Key(已脱敏)"`
OaOAuthRedirectURL string `json:"oa_oauth_redirect_url" description:"OAuth回调地址"`
MiniappAppID string `json:"miniapp_app_id" description:"小程序AppID"`
MiniappAppSecret string `json:"miniapp_app_secret" description:"小程序AppSecret(已脱敏)"`
WxMchID string `json:"wx_mch_id" description:"微信商户号"`
WxAPIV3Key string `json:"wx_api_v3_key" description:"微信APIv3密钥(已脱敏)"`
WxAPIV2Key string `json:"wx_api_v2_key" description:"微信APIv2密钥(已脱敏)"`
WxCertContent string `json:"wx_cert_content" description:"微信支付证书内容(配置状态)"`
WxKeyContent string `json:"wx_key_content" description:"微信支付密钥内容(配置状态)"`
WxSerialNo string `json:"wx_serial_no" description:"微信证书序列号"`
WxNotifyURL string `json:"wx_notify_url" description:"微信支付回调地址"`
FyInsCd string `json:"fy_ins_cd" description:"富友机构号"`
FyMchntCd string `json:"fy_mchnt_cd" description:"富友商户号"`
FyTermID string `json:"fy_term_id" description:"富友终端号"`
FyPrivateKey string `json:"fy_private_key" description:"富友私钥(配置状态)"`
FyPublicKey string `json:"fy_public_key" description:"富友公钥(配置状态)"`
FyAPIURL string `json:"fy_api_url" description:"富友API地址"`
FyNotifyURL string `json:"fy_notify_url" description:"富友支付回调地址"`
CreatedAt string `json:"created_at" description:"创建时间"`
UpdatedAt string `json:"updated_at" description:"更新时间"`
}
// WechatConfigListResponse 微信参数配置列表响应
type WechatConfigListResponse struct {
List []*WechatConfigResponse `json:"list" description:"配置列表"`
Total int64 `json:"total" description:"总数"`
Page int `json:"page" description:"当前页"`
PageSize int `json:"page_size" description:"每页数量"`
}
// MaskShortSecret 对短密钥进行脱敏处理
// 长度小于 8 返回 "***"否则保留前4位和后4位
func MaskShortSecret(val string) string {
if len(val) < 8 {
return "***"
}
return fmt.Sprintf("%s***%s", val[:4], val[len(val)-4:])
}
// MaskLongSecret 对长密钥/证书进行脱敏处理
// 空值返回 "[未配置]",否则返回 "[已配置]"
func MaskLongSecret(val string) string {
if val == "" {
return "[未配置]"
}
return "[已配置]"
}
// FromWechatConfigModel 从模型转换为响应 DTO敏感字段自动脱敏
func FromWechatConfigModel(m *model.WechatConfig) *WechatConfigResponse {
resp := &WechatConfigResponse{
ID: m.ID,
Name: m.Name,
ProviderType: m.ProviderType,
IsActive: m.IsActive,
OaAppID: m.OaAppID,
OaAppSecret: MaskShortSecret(m.OaAppSecret),
OaToken: MaskShortSecret(m.OaToken),
OaAesKey: MaskShortSecret(m.OaAesKey),
OaOAuthRedirectURL: m.OaOAuthRedirectURL,
MiniappAppID: m.MiniappAppID,
MiniappAppSecret: MaskShortSecret(m.MiniappAppSecret),
WxMchID: m.WxMchID,
WxAPIV3Key: MaskShortSecret(m.WxAPIV3Key),
WxAPIV2Key: MaskShortSecret(m.WxAPIV2Key),
WxCertContent: MaskLongSecret(m.WxCertContent),
WxKeyContent: MaskLongSecret(m.WxKeyContent),
WxSerialNo: m.WxSerialNo,
WxNotifyURL: m.WxNotifyURL,
FyInsCd: m.FyInsCd,
FyMchntCd: m.FyMchntCd,
FyTermID: m.FyTermID,
FyPrivateKey: MaskLongSecret(m.FyPrivateKey),
FyPublicKey: MaskLongSecret(m.FyPublicKey),
FyAPIURL: m.FyAPIURL,
FyNotifyURL: m.FyNotifyURL,
CreatedAt: m.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: m.UpdatedAt.Format("2006-01-02 15:04:05"),
}
if m.Description != nil {
resp.Description = *m.Description
}
return resp
}

View File

@@ -0,0 +1,56 @@
package model
import "gorm.io/gorm"
// 支付渠道类型常量
const (
ProviderTypeWechat = "wechat" // 微信直连
ProviderTypeFuiou = "fuiou" // 富友
)
// WechatConfig 微信参数配置模型
// 管理微信公众号 OAuth、小程序、微信直连支付、富友支付等多套配置
// 同一时间只有一条记录处于 is_active=true全局唯一生效配置
type WechatConfig struct {
gorm.Model
BaseModel `gorm:"embedded"`
Name string `gorm:"column:name;type:varchar(100);not null;comment:配置名称" json:"name"`
Description *string `gorm:"column:description;type:text;comment:配置描述" json:"description,omitempty"`
ProviderType string `gorm:"column:provider_type;type:varchar(20);not null;comment:支付渠道类型(wechat-微信直连,fuiou-富友)" json:"provider_type"`
IsActive bool `gorm:"column:is_active;type:boolean;not null;default:false;comment:是否激活(全局唯一)" json:"is_active"`
// OAuth 公众号
OaAppID string `gorm:"column:oa_app_id;type:varchar(100);not null;default:'';comment:公众号AppID" json:"oa_app_id"`
OaAppSecret string `gorm:"column:oa_app_secret;type:varchar(200);not null;default:'';comment:公众号AppSecret" json:"oa_app_secret"`
OaToken string `gorm:"column:oa_token;type:varchar(200);default:'';comment:公众号Token" json:"oa_token"`
OaAesKey string `gorm:"column:oa_aes_key;type:varchar(200);default:'';comment:公众号AES加密Key" json:"oa_aes_key"`
OaOAuthRedirectURL string `gorm:"column:oa_oauth_redirect_url;type:varchar(500);default:'';comment:OAuth回调地址" json:"oa_oauth_redirect_url"`
// OAuth 小程序
MiniappAppID string `gorm:"column:miniapp_app_id;type:varchar(100);default:'';comment:小程序AppID" json:"miniapp_app_id"`
MiniappAppSecret string `gorm:"column:miniapp_app_secret;type:varchar(200);default:'';comment:小程序AppSecret" json:"miniapp_app_secret"`
// 支付-微信直连
WxMchID string `gorm:"column:wx_mch_id;type:varchar(100);default:'';comment:微信商户号" json:"wx_mch_id"`
WxAPIV3Key string `gorm:"column:wx_api_v3_key;type:varchar(200);default:'';comment:微信APIv3密钥" json:"wx_api_v3_key"`
WxAPIV2Key string `gorm:"column:wx_api_v2_key;type:varchar(200);default:'';comment:微信APIv2密钥" json:"wx_api_v2_key"`
WxCertContent string `gorm:"column:wx_cert_content;type:text;default:'';comment:微信支付证书内容" json:"wx_cert_content"`
WxKeyContent string `gorm:"column:wx_key_content;type:text;default:'';comment:微信支付密钥内容" json:"wx_key_content"`
WxSerialNo string `gorm:"column:wx_serial_no;type:varchar(200);default:'';comment:微信证书序列号" json:"wx_serial_no"`
WxNotifyURL string `gorm:"column:wx_notify_url;type:varchar(500);default:'';comment:微信支付回调地址" json:"wx_notify_url"`
// 支付-富友
FyInsCd string `gorm:"column:fy_ins_cd;type:varchar(50);default:'';comment:富友机构号" json:"fy_ins_cd"`
FyMchntCd string `gorm:"column:fy_mchnt_cd;type:varchar(50);default:'';comment:富友商户号" json:"fy_mchnt_cd"`
FyTermID string `gorm:"column:fy_term_id;type:varchar(50);default:'';comment:富友终端号" json:"fy_term_id"`
FyPrivateKey string `gorm:"column:fy_private_key;type:text;default:'';comment:富友私钥" json:"fy_private_key"`
FyPublicKey string `gorm:"column:fy_public_key;type:text;default:'';comment:富友公钥" json:"fy_public_key"`
FyAPIURL string `gorm:"column:fy_api_url;type:varchar(500);default:'';comment:富友API地址" json:"fy_api_url"`
FyNotifyURL string `gorm:"column:fy_notify_url;type:varchar(500);default:'';comment:富友支付回调地址" json:"fy_notify_url"`
}
// TableName 指定表名
func (WechatConfig) TableName() string {
return "tb_wechat_config"
}