feat(收口): 补齐 8 月迭代缺口并同步 Spec 与证据链
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled

- 新增六对成对迁移 000232–000237:H5 弹窗类型、退款结算标识与申请人备注、优先轮询事实字段与两个新终态、通道阈值命中留痕、手机号最近解绑人、提现资格校验留痕
- 退款:原因必填与申请人备注、来源支付与渠道流水冻结、线下处理流水号补录审计、按订单查询可选退款方式、企微审批材料补齐且新增字段缺失映射即明确失败
- 优先轮询:人工关闭、有效期到期独立周期任务、失败与过期人工重触发、事实字段与异常重试查询、资产解析端点只读投影
- 通道阈值:命中事实同事务留痕与命中记录查询;员工账单:列表筛选与详情投影;商户池:列表投影与统计周期语义;H5:弹窗类型与类别排序
- 手机号:有效关联数量与最近解绑人、短信验证码失败次数限制;导出:佣金明细十五列与报表序号列
- 时间筛选:三处新增筛选纳入统一严格解析契约,员工账单产生时间参数改名
- 同步 12 份主 Spec 需求、两端点与异步任务证据链,门禁 context-health 与 OpenSpec 校验通过
This commit is contained in:
2026-09-18 15:34:29 +08:00
parent 5e78809b93
commit 5ed6b39deb
142 changed files with 7878 additions and 964 deletions

View File

