Files
junhong_cmp_fiber/internal/model/dto/business_user_group_dto.go
break c7f9e005af
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 1h43m42s
feat(业务用户组): AUG26-003 业务用户组与店铺负责人分组导入
- 迁移 000221:新增 tb_business_user_group、tb_business_user_group_member、tb_shop_business_owner_import_task,成员一账号一行由部分唯一索引保证,店铺所属组按当前负责人实时推导,不回填历史分组。
- 用户组 CRUD、成员改组/清空归属、店铺批量交接(原子失败不部分写入)。
- 店铺负责人 CSV 导入任务:逐行独立事务、逐行明细、任务级与行级失败分离。
- 读侧推导与筛选:未分组、业务线、停用组可筛出并带停用标记。
- 补齐操作审计动作与资源、openapi 清单、发布门禁巡检表清单。
- 归档 add-shop-salesperson-groups 变更并同步 openspec/specs/business-user-group,补齐 AUG26-003 验证证据链。
2026-09-14 16:51:44 +08:00

197 lines
12 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 dto
import "github.com/bytedance/sonic"
// 业务用户组 DTO。
// 枚举说明enabled 与 business_line 取值必须与 pkg/constants 的
// StatusDisabled/StatusEnabled 与 BusinessLineStandard/BusinessLineSmart/BusinessLineOther 保持一致。
// CreateBusinessUserGroupRequest 创建业务用户组请求。
type CreateBusinessUserGroupRequest struct {
Code string `json:"code" validate:"required,min=1,max=64" required:"true" minLength:"1" maxLength:"64" description:"业务用户组稳定编码1-64 字符,未删除组内唯一,创建后不可修改"`
Name string `json:"name" validate:"required,min=1,max=100" required:"true" minLength:"1" maxLength:"100" description:"业务用户组名称1-100 字符"`
BusinessLine string `json:"business_line" validate:"omitempty,oneof=standard smart other" enum:"standard,smart,other" description:"所属业务线 (standard:标品, smart:智能产品, other:其他),留空表示不设置"`
Sort *int64 `json:"sort" validate:"omitempty,min=0" minimum:"0" description:"排序值,非负整数,默认 0"`
Enabled *bool `json:"enabled" description:"是否启用,默认启用;停用后不得新增成员,也不得作为批量目标"`
Remark string `json:"remark" validate:"omitempty,max=500" maxLength:"500" description:"备注,最多 500 字符"`
}
// UpdateBusinessUserGroupRequest 更新业务用户组请求。
// 仅传入的字段被修改;稳定编码永不允许修改,本请求不提供该字段。
// business_line 传显式空字符串表示清空业务线。
type UpdateBusinessUserGroupRequest struct {
Name *string `json:"name" validate:"omitempty,min=1,max=100" minLength:"1" maxLength:"100" description:"业务用户组名称1-100 字符"`
BusinessLine *string `json:"business_line" enum:"standard,smart,other" description:"所属业务线 (standard:标品, smart:智能产品, other:其他);字段缺失不修改,传空字符串清空;取值白名单在服务层校验"`
Sort *int64 `json:"sort" validate:"omitempty,min=0" minimum:"0" description:"排序值,非负整数"`
Enabled *bool `json:"enabled" description:"是否启用;停用后不得新增成员,已有成员关系保留并显示已停用"`
Remark *string `json:"remark" validate:"omitempty,max=500" maxLength:"500" description:"备注,最多 500 字符"`
}
// BusinessUserGroupListRequest 查询业务用户组列表请求。
type BusinessUserGroupListRequest struct {
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码,默认 1"`
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量,默认 20最大 100"`
Enabled *bool `json:"enabled" query:"enabled" description:"按启用状态过滤;不传返回全部"`
Keyword string `json:"keyword" query:"keyword" validate:"omitempty,max=100" maxLength:"100" description:"按稳定编码或名称模糊搜索,最多 100 字符"`
BusinessLine *string `json:"business_line" query:"business_line" validate:"omitempty,oneof=standard smart other" enum:"standard,smart,other" description:"按所属业务线过滤 (standard:标品, smart:智能产品, other:其他)"`
}
// BusinessUserGroupResponse 业务用户组响应。
type BusinessUserGroupResponse struct {
ID uint `json:"id" description:"业务用户组ID"`
Code string `json:"code" description:"业务用户组稳定编码,创建后不可修改"`
Name string `json:"name" description:"业务用户组名称"`
BusinessLine string `json:"business_line" description:"所属业务线 (standard:标品, smart:智能产品, other:其他),空字符串表示未设置"`
BusinessLineName string `json:"business_line_name" description:"所属业务线中文名称,未设置时为空字符串"`
Sort int64 `json:"sort" description:"排序值"`
Enabled bool `json:"enabled" description:"是否启用;停用后不得新增成员,也不得作为批量目标"`
Remark string `json:"remark" description:"备注"`
CreatedAt string `json:"created_at" description:"创建时间"`
UpdatedAt string `json:"updated_at" description:"更新时间"`
}
// BusinessUserGroupPageResult 业务用户组分页响应。
type BusinessUserGroupPageResult struct {
Items []*BusinessUserGroupResponse `json:"items" description:"业务用户组列表"`
Total int64 `json:"total" description:"总记录数"`
Page int `json:"page" description:"当前页码"`
Size int `json:"size" description:"每页数量"`
}
// SetBusinessUserGroupMembersRequest 批量设置平台用户业务用户组归属请求。
// 设置直接替换每个账号的原归属;任一账号不是启用平台用户或目标组未启用时整批不修改。
type SetBusinessUserGroupMembersRequest struct {
AccountIDs []uint `json:"account_ids" validate:"required,min=1,dive,min=1" required:"true" description:"平台用户账号ID列表至少一个每个账号必须是启用平台用户"`
}
// ClearBusinessUserGroupMembersRequest 批量清空平台用户业务用户组归属请求。
type ClearBusinessUserGroupMembersRequest struct {
AccountIDs []uint `json:"account_ids" validate:"required,min=1,dive,min=1" required:"true" description:"平台用户账号ID列表至少一个每个账号必须是启用平台用户"`
}
// BusinessUserGroupMemberRequest 业务用户组成员维护路径参数。
type BusinessUserGroupMemberRequest struct {
ID uint `path:"id" required:"true" description:"业务用户组ID"`
}
// BusinessUserGroupDeleteRequest 业务用户组删除二次确认请求。
type BusinessUserGroupDeleteRequest struct {
Confirm bool `json:"confirm" validate:"required" required:"true" description:"确认删除,必须为 true仅无成员的用户组可删除有成员时只能停用或先移走成员"`
}
// BusinessUserGroupUpdateParams 更新业务用户组参数(路径参数 + 请求体,用于文档生成)。
type BusinessUserGroupUpdateParams struct {
IDReq
UpdateBusinessUserGroupRequest
}
// BusinessUserGroupDeleteParams 删除业务用户组参数(路径参数 + 二次确认,用于文档生成)。
type BusinessUserGroupDeleteParams struct {
IDReq
BusinessUserGroupDeleteRequest
}
// ClearBusinessUserGroupMembersParams 清空平台用户分组归属请求(用于 DELETE 显式请求体)。
type ClearBusinessUserGroupMembersParams struct {
ClearBusinessUserGroupMembersRequest
}
// BusinessUserGroupMembersResult 成员维护结果。
type BusinessUserGroupMembersResult struct {
GroupID uint `json:"group_id" description:"目标业务用户组ID清空操作为 0"`
AccountIDs []uint `json:"account_ids" description:"本次成功维护归属的平台用户账号ID列表"`
}
// BatchUpdateShopBusinessOwnerRequest 勾选店铺批量设置或清空负责人请求。
// business_owner_account_id 缺失时请求非法;显式 null 表示清空负责人。
type BatchUpdateShopBusinessOwnerRequest struct {
ShopIDs []uint `json:"shop_ids" validate:"required,min=1,max=500,dive,min=1" required:"true" minItems:"1" maxItems:"500" description:"店铺ID列表至少一个且去重最多 500 个"`
BusinessOwnerAccountID *uint `json:"business_owner_account_id" nullable:"true" description:"目标平台业务员账号ID显式 null 表示清空负责人"`
BusinessOwnerAccountIDSet bool `json:"-"`
}
// UnmarshalJSON 解析批量交接请求并保留负责人字段「缺失」和「显式 null」的差异。
func (r *BatchUpdateShopBusinessOwnerRequest) UnmarshalJSON(data []byte) error {
type plain BatchUpdateShopBusinessOwnerRequest
var decoded plain
if err := sonic.Unmarshal(data, &decoded); err != nil {
return err
}
var fields map[string]any
if err := sonic.Unmarshal(data, &fields); err != nil {
return err
}
decoded.BusinessOwnerAccountIDSet = false
if _, exists := fields["business_owner_account_id"]; exists {
decoded.BusinessOwnerAccountIDSet = true
}
*r = BatchUpdateShopBusinessOwnerRequest(decoded)
return nil
}
// BatchUpdateShopBusinessOwnerResult 勾选店铺批量交接结果。
type BatchUpdateShopBusinessOwnerResult struct {
BatchKey string `json:"batch_key" description:"批次标识,可用于审计关联时间线"`
ShopCount int `json:"shop_count" description:"本次成功变更的店铺数量"`
Cleared bool `json:"cleared" description:"本次是否为清空负责人操作"`
BusinessOwnerAccountID *uint `json:"business_owner_account_id" description:"目标平台业务员账号ID清空时为 null"`
}
// CreateShopBusinessOwnerImportRequest 创建店铺负责人 CSV 导入任务请求。
type CreateShopBusinessOwnerImportRequest struct {
FileKey string `json:"file_key" validate:"required,min=1,max=500" required:"true" minLength:"1" maxLength:"500" description:"CSV 对象存储 Key必须以 shop-imports/ 开头且扩展名为 .csv通过 POST /api/admin/storage/upload-url 获取)"`
}
// ListShopBusinessOwnerImportRequest 查询店铺负责人导入任务列表请求。
type ListShopBusinessOwnerImportRequest struct {
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码,默认 1"`
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量,默认 20最大 100"`
Status *int `json:"status" query:"status" validate:"omitempty,min=1,max=4" minimum:"1" maximum:"4" description:"任务状态 (1:待处理, 2:处理中, 3:已完成, 4:失败)"`
}
// GetShopBusinessOwnerImportRequest 店铺负责人导入任务详情路径参数。
type GetShopBusinessOwnerImportRequest struct {
ID uint `path:"id" required:"true" description:"导入任务ID"`
}
// ShopBusinessOwnerImportTaskResponse 店铺负责人导入任务响应。
type ShopBusinessOwnerImportTaskResponse struct {
ID uint `json:"id" description:"导入任务ID"`
TaskNo string `json:"task_no" description:"导入任务编号"`
FileName string `json:"file_name" description:"上传的源 CSV 文件名"`
Status int `json:"status" description:"任务状态 (1:待处理, 2:处理中, 3:已完成, 4:失败)"`
StatusName string `json:"status_name" description:"任务状态中文名称"`
TotalCount int `json:"total_count" description:"数据行总数;任务级失败时为 0"`
SuccessCount int `json:"success_count" description:"成功行数"`
FailCount int `json:"fail_count" description:"失败行数"`
ErrorMessage string `json:"error_message" description:"任务级失败原因;与行级失败原因分开记录"`
CreatorName string `json:"creator_name" description:"任务创建人名称快照"`
CreatedAt string `json:"created_at" description:"创建时间"`
StartedAt string `json:"started_at" description:"开始处理时间"`
CompletedAt string `json:"completed_at" description:"完成时间"`
}
// ShopBusinessOwnerImportTaskPageResult 店铺负责人导入任务分页响应。
type ShopBusinessOwnerImportTaskPageResult struct {
Items []*ShopBusinessOwnerImportTaskResponse `json:"items" description:"导入任务列表"`
Total int64 `json:"total" description:"总记录数"`
Page int `json:"page" description:"当前页码"`
Size int `json:"size" description:"每页数量"`
}
// ShopBusinessOwnerImportItemResponse 店铺负责人导入逐行结果。
type ShopBusinessOwnerImportItemResponse struct {
Line int `json:"line" description:"行号,自数据首行起计(表头不计入)"`
ShopCode string `json:"shop_code" description:"店铺编码"`
OperationType string `json:"operation_type" description:"操作类型 (换绑/清空)"`
Status int `json:"status" description:"行状态 (3:成功, 4:失败)"`
StatusName string `json:"status_name" description:"行状态中文名称"`
Reason string `json:"reason" description:"失败原因;成功行为空"`
}
// ShopBusinessOwnerImportTaskDetailResponse 店铺负责人导入任务详情响应。
type ShopBusinessOwnerImportTaskDetailResponse struct {
ShopBusinessOwnerImportTaskResponse
Items []ShopBusinessOwnerImportItemResponse `json:"items" description:"逐行结果明细;任务级失败时为空数组"`
}