Files
junhong_cmp_fiber/internal/service/package/terms.go
邹进 909f4d0ec2
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 8m39s
feat: 实名即生效套餐沉默期、延期办理与资产出库时间
- 迁移 000259~000265:套餐记录沉默期字段与扫描索引、套餐默认沉默期、延期政策/流水/订单/批次/明细 5 张表、默认政策与系统配置(总开关默认关闭)、企微审批白名单、卡与设备出库时间及出库记录
- 沉默期设置(订单发放、自动购包、钱包续费)、后台购买时调整(超默认值按政策免审或企微审批)、统一判定与激活闸门、排队与孤儿扫描跳过过期套餐、过期套餐禁止退款、每 10 分钟扫描
- 总开关专用接口与打开前 4 项检查;延期政策;延期资格与计费;H5 自助与后台二维码在线支付(商户池路由)、支付回调与查单恢复、线下登记、特殊/批量延期、异常重试与原路退款;沉默期审批场景与终态处理
- 换货沉默期流水与出库时间;卡/设备列表、详情、导出与出库记录接口;延期订单与即将过期套餐列表及导出;财务流水纳入延期支付
- 沉默期与出库时间回填工具 cmd/silence-backfill;前端接口变更说明

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 16:32:58 +09:00

99 lines
4.5 KiB
Go
Raw 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 packagepkg
import (
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
)
// ValidateExpiryBaseOverride 校验显式提交的分配生效条件覆盖。
func ValidateExpiryBaseOverride(value *string, submitted bool) (*string, error) {
if !submitted {
return nil, errors.New(errors.CodeInvalidParam, "生效条件覆盖字段未提交,须显式传值(null 或合法枚举)")
}
if value == nil {
return nil, nil
}
if *value != constants.PackageExpiryBaseFromActivation && *value != constants.PackageExpiryBaseFromPurchase {
return nil, errors.New(errors.CodeInvalidParam, "生效条件覆盖值非法,合法值为:购买即生效、实名激活时生效")
}
return value, nil
}
// EffectiveExpiryBase 返回套餐分配最终采用的生效条件。
func EffectiveExpiryBase(pkg *model.Package, allocation *model.ShopPackageAllocation) string {
if allocation != nil && allocation.ExpiryBaseOverride != nil {
return *allocation.ExpiryBaseOverride
}
return pkg.ExpiryBase
}
// ExpiryBaseName 返回生效条件中文名称。
func ExpiryBaseName(value string) string {
switch value {
case constants.PackageExpiryBaseFromActivation:
return "实名激活时生效"
case constants.PackageExpiryBaseFromPurchase:
return "购买即生效"
default:
return "未知"
}
}
// ExpiryBaseOverrideName 返回覆盖值中文名称,空值表示跟随套餐默认。
func ExpiryBaseOverrideName(value *string) string {
if value == nil {
return "跟随套餐默认"
}
return ExpiryBaseName(*value)
}
// ValidateAllocationExpiryBaseOverride 校验分配覆盖不得改变套餐生效条件(add-package-silence-period §3.3)。
// 套餐服务与店铺分配(单个、批量、系列授权)写入路径共用:
// 覆盖把实名即生效套餐改成购买即生效会绕过沉默期;把购买即生效套餐改成实名即生效时该套餐没有默认沉默期,两者都阻断。
// 覆盖为空(跟随套餐默认)或与套餐一致时放行;加油包不进入沉默期,不受此约束。
func ValidateAllocationExpiryBaseOverride(pkg *model.Package, override *string) error {
if pkg == nil || override == nil || pkg.PackageType == constants.PackageTypeAddon {
return nil
}
if *override != pkg.ExpiryBase {
return errors.New(errors.CodePackageExpiryBaseChangeForbidden,
"套餐「"+pkg.PackageName+"」不允许通过分配覆盖改变生效条件(实名即生效与购买即生效不能互相覆盖)")
}
return nil
}
// ValidatePackageExpiryBaseChange 校验套餐自身生效条件变更(§3.3):实名即生效改为购买即生效阻断;
// 购买即生效改为实名即生效时,同一请求必须提供默认沉默期。
func ValidatePackageExpiryBaseChange(pkg *model.Package, before, after string, silenceDaysSubmitted bool) error {
if pkg == nil || pkg.PackageType == constants.PackageTypeAddon || before == after {
return nil
}
if before == constants.PackageExpiryBaseFromActivation && after == constants.PackageExpiryBaseFromPurchase {
return errors.New(errors.CodePackageExpiryBaseChangeForbidden, "实名即生效套餐不允许改为购买即生效")
}
if before == constants.PackageExpiryBaseFromPurchase && after == constants.PackageExpiryBaseFromActivation && !silenceDaysSubmitted {
return errors.New(errors.CodeSilencePeriodOutOfRange, "改为实名即生效时必须同时设置默认沉默期(1~90 天)")
}
return nil
}
// RequiresSilencePeriod 判断套餐是否必须配置默认沉默期:实名即生效的正式套餐。
func RequiresSilencePeriod(pkg *model.Package) bool {
return pkg != nil && pkg.PackageType != constants.PackageTypeAddon && pkg.ExpiryBase == constants.PackageExpiryBaseFromActivation
}
// ValidatePackageSilenceConfig 校验套餐最终的沉默期配置:实名即生效正式套餐必填 1~90 天;其他套餐不得填写。
func ValidatePackageSilenceConfig(pkg *model.Package) error {
if RequiresSilencePeriod(pkg) {
if pkg.SilencePeriodDays == nil || *pkg.SilencePeriodDays < constants.SilencePackageDaysMin || *pkg.SilencePeriodDays > constants.SilencePackageDaysMax {
return errors.New(errors.CodeSilencePeriodOutOfRange, "实名即生效套餐必须配置默认沉默期(1~90 天)")
}
return nil
}
if pkg != nil && pkg.SilencePeriodDays != nil {
return errors.New(errors.CodeInvalidParam, "只有实名即生效的正式套餐可以配置沉默期")
}
return nil
}