@@ -68,6 +68,21 @@ const (
ApprovalFieldRefundReason = "refund_reason"
// ApprovalFieldPackageUsageID 表示退款指定套餐使用记录 ID 业务字段。
ApprovalFieldPackageUsageID = "package_usage_id"
// ApprovalFieldRefundAssetType 表示退款资产类型(卡或设备)业务字段。
// 与 ApprovalFieldAssetType下单快照的订单类型语义不同是本变更新增的审批材料字段。
ApprovalFieldRefundAssetType = "refund_asset_type"
// ApprovalFieldRefundDeviceType 表示退款关联设备的设备类型业务字段,无关联设备时为空串。
ApprovalFieldRefundDeviceType = "refund_device_type"
// ApprovalFieldRefundDeviceModel 表示退款关联设备的设备型号业务字段,无关联设备时为空串。
ApprovalFieldRefundDeviceModel = "refund_device_model"
// ApprovalFieldRefundPackageUsedMB 表示退款当前套餐已用量MB业务字段解析不到时为 0。
ApprovalFieldRefundPackageUsedMB = "refund_package_used_mb"
// ApprovalFieldRefundPackageTotalMB 表示退款当前套餐总量MB业务字段解析不到时为 0。
ApprovalFieldRefundPackageTotalMB = "refund_package_total_mb"
// ApprovalFieldRefundChannelTradeNo 表示冻结的原支付渠道交易流水号业务字段,线下订单为空串。
ApprovalFieldRefundChannelTradeNo = "refund_channel_trade_no"
// ApprovalFieldRefundApplicantRemark 表示本次提交的申请人备注业务字段,未填写时为空串。
ApprovalFieldRefundApplicantRemark = "refund_applicant_remark"
// ApprovalFieldCollectionApplicationID 表示员工代收款核销申请 ID 业务字段。
ApprovalFieldCollectionApplicationID = "collection_application_id"
// ApprovalFieldCollectionPaymentMethod 表示员工代收款线下收款方式名称业务字段。
@@ -107,6 +122,11 @@ const (
ApprovalFieldDistributionContactName = "distribution_contact_name"
// ApprovalFieldDistributionRegion 表示注册地址摘要业务字段。
ApprovalFieldDistributionRegion = "distribution_region"
// ApprovalFieldDistributionBusinessOwner 表示上级店铺当前业务员名称快照业务字段,无业务员时为空串。
ApprovalFieldDistributionBusinessOwner = "distribution_business_owner"
// ApprovalFieldDistributionBusinessOwnerAbsence 表示上级店铺无业务员标记业务字段。
// 无业务员时提交「无」,有业务员时为空串,使审批材料显式区分「未填」与「确认无」。
ApprovalFieldDistributionBusinessOwnerAbsence = "distribution_business_owner_absence"
// ApprovalFieldQualificationSubjectType 表示提现资格签约主体类型中文名业务字段。
ApprovalFieldQualificationSubjectType = "qualification_subject_type"
// ApprovalFieldQualificationSubjectCodeMasked 表示脱敏后的签约主体代码业务字段。

View File

@@ -378,6 +378,8 @@ const (
AuditActionRefundChannelCalled = "refund.channel_call"
// AuditActionRefundChannelRecovered 表示恢复任务按渠道退款请求号回填原路退款结果。
AuditActionRefundChannelRecovered = "refund.channel_recover"
// AuditActionRefundOfflineSettled 表示授权账号补录或更正线下退款处理流水号。
AuditActionRefundOfflineSettled = "refund.offline_settle"
// AuditActionApprovalRequested 表示创建通用审批实例并请求渠道提交。
AuditActionApprovalRequested = "approval.request"
// AuditActionApprovalSubmissionSynced 表示同步审批渠道提交结果。
@@ -614,6 +616,12 @@ const (
AuditActionPollingPriorityDequeued = "polling_priority.dequeue"
// AuditActionPollingPriorityManualDenied 表示拒绝一次人工优先入队。
AuditActionPollingPriorityManualDenied = "polling_priority.manual_denied"
// AuditActionPollingPriorityClosed 表示人工关闭未完成的卡轮询优先项。
AuditActionPollingPriorityClosed = "polling_priority.close"
// AuditActionPollingPriorityExpired 表示有效期到期扫描把未完成优先项转已过期终态并出队。
AuditActionPollingPriorityExpired = "polling_priority.expire"
// AuditActionPollingPriorityRetriggered 表示对失败或已过期优先项的人工重触发。
AuditActionPollingPriorityRetriggered = "polling_priority.retrigger"
// AuditActionWeComCredentialsRead 表示读取企业微信应用明文凭据。
AuditActionWeComCredentialsRead = "wecom.application.credentials_read"
// AuditActionRoleCreated 表示创建角色。

View File

@@ -461,6 +461,16 @@ const (
VerificationCodeLength = 6 // 验证码长度6位数字
VerificationCodeExpiration = 5 * time.Minute // 验证码过期时间5分钟
VerificationCodeRateLimit = 60 * time.Second // 验证码发送频率限制60秒
// VerificationCodeMaxFailures 是同一手机号在一个失败计数窗口内允许的校验失败次数上限。
// 达到上限后窗口内后续校验一律拒绝(即使验证码正确),锁定随窗口到期自动解除,无需人工解锁。
VerificationCodeMaxFailures = 5
// VerificationCodeFailureWindow 是校验失败计数的滑动窗口,同时作为达到上限后的锁定上限。
// 每次失败都刷新窗口;窗口内校验成功即清零。计数与锁定的读写在存储故障时一律放行并记录。
VerificationCodeFailureWindow = 10 * time.Minute
// VerificationCodeLockedMessage 是锁定期间的对外提示。
// 只说明失败次数过多,不泄露验证码正确性、剩余可失败次数与内部键名。
VerificationCodeLockedMessage = "验证码校验失败次数过多,请稍后再试"
)
// ======== 默认超级管理员账号配置(用于系统初始化) ========

View File

@@ -30,6 +30,16 @@ const (
// H5PopupCandidateTypeOperation 表示候选为运营弹窗。
H5PopupCandidateTypeOperation = "operation"
// H5PopupTypePromotion 表示运营弹窗类型为套餐政策推广。
H5PopupTypePromotion = "promotion"
// H5PopupTypeAnnouncement 表示运营弹窗类型为通用公告。
H5PopupTypeAnnouncement = "announcement"
// H5PopupTypeDefaultPriorityPromotion 是套餐政策推广未显式指定优先级时的缺省优先级(对应 111.md §13.3 的「中」)。
H5PopupTypeDefaultPriorityPromotion = 100
// H5PopupTypeDefaultPriorityAnnouncement 是通用公告未显式指定优先级时的缺省优先级(对应 111.md §13.3 的「低」)。
H5PopupTypeDefaultPriorityAnnouncement = 0
// H5PopupRiskExchangeReason 是客户自助风险换卡单的固定换货原因,保证换货原因非空且可追溯来源。
H5PopupRiskExchangeReason = "运营商风险停机,客户自助申请寄送新卡"
@@ -79,6 +89,39 @@ func IsH5PopupActionType(actionType string) bool {
}
}
// IsH5PopupType 判断运营弹窗类型是否合法;类型为必填单选,空值不合法。
func IsH5PopupType(popupType string) bool {
switch popupType {
case H5PopupTypePromotion, H5PopupTypeAnnouncement:
return true
default:
return false
}
}
// GetH5PopupTypeName 获取运营弹窗类型的中文名称。
func GetH5PopupTypeName(popupType string) string {
switch popupType {
case H5PopupTypePromotion:
return "套餐政策推广"
case H5PopupTypeAnnouncement:
return "通用公告"
default:
return ""
}
}
// H5PopupTypeDefaultPriority 返回该类型未显式指定优先级时的缺省优先级。
// 类别本身已由候选排序的类别表达式保证「推广高于公告」,缺省值只决定同类别内的默认次序。
func H5PopupTypeDefaultPriority(popupType string) int {
switch popupType {
case H5PopupTypePromotion:
return H5PopupTypeDefaultPriorityPromotion
default:
return H5PopupTypeDefaultPriorityAnnouncement
}
}
// IsCarrierType 判断卡类型是否为受控运营商类型;用于范围配置的取值校验。
func IsCarrierType(carrierType string) bool {
switch carrierType {

View File

@@ -1,5 +1,7 @@
package constants
import "time"
// PollingShardCount 轮询队列默认分片数
// 千万级规模下16 分片可将单队列深度控制在 ~60 万以内
const PollingShardCount = 16
@@ -45,6 +47,17 @@ const (
// 本值不是可维护配置项,也不改变普通轮询的无限重试策略。
const PollingPriorityMaxAttempts = 3
// PollingPriorityValidity 优先轮询项固定的有效期长度。
// 自入队时间(事实行 priority_effective_from起算超期仍未完成的项转已过期终态并出队
// 保留触发类型、尝试次数与最近失败原因。与尝试上限同为代码常量,不新增可维护配置项。
// 取 72 小时:覆盖最长轮询间隔叠加三次可恢复重试的时间,避免正常加急被过早判为超期。
const PollingPriorityValidity = 72 * time.Hour
// TaskTypePollingPriorityExpiry 优先轮询项有效期到期处理任务类型(由 Asynq Scheduler 调度)。
// 该任务只扫描并终结超期未完成项MUST NOT 依赖查询或读侧隐式触发;
// 未在 QueueForTaskType 中单独声明,按该函数默认分支落在 default 队列。
const TaskTypePollingPriorityExpiry = "polling_priority:expiry"
// PollingPriorityClaimLease 优先轮询项的认领租约(秒)。
// 必须长于既有轮询任务超时 60 秒,为执行收尾与重新入队留出余量;
// 执行中且超过租约仍未被更新的优先项允许相邻执行接管领取。
@@ -60,6 +73,32 @@ const (
PollingPriorityStatusCompleted = "completed"
// PollingPriorityStatusFailed 优先轮询项状态-失败出队(终态)
PollingPriorityStatusFailed = "failed"
// PollingPriorityStatusClosed 优先轮询项状态-已关闭(人工关闭终态,已出队)
PollingPriorityStatusClosed = "closed"
// PollingPriorityStatusExpired 优先轮询项状态-已过期(有效期到期终态,已出队)
PollingPriorityStatusExpired = "expired"
)
// PollingPriorityActiveStatuses 返回占用活动项唯一键的状态集合。
// 与事实表部分唯一索引谓词 status IN ('pending','processing') 严格一致:
// 已关闭与已过期是终态,不占键位,同一卡同一任务类型可再次入队或人工重触发。
func PollingPriorityActiveStatuses() []string {
return []string{PollingPriorityStatusPending, PollingPriorityStatusProcessing}
}
// 轮询优先项执行结局常量(资产详情投影口径)。
// 与项状态区分:项状态描述队列位置(含待执行、失败出队),结局描述「最近一次轮询的对外结论」。
const (
// PollingPriorityOutcomeProcessing 结局-处理中(待执行或执行中)
PollingPriorityOutcomeProcessing = "processing"
// PollingPriorityOutcomeSuccess 结局-执行成功
PollingPriorityOutcomeSuccess = "success"
// PollingPriorityOutcomeFailed 结局-轮询失败
PollingPriorityOutcomeFailed = "failed"
// PollingPriorityOutcomeClosed 结局-已关闭
PollingPriorityOutcomeClosed = "closed"
// PollingPriorityOutcomeExpired 结局-已过期
PollingPriorityOutcomeExpired = "expired"
)
// 轮询优先项触发类型常量
@@ -129,11 +168,61 @@ func PollingPriorityStatusName(status string) string {
return "已完成"
case PollingPriorityStatusFailed:
return "失败出队"
case PollingPriorityStatusClosed:
return "已关闭"
case PollingPriorityStatusExpired:
return "已过期"
default:
return "未知"
}
}
// PollingPriorityStatusOutcome 把优先项状态投影为对外执行结局。
// 待执行与执行中都属于「处理中」:二者都没有产生新的对外结论。
func PollingPriorityStatusOutcome(status string) string {
switch status {
case PollingPriorityStatusCompleted:
return PollingPriorityOutcomeSuccess
case PollingPriorityStatusFailed:
return PollingPriorityOutcomeFailed
case PollingPriorityStatusClosed:
return PollingPriorityOutcomeClosed
case PollingPriorityStatusExpired:
return PollingPriorityOutcomeExpired
default:
return PollingPriorityOutcomeProcessing
}
}
// PollingPriorityOutcomeName 返回优先项执行结局的中文名称。
func PollingPriorityOutcomeName(outcome string) string {
switch outcome {
case PollingPriorityOutcomeSuccess:
return "执行成功"
case PollingPriorityOutcomeFailed:
return "轮询失败"
case PollingPriorityOutcomeClosed:
return "已关闭"
case PollingPriorityOutcomeExpired:
return "已过期"
case PollingPriorityOutcomeProcessing:
return "处理中"
default:
return "未知"
}
}
// PollingPriorityActive 判断状态是否为占用活动项唯一键的状态。
func PollingPriorityActive(status string) bool {
return status == PollingPriorityStatusPending || status == PollingPriorityStatusProcessing
}
// PollingPriorityRetriggerable 判断状态是否允许人工重触发。
// 只有失败出队与已过期的项才需要人工重新加急;已完成与已关闭是人工/系统已经确认的结论。
func PollingPriorityRetriggerable(status string) bool {
return status == PollingPriorityStatusFailed || status == PollingPriorityStatusExpired
}
// PollingPriorityTriggerName 返回优先轮询项触发类型的中文名称。
func PollingPriorityTriggerName(triggerType string) string {
switch triggerType {

View File

@@ -138,6 +138,13 @@ func RedisVerificationCodeLimitKey(phone string) string {
return fmt.Sprintf("verification:limit:%s", phone)
}
// RedisVerificationCodeFailKey 生成验证码校验失败计数的 Redis 键
// 用途:以手机号维度累计校验失败次数,达到上限后短时锁定
// 过期时间VerificationCodeFailureWindow每次失败在同一 Lua 脚本内原子刷新,到期即计数清零并自动解锁)
func RedisVerificationCodeFailKey(phone string) string {
return fmt.Sprintf("verification:fail:%s", phone)
}
// ========================================
// 钱包相关 Redis Key
// ========================================

View File

@@ -18,6 +18,43 @@ const (
WithdrawalQualificationStatusInvalidated = 3
)
// WithdrawalQualificationCheckPassed 表示本次提现资料资格校验通过。
// 取值为 0/1与 tb_commission_withdrawal_request_attempt.qualification_passed 的 CHECK 一致。
const (
WithdrawalQualificationCheckPassed = 1
WithdrawalQualificationCheckNotPassed = 0
)
// 提现资料资格校验未通过的稳定原因编码。
// 编码是机器判定与审计的稳定口径,中文文案由 GetWithdrawalQualificationFailureReasonName 映射,
// 与 tb_commission_withdrawal_request_attempt.qualification_failure_reason 的 CHECK 取值域一致。
const (
// WithdrawalQualificationFailureMissing 表示申请店铺不存在任何资料资格版本。
WithdrawalQualificationFailureMissing = "qualification_missing"
// WithdrawalQualificationFailureInvalidated 表示资料资格已失效或已被作废。
WithdrawalQualificationFailureInvalidated = "qualification_invalidated"
// WithdrawalQualificationFailureShopDisabled 表示店铺已停用,资料资格随之不可用。
WithdrawalQualificationFailureShopDisabled = "shop_disabled"
// WithdrawalQualificationFailureNotApproved 表示资料资格尚未通过审批(待审批或已驳回)。
WithdrawalQualificationFailureNotApproved = "qualification_not_approved"
)
// GetWithdrawalQualificationFailureReasonName 返回提现资料资格校验未通过原因的中文名称。
func GetWithdrawalQualificationFailureReasonName(reason string) string {
switch reason {
case WithdrawalQualificationFailureMissing:
return "资格不存在"
case WithdrawalQualificationFailureInvalidated:
return "已失效"
case WithdrawalQualificationFailureShopDisabled:
return "已停用"
case WithdrawalQualificationFailureNotApproved:
return "资料未通过审批"
default:
return "未知"
}
}
// GetWithdrawalQualificationStatusName 返回提现资料资格状态的中文名称。
func GetWithdrawalQualificationStatusName(status int) string {
switch status {