All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 10m15s
- 新增单行配置表 tb_asset_auto_renewal_config 与尝试记录表 tb_asset_auto_renewal_attempt(迁移 000229/000230) - 每日按上海自然日扫描,窗口内以同一资产钱包可用余额续购当前主套餐,资金/订单/套餐/审计同一事务闭合 - 唯一键保证每资产每日至多一次尝试,占位中断由后续扫描收敛,当日不重试 - 四类失败原因向客户与店铺各投递每日至多一条站内通知,并注册通知类型与个人客户白名单 - 续费成功后按条件经 Outbox 可靠投递复机,新增恢复扫描只查询回填,不使用即发即弃调用 - 配置读写仅超级管理员与平台账号,保存记录操作者、前后值快照并登记统一审计 - tasks 7.1–7.15 全部验证通过(本机隔离 PostgreSQL/Redis,零外部渠道调用)
83 lines
3.2 KiB
Go
83 lines
3.2 KiB
Go
package admin
|
||
|
||
import (
|
||
"github.com/go-playground/validator/v10"
|
||
"github.com/gofiber/fiber/v2"
|
||
|
||
assetAutoRenewalApp "github.com/break/junhong_cmp_fiber/internal/application/assetautorenewal"
|
||
"github.com/break/junhong_cmp_fiber/internal/handler/validation"
|
||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||
)
|
||
|
||
// AssetAutoRenewalConfigHandler 资产钱包自动续费配置 Handler。
|
||
//
|
||
// 路由组已有「仅超级管理员与平台账号」门禁,应用层仍会复核账号类型(ENG-AUTHZ-001),
|
||
// 因此代理、企业与个人客户即使绕过路由也无任何入口。
|
||
type AssetAutoRenewalConfigHandler struct {
|
||
service *assetAutoRenewalApp.Service
|
||
validator *validator.Validate
|
||
}
|
||
|
||
// NewAssetAutoRenewalConfigHandler 创建资产钱包自动续费配置 Handler。
|
||
func NewAssetAutoRenewalConfigHandler(service *assetAutoRenewalApp.Service, validator *validator.Validate) *AssetAutoRenewalConfigHandler {
|
||
return &AssetAutoRenewalConfigHandler{service: service, validator: validator}
|
||
}
|
||
|
||
// GetConfig 读取资产钱包自动续费配置。
|
||
// GET /api/admin/asset-auto-renewal-config
|
||
func (h *AssetAutoRenewalConfigHandler) GetConfig(c *fiber.Ctx) error {
|
||
config, err := h.service.GetConfig(c.UserContext())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return response.Success(c, toAssetAutoRenewalConfigResponse(config))
|
||
}
|
||
|
||
// UpdateConfig 保存资产钱包自动续费配置。
|
||
// PUT /api/admin/asset-auto-renewal-config
|
||
func (h *AssetAutoRenewalConfigHandler) UpdateConfig(c *fiber.Ctx) error {
|
||
var req dto.UpdateAssetAutoRenewalConfigRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam, "请求参数格式不正确")
|
||
}
|
||
if err := h.validator.Struct(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam, validation.Message("保存自动续费配置参数不合法", &req, err))
|
||
}
|
||
config, err := h.service.SaveConfig(c.UserContext(), assetAutoRenewalApp.ConfigRequest{
|
||
Enabled: req.Enabled,
|
||
Scope: req.Scope,
|
||
PackageIDs: req.PackageIDs,
|
||
DaysBeforeExpiry: req.DaysBeforeExpiry,
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return response.Success(c, toAssetAutoRenewalConfigResponse(config))
|
||
}
|
||
|
||
// toAssetAutoRenewalConfigResponse 组装配置响应,枚举附加中文名称字段(ENG-DTO-001)。
|
||
func toAssetAutoRenewalConfigResponse(config *assetAutoRenewalApp.ConfigView) dto.AssetAutoRenewalConfigResponse {
|
||
packageIDs := config.PackageIDs
|
||
if packageIDs == nil {
|
||
packageIDs = []uint{}
|
||
}
|
||
enabledName := "关闭"
|
||
if config.Enabled == constants.AssetAutoRenewalConfigEnabledOn {
|
||
enabledName = "开启"
|
||
}
|
||
return dto.AssetAutoRenewalConfigResponse{
|
||
Enabled: config.Enabled,
|
||
Scope: config.Scope,
|
||
PackageIDs: packageIDs,
|
||
DaysBeforeExpiry: config.DaysBeforeExpiry,
|
||
ConfigVersion: config.ConfigVersion,
|
||
ScopeName: constants.GetAssetAutoRenewalScopeName(config.Scope),
|
||
EnabledName: enabledName,
|
||
Updater: config.Updater,
|
||
UpdatedAt: config.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||
}
|
||
}
|