feat(业务用户组): AUG26-003 业务用户组与店铺负责人分组导入
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 1h43m42s
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 1h43m42s
- 迁移 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 验证证据链。
This commit is contained in:
231
internal/handler/admin/business_user_group.go
Normal file
231
internal/handler/admin/business_user_group.go
Normal file
@@ -0,0 +1,231 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
businessusergroupApp "github.com/break/junhong_cmp_fiber/internal/application/businessusergroup"
|
||||
shopApp "github.com/break/junhong_cmp_fiber/internal/application/shop"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
businessusergroupQuery "github.com/break/junhong_cmp_fiber/internal/query/businessusergroup"
|
||||
"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/response"
|
||||
)
|
||||
|
||||
// BusinessUserGroupHandler 业务用户组与店铺负责人批量交接处理器。
|
||||
type BusinessUserGroupHandler struct {
|
||||
service *businessusergroupApp.Service
|
||||
query *businessusergroupQuery.Query
|
||||
batchService *shopApp.BatchBusinessOwnerService
|
||||
validator *validator.Validate
|
||||
}
|
||||
|
||||
// NewBusinessUserGroupHandler 创建业务用户组处理器。
|
||||
func NewBusinessUserGroupHandler(service *businessusergroupApp.Service, validator *validator.Validate) *BusinessUserGroupHandler {
|
||||
return &BusinessUserGroupHandler{service: service, validator: validator}
|
||||
}
|
||||
|
||||
// SetQuery 注入业务用户组读取投影。
|
||||
func (h *BusinessUserGroupHandler) SetQuery(query *businessusergroupQuery.Query) {
|
||||
h.query = query
|
||||
}
|
||||
|
||||
// SetBatchService 注入店铺负责人批量交接事务脚本。
|
||||
func (h *BusinessUserGroupHandler) SetBatchService(service *shopApp.BatchBusinessOwnerService) {
|
||||
h.batchService = service
|
||||
}
|
||||
|
||||
// Create 创建业务用户组。
|
||||
// POST /api/admin/business-user-groups
|
||||
func (h *BusinessUserGroupHandler) Create(c *fiber.Ctx) error {
|
||||
if err := requirePlatformManagement(c); err != nil {
|
||||
return err
|
||||
}
|
||||
var request dto.CreateBusinessUserGroupRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
if err := h.validate(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
result, err := h.service.Create(c.UserContext(), &request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// List 查询业务用户组列表。
|
||||
// GET /api/admin/business-user-groups
|
||||
func (h *BusinessUserGroupHandler) List(c *fiber.Ctx) error {
|
||||
if err := requirePlatformManagement(c); err != nil {
|
||||
return err
|
||||
}
|
||||
var request dto.BusinessUserGroupListRequest
|
||||
if err := c.QueryParser(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
if err := h.validate(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
if h.query == nil {
|
||||
return errors.New(errors.CodeInternalError, "业务用户组查询尚未配置")
|
||||
}
|
||||
result, err := h.query.List(c.UserContext(), request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.SuccessWithPagination(c, result.Items, result.Total, result.Page, result.Size)
|
||||
}
|
||||
|
||||
// Detail 查询业务用户组详情。
|
||||
// GET /api/admin/business-user-groups/:id
|
||||
func (h *BusinessUserGroupHandler) Detail(c *fiber.Ctx) error {
|
||||
if err := requirePlatformManagement(c); err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := pathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if h.query == nil {
|
||||
return errors.New(errors.CodeInternalError, "业务用户组查询尚未配置")
|
||||
}
|
||||
result, err := h.query.Detail(c.UserContext(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// Update 更新业务用户组的名称、业务线、排序、启停与备注;编码不可修改。
|
||||
// PUT /api/admin/business-user-groups/:id
|
||||
func (h *BusinessUserGroupHandler) Update(c *fiber.Ctx) error {
|
||||
if err := requirePlatformManagement(c); err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := pathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var request dto.UpdateBusinessUserGroupRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
if err := h.validate(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
result, err := h.service.Update(c.UserContext(), id, &request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// Delete 二次确认删除无成员的业务用户组。
|
||||
// DELETE /api/admin/business-user-groups/:id
|
||||
func (h *BusinessUserGroupHandler) Delete(c *fiber.Ctx) error {
|
||||
if err := requirePlatformManagement(c); err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := pathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var request dto.BusinessUserGroupDeleteRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
if !request.Confirm {
|
||||
return errors.New(errors.CodeInvalidParam, "删除业务用户组必须二次确认")
|
||||
}
|
||||
if err := h.service.Delete(c.UserContext(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, nil)
|
||||
}
|
||||
|
||||
// SetMembers 批量设置平台用户的业务用户组归属,直接替换原归属。
|
||||
// PUT /api/admin/business-user-groups/:id/members
|
||||
func (h *BusinessUserGroupHandler) SetMembers(c *fiber.Ctx) error {
|
||||
if err := requirePlatformManagement(c); err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := pathID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var request dto.SetBusinessUserGroupMembersRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
if err := h.validate(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
result, err := h.service.SetMembers(c.UserContext(), id, &request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// ClearMembers 批量清空平台用户的业务用户组归属。
|
||||
// DELETE /api/admin/business-user-groups/members
|
||||
func (h *BusinessUserGroupHandler) ClearMembers(c *fiber.Ctx) error {
|
||||
if err := requirePlatformManagement(c); err != nil {
|
||||
return err
|
||||
}
|
||||
var request dto.ClearBusinessUserGroupMembersRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
if err := h.validate(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
result, err := h.service.ClearMembers(c.UserContext(), &request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// BatchUpdateShopBusinessOwner 勾选店铺批量设置或清空平台业务员负责人。
|
||||
// PUT /api/admin/shops/business-owner/batch
|
||||
func (h *BusinessUserGroupHandler) BatchUpdateShopBusinessOwner(c *fiber.Ctx) error {
|
||||
if err := requirePlatformManagement(c); err != nil {
|
||||
return err
|
||||
}
|
||||
var request dto.BatchUpdateShopBusinessOwnerRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
if err := h.validate(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
if h.batchService == nil {
|
||||
return errors.New(errors.CodeInternalError, "店铺负责人批量交接服务尚未配置")
|
||||
}
|
||||
result, err := h.batchService.Execute(c.UserContext(), &request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
func (h *BusinessUserGroupHandler) validate(request any) error {
|
||||
if h.validator == nil {
|
||||
return errors.New(errors.CodeInternalError)
|
||||
}
|
||||
return h.validator.Struct(request)
|
||||
}
|
||||
|
||||
// requirePlatformManagement 校验调用者仅限超级管理员与平台账号,代理与企业统一返回 403。
|
||||
func requirePlatformManagement(c *fiber.Ctx) error {
|
||||
userType := middleware.GetUserTypeFromContext(c.UserContext())
|
||||
if userType != constants.UserTypeSuperAdmin && userType != constants.UserTypePlatform {
|
||||
return errors.New(errors.CodeForbidden, constants.PlatformManagementForbiddenMessage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
71
internal/handler/admin/shop_business_owner_import.go
Normal file
71
internal/handler/admin/shop_business_owner_import.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
importService "github.com/break/junhong_cmp_fiber/internal/service/shop_business_owner_import"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||
)
|
||||
|
||||
// ShopBusinessOwnerImportHandler 店铺负责人 CSV 导入任务处理器。
|
||||
type ShopBusinessOwnerImportHandler struct {
|
||||
service *importService.Service
|
||||
}
|
||||
|
||||
// NewShopBusinessOwnerImportHandler 创建店铺负责人导入任务处理器。
|
||||
func NewShopBusinessOwnerImportHandler(service *importService.Service) *ShopBusinessOwnerImportHandler {
|
||||
return &ShopBusinessOwnerImportHandler{service: service}
|
||||
}
|
||||
|
||||
// Create 创建店铺负责人 CSV 导入任务。
|
||||
// POST /api/admin/shops/business-owner-imports
|
||||
func (h *ShopBusinessOwnerImportHandler) Create(c *fiber.Ctx) error {
|
||||
if err := requirePlatformManagement(c); err != nil {
|
||||
return err
|
||||
}
|
||||
var request dto.CreateShopBusinessOwnerImportRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
result, err := h.service.Create(c.UserContext(), &request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
|
||||
// List 查询店铺负责人导入任务列表。
|
||||
// GET /api/admin/shops/business-owner-imports
|
||||
func (h *ShopBusinessOwnerImportHandler) List(c *fiber.Ctx) error {
|
||||
if err := requirePlatformManagement(c); err != nil {
|
||||
return err
|
||||
}
|
||||
var request dto.ListShopBusinessOwnerImportRequest
|
||||
if err := c.QueryParser(&request); err != nil {
|
||||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// Detail 查询店铺负责人导入任务详情与逐行结果。
|
||||
// GET /api/admin/shops/business-owner-imports/:id
|
||||
func (h *ShopBusinessOwnerImportHandler) Detail(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.GetByID(c.UserContext(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return response.Success(c, result)
|
||||
}
|
||||
Reference in New Issue
Block a user