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

@@ -220,6 +220,12 @@ func NewRegistry() *Registry {
shopBusinessOwnerImportTaskCompleted := taskAction(constants.AuditActionShopBusinessOwnerImportTaskCompleted, "完成店铺负责人导入任务", constants.AuditResourceShopBusinessOwnerImportTask, constants.AuditActorSystemTask, constants.AuditSourceWorker)
exportTaskCreated := taskAction(constants.AuditActionExportTaskCreated, "创建业务导出任务", constants.AuditResourceExportTask, constants.AuditActorAccount, constants.AuditSourceAdminAPI)
exportTaskCancelled := taskAction(constants.AuditActionExportTaskCancelled, "取消业务导出任务", constants.AuditResourceExportTask, constants.AuditActorAccount, constants.AuditSourceAdminAPI)
phoneAssetAssociationCreated := phoneAssetAssociationAction(constants.AuditActionPhoneAssetAssociationCreated, "验证手机号后建立资产关联", constants.AuditActorPersonalCustomer, constants.AuditSourcePersonalAPI)
phoneAssetAssociationMigrated := phoneAssetAssociationAction(constants.AuditActionPhoneAssetAssociationMigrated, "换绑手机号并迁移资产关联", constants.AuditActorPersonalCustomer, constants.AuditSourcePersonalAPI)
phoneAssetAssociationUnbound := phoneAssetAssociationAction(constants.AuditActionPhoneAssetAssociationUnbound, "解除手机号资产关联", constants.AuditActorAccount, constants.AuditSourceAdminAPI)
phoneAssetAssociationImported := phoneAssetAssociationAction(constants.AuditActionPhoneAssetAssociationImported, "导入解除手机号资产关联", constants.AuditActorSystemTask, constants.AuditSourceWorker)
phoneAssetUnbindImportTaskCreated := taskAction(constants.AuditActionPhoneAssetUnbindImportTaskCreated, "创建手机号资产解绑导入任务", constants.AuditResourcePhoneAssetUnbindImportTask, constants.AuditActorAccount, constants.AuditSourceAdminAPI)
phoneAssetUnbindImportTaskCompleted := taskAction(constants.AuditActionPhoneAssetUnbindImportTaskCompleted, "完成手机号资产解绑导入任务", constants.AuditResourcePhoneAssetUnbindImportTask, constants.AuditActorSystemTask, constants.AuditSourceWorker)
notificationDelivered := notificationAction(constants.AuditActionNotificationDelivered, "生成站内通知", constants.AuditResourceNotification, constants.AuditActorSystemTask, constants.AuditSourceWorker)
notificationRead := notificationAction(constants.AuditActionNotificationRead, "标记通知已读", constants.AuditResourceNotification, constants.AuditActorAccount, constants.AuditSourceAdminAPI)
notificationRead.AllowedOrigins = []ActionOrigin{{Actor: constants.AuditActorPersonalCustomer, Source: constants.AuditSourcePersonalAPI}}
@@ -576,6 +582,12 @@ func NewRegistry() *Registry {
constants.AuditActionShopBusinessOwnerImportTaskCompleted: shopBusinessOwnerImportTaskCompleted,
constants.AuditActionExportTaskCreated: exportTaskCreated,
constants.AuditActionExportTaskCancelled: exportTaskCancelled,
constants.AuditActionPhoneAssetAssociationCreated: phoneAssetAssociationCreated,
constants.AuditActionPhoneAssetAssociationMigrated: phoneAssetAssociationMigrated,
constants.AuditActionPhoneAssetAssociationUnbound: phoneAssetAssociationUnbound,
constants.AuditActionPhoneAssetAssociationImported: phoneAssetAssociationImported,
constants.AuditActionPhoneAssetUnbindImportTaskCreated: phoneAssetUnbindImportTaskCreated,
constants.AuditActionPhoneAssetUnbindImportTaskCompleted: phoneAssetUnbindImportTaskCompleted,
constants.AuditActionNotificationDelivered: notificationDelivered,
constants.AuditActionNotificationRead: notificationRead,
constants.AuditActionNotificationReadAll: notificationReadAll,
@@ -796,6 +808,14 @@ func NewRegistry() *Registry {
Type: constants.AuditResourceExportTask, Name: "业务导出任务",
IdentityFields: []string{"id", "task_no", "scene", "format", "creator_user_id", "creator_user_type", "creator_shop_id", "creator_enterprise_id", "scope_shop_ids"},
},
constants.AuditResourcePhoneAssetAssociation: {
Type: constants.AuditResourcePhoneAssetAssociation, Name: "手机号—资产关联",
IdentityFields: []string{"id", "phone_masked", "asset_type", "asset_id", "status", "source", "invalidated_at", "invalidation_method", "invalidation_reason"},
},
constants.AuditResourcePhoneAssetUnbindImportTask: {
Type: constants.AuditResourcePhoneAssetUnbindImportTask, Name: "手机号资产解绑导入任务",
IdentityFields: []string{"id", "task_no", "file_name"},
},
constants.AuditResourceNotification: {
Type: constants.AuditResourceNotification, Name: "站内通知",
IdentityFields: []string{"id", "event_id", "recipient_kind", "recipient_id", "category", "type", "severity", "ref_type", "ref_id", "ref_key"},
@@ -1326,6 +1346,7 @@ func deviceBatchAction(code, name, primaryResource string) ActionDefinition {
}
}
// taskAction 定义 Worker 或后台账号触发的异步任务动作。
func taskAction(code, name, primaryResource, actor, source string) ActionDefinition {
return ActionDefinition{
Code: code, Name: name, Category: constants.AuditCategoryBusiness, Risk: constants.AuditRiskNormal,
@@ -1335,6 +1356,17 @@ func taskAction(code, name, primaryResource, actor, source string) ActionDefinit
}
}
// phoneAssetAssociationAction 定义手机号—资产关联的建立、迁移与解除动作。
// 关联由 H5 短信验证建立、由后台或导入任务解除,事件只记录脱敏手机号,不写主体投影。
func phoneAssetAssociationAction(code, name, actor, source string) ActionDefinition {
return ActionDefinition{
Code: code, Name: name, Category: constants.AuditCategoryBusiness, Risk: constants.AuditRiskNormal,
PrimaryResource: constants.AuditResourcePhoneAssetAssociation, AllowedActor: actor, Source: source,
RequireTransaction: true, DefaultVisibility: constants.AuditSubjectInternalOnly,
AllowedVisibility: []string{constants.AuditSubjectInternalOnly},
}
}
// businessUserGroupAction 定义业务用户组维护动作;组只承载业务分类,主体不可见事件细节。
func businessUserGroupAction(code, name, risk, primaryResource string) ActionDefinition {
return ActionDefinition{