Files
junhong_cmp_fiber/internal/handler/admin/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

158 lines
5.4 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 admin
import (
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
associationSvc "github.com/break/junhong_cmp_fiber/internal/service/phone_asset_association"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/response"
)
// PhoneAssetAssociationHandler 手机号—资产关联后台处理器。
// 仅提供查看投影与解除入口;关联只能由 H5 短信验证建立,后台不提供创建或补录入口。
type PhoneAssetAssociationHandler struct {
service *associationSvc.Service
validator *validator.Validate
}
// NewPhoneAssetAssociationHandler 创建手机号—资产关联处理器。
func NewPhoneAssetAssociationHandler(service *associationSvc.Service, validator *validator.Validate) *PhoneAssetAssociationHandler {
return &PhoneAssetAssociationHandler{service: service, validator: validator}
}
// List 查询手机号—资产关联列表。
// GET /api/admin/phone-asset-associations
func (h *PhoneAssetAssociationHandler) List(c *fiber.Ctx) error {
if err := requirePlatformManagement(c); err != nil {
return err
}
var request dto.ListPhoneAssetAssociationRequest
if err := c.QueryParser(&request); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
if err := h.validate("手机号资产关联查询参数不合法", &request); err != nil {
return err
}
result, err := h.service.List(c.UserContext(), &request)
if err != nil {
return err
}
return response.SuccessWithPagination(c, result.Items, result.Total, result.Page, result.Size)
}
// Unbind 解除指定的一条手机号—资产关联。
// DELETE /api/admin/phone-asset-associations/:id
func (h *PhoneAssetAssociationHandler) Unbind(c *fiber.Ctx) error {
if err := requirePlatformManagement(c); err != nil {
return err
}
id, err := pathID(c)
if err != nil {
return err
}
var request dto.UnbindPhoneAssetAssociationRequest
if err := c.BodyParser(&request); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
// 路径字段由 Handler 解析后回填再执行结构校验ENG-ERR-002
request.ID = id
if err := h.validate("手机号资产解绑参数不合法", &request); err != nil {
return err
}
result, err := h.service.Unbind(c.UserContext(), &request)
if err != nil {
return err
}
return response.Success(c, result)
}
// BatchUnbind 按资产集合解除全部当前有效关联。
// POST /api/admin/phone-asset-associations/batch-unbind
func (h *PhoneAssetAssociationHandler) BatchUnbind(c *fiber.Ctx) error {
if err := requirePlatformManagement(c); err != nil {
return err
}
var request dto.BatchUnbindPhoneAssetAssociationRequest
if err := c.BodyParser(&request); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
if err := h.validate("手机号资产批量解绑参数不合法", &request); err != nil {
return err
}
result, err := h.service.BatchUnbind(c.UserContext(), &request)
if err != nil {
return err
}
return response.Success(c, result)
}
// CreateImportTask 创建手机号资产解绑 CSV 导入任务。
// POST /api/admin/phone-asset-associations/unbind-imports
func (h *PhoneAssetAssociationHandler) CreateImportTask(c *fiber.Ctx) error {
if err := requirePlatformManagement(c); err != nil {
return err
}
var request dto.CreatePhoneAssetUnbindImportRequest
if err := c.BodyParser(&request); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
if err := h.validate("手机号资产解绑导入参数不合法", &request); err != nil {
return err
}
result, err := h.service.CreateImportTask(c.UserContext(), &request)
if err != nil {
return err
}
return response.Success(c, result)
}
// ListImportTasks 查询手机号资产解绑导入任务列表。
// GET /api/admin/phone-asset-associations/unbind-imports
func (h *PhoneAssetAssociationHandler) ListImportTasks(c *fiber.Ctx) error {
if err := requirePlatformManagement(c); err != nil {
return err
}
var request dto.ListPhoneAssetUnbindImportRequest
if err := c.QueryParser(&request); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
if err := h.validate("手机号资产解绑导入查询参数不合法", &request); err != nil {
return err
}
result, err := h.service.ListImportTasks(c.UserContext(), &request)
if err != nil {
return err
}
return response.SuccessWithPagination(c, result.Items, result.Total, result.Page, result.Size)
}
// ImportTaskDetail 查询手机号资产解绑导入任务详情与逐行结果。
// GET /api/admin/phone-asset-associations/unbind-imports/:id
func (h *PhoneAssetAssociationHandler) ImportTaskDetail(c *fiber.Ctx) error {
if err := requirePlatformManagement(c); err != nil {
return err
}
id, err := pathID(c)
if err != nil {
return err
}
result, err := h.service.GetImportTask(c.UserContext(), id)
if err != nil {
return err
}
return response.Success(c, result)
}
// validate 执行结构校验失败时返回字段级提示ENG-ERR-002
func (h *PhoneAssetAssociationHandler) validate(prefix string, request any) error {
if h.validator == nil {
return errors.New(errors.CodeInternalError)
}
if err := h.validator.Struct(request); err != nil {
return errors.New(errors.CodeInvalidParam, validationMessage(prefix, request, err))
}
return nil
}