Files
junhong_cmp_fiber/internal/routes/phone_asset_association.go
break 70e680eb0a
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m2s
feat(手机号资产关联): AUG26-009 手机号—资产关联、十项上限与后台解绑
- 新增成对迁移 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 通过
2026-09-15 11:54:56 +08:00

108 lines
5.9 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 routes
import (
"github.com/gofiber/fiber/v2"
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
"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/middleware"
"github.com/break/junhong_cmp_fiber/pkg/openapi"
)
// phoneAssetUnbindImportDoc 说明手机号资产解绑 CSV 的模板要求。
// 模板由前端提供,后端只描述固定列序、取值与编码要求,不提供模板下载端点与模板资源。
const phoneAssetUnbindImportDoc = `仅超级管理员和平台账号可操作,代理与企业账号返回 403。
### 完整导入流程
1. **获取上传 URL**:调用 ` + "`POST /api/admin/storage/upload-url`" + `purpose 传 ` + "`phone_unbind_import`" + `
2. **上传 CSV**:使用返回的预签名 URL 上传文件到对象存储
3. **调用本接口**:使用返回的 ` + "`file_key`" + ` 创建导入任务(必须以 ` + "`phone-unbind-imports/`" + ` 开头且扩展名为 ` + "`.csv`" + `
### 模板列序(首行表头必须完全一致)
` + "`资产标识`" + `` + "`备注`" + `
- 资产标识:以 ICCID、虚拟号、IMEI、SN 或接入号唯一定位资产;不存在或已删除时该行失败
- 备注:可选,填写时写入该行审计
### 编码要求
文件编码为 UTF-8可带 BOM非 UTF-8 时按 GBK 尝试解码,仍失败时按任务级失败并给出明确原因。
### 执行语义
- 逐行独立事务:成功行提交,失败行保留原状,不设行数硬上限
- 每行解除该资产全部当前有效关联,解绑原因取任务级必填字段
- 行号自数据首行起计(表头不计入);任务级失败不产生行明细
- 逐行结果保存解绑当时的完整手机号快照,供任务详情与结果展示`
// registerPhoneAssetAssociationRoutes 注册手机号—资产关联查看与解除路由。
// 沿用超管/平台路由组级 gate 先例:代理、企业与个人客户账号一律 403。
func registerPhoneAssetAssociationRoutes(router fiber.Router, handler *admin.PhoneAssetAssociationHandler, doc *openapi.Generator, basePath string) {
group := router.Group("/phone-asset-associations", func(c *fiber.Ctx) error {
userType := middleware.GetUserTypeFromContext(c.UserContext())
if userType != constants.UserTypeSuperAdmin && userType != constants.UserTypePlatform {
return errors.New(errors.CodeForbidden, constants.PlatformManagementForbiddenMessage)
}
return c.Next()
})
groupPath := basePath + "/phone-asset-associations"
Register(group, doc, groupPath, "GET", "", handler.List, RouteSpec{
Summary: "查询手机号资产关联列表",
Description: constants.PhoneAssetAssociationAccessDescription + "按资产数据范围过滤后分页返回资产(类型/ID/标识)、完整手机号、建立时间、建立来源与状态;支持资产标识、手机号、关联状态与创建时间区间筛选。越权与不存在不形成可枚举差异。",
Tags: []string{"手机号资产关联"},
Input: new(dto.ListPhoneAssetAssociationRequest),
Output: new(dto.PhoneAssetAssociationPageResult),
Auth: true,
})
Register(group, doc, groupPath, "POST", "/batch-unbind", handler.BatchUnbind, RouteSpec{
Summary: "按资产批量解除手机号关联",
Description: constants.PhoneAssetAssociationAccessDescription + "资产集合按 (资产类型, 资产ID) 去重后逐项独立执行:每项解除该资产全部当前有效关联,返回成功数、失败数与逐项结果,部分成功不回滚成功项。越权、资产不存在与已无有效关系返回同一失败文案。" + constants.PhoneAssetAssociationDeniedMessage + "。",
Tags: []string{"手机号资产关联"},
Input: new(dto.BatchUnbindPhoneAssetAssociationRequest),
Output: new(dto.BatchUnbindPhoneAssetAssociationResponse),
Auth: true,
})
Register(group, doc, groupPath, "POST", "/unbind-imports", handler.CreateImportTask, RouteSpec{
Summary: "创建手机号资产解绑导入任务",
Description: phoneAssetUnbindImportDoc,
Tags: []string{"手机号资产关联"},
Input: new(dto.CreatePhoneAssetUnbindImportRequest),
Output: new(dto.PhoneAssetUnbindImportTaskResponse),
Auth: true,
})
Register(group, doc, groupPath, "GET", "/unbind-imports", handler.ListImportTasks, RouteSpec{
Summary: "查询手机号资产解绑导入任务列表",
Description: constants.PhoneAssetAssociationAccessDescription,
Tags: []string{"手机号资产关联"},
Input: new(dto.ListPhoneAssetUnbindImportRequest),
Output: new(dto.PhoneAssetUnbindImportTaskPageResult),
Auth: true,
})
Register(group, doc, groupPath, "GET", "/unbind-imports/:id", handler.ImportTaskDetail, RouteSpec{
Summary: "查询手机号资产解绑导入任务详情",
Description: constants.PhoneAssetAssociationAccessDescription + "返回成功数、失败数、逐行失败原因与解绑当时的完整手机号快照,并单独返回任务级错误原因。",
Tags: []string{"手机号资产关联"},
Input: new(dto.GetPhoneAssetUnbindImportRequest),
Output: new(dto.PhoneAssetUnbindImportTaskDetailResponse),
Auth: true,
})
Register(group, doc, groupPath, "DELETE", "/:id", handler.Unbind, RouteSpec{
Summary: "解除单条手机号资产关联",
Description: constants.PhoneAssetAssociationAccessDescription + "路径主键即指定一条资产—手机号关系必须指定必须二次确认并填写原因1500 字符),缺一即拒绝且不解除任何关系。越权、资产不存在与已无有效关系返回同一失败文案。" + constants.PhoneAssetAssociationDeniedMessage + "。",
Tags: []string{"手机号资产关联"},
Input: new(dto.UnbindPhoneAssetAssociationRequest),
Output: new(dto.UnbindPhoneAssetAssociationResponse),
Auth: true,
})
}