feat(手机号资产关联): AUG26-009 手机号—资产关联、十项上限与后台解绑
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m2s

- 新增成对迁移 000223(tb_phone_asset_association,含有效关系部分唯一索引与 down 守卫)与 000224(解绑导入任务表),不回填历史
- H5:need_bind_phone 三支判定(开关关闭完全短路);已有主号幂等建联;十项上限按手机号 advisory 串行化(含换绑到全新号的并发场景);换绑原子迁移与冲突整单回滚;不写遗留列
- 后台:关联列表、单项/批量解绑、CSV 导入解绑(B1–B16),超管/平台 gate + 资产数据范围复核,三态统一文案
- 读侧:卡/设备列表与详情按页一次 IN 聚合;两类导出补「关联手机号」列并保留历史表头反解兼容
- 脱敏:关联审计走独立动作/资源只写脱敏手机号;访问日志手机号类字段脱敏
- 同步主 Spec openspec/specs/phone-asset-association 并归档 AUG26-009,补齐 requirement-evidence 与入口矩阵,context-health 通过
This commit is contained in:
2026-09-15 11:54:56 +08:00
parent 93e072e1e2
commit 70e680eb0a
67 changed files with 3951 additions and 147 deletions

View File

@@ -0,0 +1,142 @@
package constants
import "time"
// 手机号—资产关联的状态取值。
const (
// PhoneAssetAssociationStatusInvalid 表示关联已失效,不再占用十项额度。
PhoneAssetAssociationStatusInvalid = 0
// PhoneAssetAssociationStatusValid 表示关联当前有效。
PhoneAssetAssociationStatusValid = 1
)
// 手机号—资产关联的建立来源;关联只能由 H5 短信验证建立,后台无创建或补录入口。
const (
// PhoneAssetAssociationSourceH5SMSVerification 表示关联由 H5 短信验证码校验通过后建立。
PhoneAssetAssociationSourceH5SMSVerification = "h5_sms_verification"
)
// 手机号—资产关联的失效方式,用于审计与事后核对解除入口。
const (
// PhoneAssetAssociationInvalidateMethodBackendSingle 表示后台单项解除。
PhoneAssetAssociationInvalidateMethodBackendSingle = "backend_single"
// PhoneAssetAssociationInvalidateMethodBackendBatch 表示后台勾选批量解除。
PhoneAssetAssociationInvalidateMethodBackendBatch = "backend_batch"
// PhoneAssetAssociationInvalidateMethodCSVImport 表示 CSV 导入解除。
PhoneAssetAssociationInvalidateMethodCSVImport = "csv_import"
)
// 手机号—资产关联的业务口径常量。
const (
// PhoneAssetAssociationMaxValidPerPhone 表示单手机号可同时有效的关联资产项数上限。
PhoneAssetAssociationMaxValidPerPhone = 10
// PhoneAssetAssociationLimitMessage 表示超过十项上限时的统一失败文案。
PhoneAssetAssociationLimitMessage = "该手机号最多关联10项有效资产"
)
// 手机号—资产关联解绑入口的角色说明。
const (
// PhoneAssetAssociationAccessDescription 表示关联查看与解除入口的角色说明。
PhoneAssetAssociationAccessDescription = "仅超级管理员和平台账号可访问,代理与企业账号返回 403。"
)
// 手机号—资产关联解除的入参口径。
const (
// PhoneAssetAssociationUnbindReasonMaxLength 表示解除原因的最大长度。
PhoneAssetAssociationUnbindReasonMaxLength = 500
)
// 手机号—资产关联 CSV 解绑导入的场景常量。
const (
// StoragePurposePhoneAssetUnbindImport 表示手机号资产解绑 CSV 导入上传用途。
StoragePurposePhoneAssetUnbindImport = "phone_unbind_import"
// PhoneAssetUnbindImportStoragePrefix 表示解绑导入文件的对象存储目录。
PhoneAssetUnbindImportStoragePrefix = "phone-unbind-imports"
// PhoneAssetUnbindImportTaskTimeout 表示单个解绑导入任务的最长执行时间。
PhoneAssetUnbindImportTaskTimeout = 2 * time.Hour
// PhoneAssetUnbindImportProgressBatchSize 表示导入按批更新进度计数的批大小。
PhoneAssetUnbindImportProgressBatchSize = 100
// PhoneAssetUnbindImportItemStatusSuccess 表示导入单行已成功解除关联。
PhoneAssetUnbindImportItemStatusSuccess = 3
// PhoneAssetUnbindImportItemStatusFailed 表示导入单行校验或执行失败并保留原状。
PhoneAssetUnbindImportItemStatusFailed = 4
)
// PhoneAssetUnbindImportColumns 表示导入文件首行必须完全一致的固定列序;解绑原因由任务级必填字段提供。
var PhoneAssetUnbindImportColumns = []string{"资产标识", "备注"}
// 手机号—资产关联解绑导入的任务级失败原因,与行级失败原因分开记录。
const (
// PhoneAssetUnbindImportErrorFileFormat 表示文件格式或表头与固定列序不符。
PhoneAssetUnbindImportErrorFileFormat = "文件格式或表头与固定列序不符"
// PhoneAssetUnbindImportErrorEncoding 表示文件既非合法 UTF-8 也无法按 GBK 解码。
PhoneAssetUnbindImportErrorEncoding = "文件编码无法按 UTF-8 或 GBK 解码"
// PhoneAssetUnbindImportErrorNoDataRow 表示文件没有数据行。
PhoneAssetUnbindImportErrorNoDataRow = "文件没有数据行"
)
// 手机号—资产关联解绑导入的行级失败原因,取值集合固定。
const (
// PhoneAssetUnbindImportRowErrorIdentifier 表示资产标识为空。
PhoneAssetUnbindImportRowErrorIdentifier = "资产标识不能为空"
// PhoneAssetUnbindImportRowErrorAssetMissing 表示资产标识无法定位到资产。
PhoneAssetUnbindImportRowErrorAssetMissing = "资产标识不存在或已删除"
// PhoneAssetUnbindImportRowErrorNoAssociation 表示该资产已无有效关联。
PhoneAssetUnbindImportRowErrorNoAssociation = "该资产已无有效关联手机号"
// PhoneAssetUnbindImportRowErrorFormat 表示该行列数与固定列序不一致。
PhoneAssetUnbindImportRowErrorFormat = "行格式错误"
// PhoneAssetUnbindImportRowErrorRemarkTooLong 表示备注超过长度上限。
PhoneAssetUnbindImportRowErrorRemarkTooLong = "备注超过500字符"
)
// 手机号—资产关联解绑导入的备注长度上限。
const (
// PhoneAssetUnbindImportRemarkMaxLength 表示行内备注的最大长度。
PhoneAssetUnbindImportRemarkMaxLength = 500
)
// 手机号—资产关联的三态统一失败文案。
// 越权、资产不存在与已无有效关系三种情况必须返回同一文案避免形成可枚举差异ENG-AUTHZ-001
// 本常量只供新增用例使用;既有内联字面量按 As-Is 保留,不在本 Change 统一。
const (
// PhoneAssetAssociationDeniedMessage 表示越权、资产不存在或已无有效关系时的统一失败文案。
PhoneAssetAssociationDeniedMessage = "无权限操作该资源或资源不存在"
)
// GetPhoneAssetAssociationImportItemStatusName 返回解绑导入行状态中文名称。
func GetPhoneAssetAssociationImportItemStatusName(status int) string {
switch status {
case PhoneAssetUnbindImportItemStatusSuccess:
return "成功"
case PhoneAssetUnbindImportItemStatusFailed:
return "失败"
default:
return "未知"
}
}
// GetPhoneAssetAssociationStatusName 返回关联状态中文名称。
func GetPhoneAssetAssociationStatusName(status int) string {
switch status {
case PhoneAssetAssociationStatusValid:
return "有效"
case PhoneAssetAssociationStatusInvalid:
return "已失效"
default:
return "未知"
}
}
// PhoneAssetAssociationInvalidateMethodName 返回失效方式中文名称。
func PhoneAssetAssociationInvalidateMethodName(method string) string {
switch method {
case PhoneAssetAssociationInvalidateMethodBackendSingle:
return "后台单项解除"
case PhoneAssetAssociationInvalidateMethodBackendBatch:
return "后台批量解除"
case PhoneAssetAssociationInvalidateMethodCSVImport:
return "CSV导入解除"
default:
return "未知"
}
}