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,145 @@
package postgres
import (
"context"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/store"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
)
// WechatConfigStore 微信参数配置数据访问层
type WechatConfigStore struct {
db *gorm.DB
rdb *redis.Client
}
// NewWechatConfigStore 创建微信参数配置 Store 实例
func NewWechatConfigStore(db *gorm.DB, rdb *redis.Client) *WechatConfigStore {
return &WechatConfigStore{db: db, rdb: rdb}
}
// Create 创建微信参数配置
func (s *WechatConfigStore) Create(ctx context.Context, config *model.WechatConfig) error {
return s.db.WithContext(ctx).Create(config).Error
}
// GetByID 根据 ID 获取配置(遵循软删除)
func (s *WechatConfigStore) GetByID(ctx context.Context, id uint) (*model.WechatConfig, error) {
var config model.WechatConfig
if err := s.db.WithContext(ctx).First(&config, id).Error; err != nil {
return nil, err
}
return &config, nil
}
// GetByIDUnscoped 根据 ID 获取配置(包含已软删除的记录,用于回调处理)
func (s *WechatConfigStore) GetByIDUnscoped(ctx context.Context, id uint) (*model.WechatConfig, error) {
var config model.WechatConfig
if err := s.db.WithContext(ctx).Unscoped().First(&config, id).Error; err != nil {
return nil, err
}
return &config, nil
}
// List 查询配置列表,支持按 provider_type 和 is_active 过滤
func (s *WechatConfigStore) List(ctx context.Context, opts *store.QueryOptions, filters map[string]interface{}) ([]*model.WechatConfig, int64, error) {
var configs []*model.WechatConfig
var total int64
query := s.db.WithContext(ctx).Model(&model.WechatConfig{})
if providerType, ok := filters["provider_type"].(string); ok && providerType != "" {
query = query.Where("provider_type = ?", providerType)
}
if isActive, ok := filters["is_active"].(*bool); ok && isActive != nil {
query = query.Where("is_active = ?", *isActive)
}
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
if opts == nil {
opts = store.DefaultQueryOptions()
}
offset := (opts.Page - 1) * opts.PageSize
query = query.Offset(offset).Limit(opts.PageSize)
if opts.OrderBy != "" {
query = query.Order(opts.OrderBy)
}
if err := query.Find(&configs).Error; err != nil {
return nil, 0, err
}
return configs, total, nil
}
// Update 更新微信参数配置
func (s *WechatConfigStore) Update(ctx context.Context, config *model.WechatConfig) error {
return s.db.WithContext(ctx).Save(config).Error
}
// SoftDelete 软删除微信参数配置
func (s *WechatConfigStore) SoftDelete(ctx context.Context, id uint) error {
return s.db.WithContext(ctx).Delete(&model.WechatConfig{}, id).Error
}
// GetActive 获取当前激活的配置
// 返回 gorm.ErrRecordNotFound 如果没有激活的配置
func (s *WechatConfigStore) GetActive(ctx context.Context) (*model.WechatConfig, error) {
var config model.WechatConfig
if err := s.db.WithContext(ctx).Where("is_active = ? AND deleted_at IS NULL", true).First(&config).Error; err != nil {
return nil, err
}
return &config, nil
}
// ActivateInTx 在事务内激活指定配置(先停用所有,再激活指定记录)
func (s *WechatConfigStore) ActivateInTx(ctx context.Context, tx *gorm.DB, id uint) error {
// 先停用所有激活的配置
if err := tx.WithContext(ctx).Model(&model.WechatConfig{}).
Where("is_active = ?", true).
Update("is_active", false).Error; err != nil {
return err
}
// 再激活指定配置
return tx.WithContext(ctx).Model(&model.WechatConfig{}).
Where("id = ?", id).
Update("is_active", true).Error
}
// DB 返回底层数据库连接,用于事务操作
func (s *WechatConfigStore) DB() *gorm.DB {
return s.db
}
// Deactivate 停用指定配置
func (s *WechatConfigStore) Deactivate(ctx context.Context, id uint) error {
return s.db.WithContext(ctx).Model(&model.WechatConfig{}).
Where("id = ?", id).
Update("is_active", false).Error
}
// CountPendingOrdersByConfigID 统计指定配置的待支付订单数
func (s *WechatConfigStore) CountPendingOrdersByConfigID(ctx context.Context, configID uint) (int64, error) {
var count int64
err := s.db.WithContext(ctx).
Table("tb_order").
Where("payment_config_id = ? AND payment_status = ? AND deleted_at IS NULL", configID, 1).
Count(&count).Error
return count, err
}
// CountPendingRechargesByConfigID 统计指定配置的待支付充值记录数
func (s *WechatConfigStore) CountPendingRechargesByConfigID(ctx context.Context, configID uint) (int64, error) {
var count int64
err := s.db.WithContext(ctx).
Table("tb_asset_recharge_record").
Where("payment_config_id = ? AND status = ? AND deleted_at IS NULL", configID, 1).
Count(&count).Error
return count, err
}