All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m0s
单卡回收优化: - 移除 from_shop_id 参数,系统自动识别卡所属店铺 - 保持直属下级限制,混合来源分别处理 - 新增 GetDistributedStandaloneByICCIDRange/GetDistributedStandaloneByFilters 方法 店铺禁用拦截: - 登录时检查关联店铺状态,禁用店铺无法登录 - 新增 CodeShopDisabled 错误码 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
349 lines
10 KiB
Go
349 lines
10 KiB
Go
package shop
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/break/junhong_cmp_fiber/internal/model"
|
|
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
|
"github.com/break/junhong_cmp_fiber/internal/store"
|
|
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
|
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
|
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type Service struct {
|
|
shopStore *postgres.ShopStore
|
|
accountStore *postgres.AccountStore
|
|
shopRoleStore *postgres.ShopRoleStore
|
|
roleStore *postgres.RoleStore
|
|
}
|
|
|
|
func New(
|
|
shopStore *postgres.ShopStore,
|
|
accountStore *postgres.AccountStore,
|
|
shopRoleStore *postgres.ShopRoleStore,
|
|
roleStore *postgres.RoleStore,
|
|
) *Service {
|
|
return &Service{
|
|
shopStore: shopStore,
|
|
accountStore: accountStore,
|
|
shopRoleStore: shopRoleStore,
|
|
roleStore: roleStore,
|
|
}
|
|
}
|
|
|
|
func (s *Service) Create(ctx context.Context, req *dto.CreateShopRequest) (*dto.ShopResponse, error) {
|
|
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
if currentUserID == 0 {
|
|
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
}
|
|
|
|
existing, err := s.shopStore.GetByCode(ctx, req.ShopCode)
|
|
if err == nil && existing != nil {
|
|
return nil, errors.New(errors.CodeShopCodeExists, "店铺编号已存在")
|
|
}
|
|
|
|
level := 1
|
|
if req.ParentID != nil {
|
|
parent, err := s.shopStore.GetByID(ctx, *req.ParentID)
|
|
if err != nil {
|
|
return nil, errors.New(errors.CodeInvalidParentID, "上级店铺不存在或无效")
|
|
}
|
|
level = parent.Level + 1
|
|
if level > constants.ShopMaxLevel {
|
|
return nil, errors.New(errors.CodeShopLevelExceeded, "店铺层级不能超过 7 级")
|
|
}
|
|
}
|
|
|
|
existingAccount, err := s.accountStore.GetByUsername(ctx, req.InitUsername)
|
|
if err == nil && existingAccount != nil {
|
|
return nil, errors.New(errors.CodeUsernameExists, "初始账号用户名已存在")
|
|
}
|
|
|
|
existingAccount, err = s.accountStore.GetByPhone(ctx, req.InitPhone)
|
|
if err == nil && existingAccount != nil {
|
|
return nil, errors.New(errors.CodePhoneExists, "初始账号手机号已存在")
|
|
}
|
|
|
|
// 验证默认角色:必须存在、是客户角色且已启用
|
|
defaultRole, err := s.roleStore.GetByID(ctx, req.DefaultRoleID)
|
|
if err != nil {
|
|
return nil, errors.New(errors.CodeNotFound, "请选择默认角色")
|
|
}
|
|
if defaultRole.RoleType != constants.RoleTypeCustomer {
|
|
return nil, errors.New(errors.CodeInvalidParam, "店铺默认角色必须是客户角色")
|
|
}
|
|
if defaultRole.Status != constants.StatusEnabled {
|
|
return nil, errors.New(errors.CodeInvalidParam, "默认角色已禁用")
|
|
}
|
|
|
|
shop := &model.Shop{
|
|
ShopName: req.ShopName,
|
|
ShopCode: req.ShopCode,
|
|
ParentID: req.ParentID,
|
|
Level: level,
|
|
ContactName: req.ContactName,
|
|
ContactPhone: req.ContactPhone,
|
|
Province: req.Province,
|
|
City: req.City,
|
|
District: req.District,
|
|
Address: req.Address,
|
|
Status: constants.ShopStatusEnabled,
|
|
}
|
|
shop.Creator = currentUserID
|
|
shop.Updater = currentUserID
|
|
|
|
if err := s.shopStore.Create(ctx, shop); err != nil {
|
|
return nil, errors.Wrap(errors.CodeInternalError, err, "创建店铺失败")
|
|
}
|
|
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.InitPassword), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return nil, errors.Wrap(errors.CodeInternalError, err, "密码哈希失败")
|
|
}
|
|
|
|
account := &model.Account{
|
|
Username: req.InitUsername,
|
|
Phone: req.InitPhone,
|
|
Password: string(hashedPassword),
|
|
UserType: constants.UserTypeAgent,
|
|
ShopID: &shop.ID,
|
|
Status: constants.StatusEnabled,
|
|
}
|
|
account.Creator = currentUserID
|
|
account.Updater = currentUserID
|
|
|
|
if err := s.accountStore.Create(ctx, account); err != nil {
|
|
return nil, errors.Wrap(errors.CodeInternalError, err, "创建初始账号失败")
|
|
}
|
|
|
|
// 设置店铺默认角色
|
|
shopRole := &model.ShopRole{
|
|
ShopID: shop.ID,
|
|
RoleID: req.DefaultRoleID,
|
|
Status: constants.StatusEnabled,
|
|
Creator: currentUserID,
|
|
Updater: currentUserID,
|
|
}
|
|
if err := s.shopRoleStore.BatchCreate(ctx, []*model.ShopRole{shopRole}); err != nil {
|
|
return nil, errors.Wrap(errors.CodeInternalError, err, "设置店铺默认角色失败")
|
|
}
|
|
|
|
return &dto.ShopResponse{
|
|
ID: shop.ID,
|
|
ShopName: shop.ShopName,
|
|
ShopCode: shop.ShopCode,
|
|
ParentID: shop.ParentID,
|
|
Level: shop.Level,
|
|
ContactName: shop.ContactName,
|
|
ContactPhone: shop.ContactPhone,
|
|
Province: shop.Province,
|
|
City: shop.City,
|
|
District: shop.District,
|
|
Address: shop.Address,
|
|
Status: shop.Status,
|
|
CreatedAt: shop.CreatedAt.Format("2006-01-02 15:04:05"),
|
|
UpdatedAt: shop.UpdatedAt.Format("2006-01-02 15:04:05"),
|
|
}, nil
|
|
}
|
|
|
|
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateShopRequest) (*dto.ShopResponse, error) {
|
|
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
if currentUserID == 0 {
|
|
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
}
|
|
|
|
shop, err := s.shopStore.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, errors.New(errors.CodeShopNotFound, "店铺不存在")
|
|
}
|
|
|
|
shop.ShopName = req.ShopName
|
|
shop.ContactName = req.ContactName
|
|
shop.ContactPhone = req.ContactPhone
|
|
shop.Province = req.Province
|
|
shop.City = req.City
|
|
shop.District = req.District
|
|
shop.Address = req.Address
|
|
shop.Status = req.Status
|
|
shop.Updater = currentUserID
|
|
|
|
if err := s.shopStore.Update(ctx, shop); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &dto.ShopResponse{
|
|
ID: shop.ID,
|
|
ShopName: shop.ShopName,
|
|
ShopCode: shop.ShopCode,
|
|
ParentID: shop.ParentID,
|
|
Level: shop.Level,
|
|
ContactName: shop.ContactName,
|
|
ContactPhone: shop.ContactPhone,
|
|
Province: shop.Province,
|
|
City: shop.City,
|
|
District: shop.District,
|
|
Address: shop.Address,
|
|
Status: shop.Status,
|
|
CreatedAt: shop.CreatedAt.Format("2006-01-02 15:04:05"),
|
|
UpdatedAt: shop.UpdatedAt.Format("2006-01-02 15:04:05"),
|
|
}, nil
|
|
}
|
|
|
|
// Disable 禁用店铺
|
|
func (s *Service) Disable(ctx context.Context, id uint) error {
|
|
// 获取当前用户 ID
|
|
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
if currentUserID == 0 {
|
|
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
}
|
|
|
|
// 查询店铺
|
|
shop, err := s.shopStore.GetByID(ctx, id)
|
|
if err != nil {
|
|
return errors.New(errors.CodeShopNotFound, "店铺不存在")
|
|
}
|
|
|
|
// 更新状态
|
|
shop.Status = constants.StatusDisabled
|
|
shop.Updater = currentUserID
|
|
|
|
return s.shopStore.Update(ctx, shop)
|
|
}
|
|
|
|
// Enable 启用店铺
|
|
func (s *Service) Enable(ctx context.Context, id uint) error {
|
|
// 获取当前用户 ID
|
|
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
if currentUserID == 0 {
|
|
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
}
|
|
|
|
// 查询店铺
|
|
shop, err := s.shopStore.GetByID(ctx, id)
|
|
if err != nil {
|
|
return errors.New(errors.CodeShopNotFound, "店铺不存在")
|
|
}
|
|
|
|
// 更新状态
|
|
shop.Status = constants.StatusEnabled
|
|
shop.Updater = currentUserID
|
|
|
|
return s.shopStore.Update(ctx, shop)
|
|
}
|
|
|
|
// GetByID 获取店铺详情
|
|
func (s *Service) GetByID(ctx context.Context, id uint) (*model.Shop, error) {
|
|
shop, err := s.shopStore.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, errors.New(errors.CodeShopNotFound, "店铺不存在")
|
|
}
|
|
return shop, nil
|
|
}
|
|
|
|
func (s *Service) ListShopResponses(ctx context.Context, req *dto.ShopListRequest) ([]*dto.ShopResponse, int64, error) {
|
|
opts := &store.QueryOptions{
|
|
Page: req.Page,
|
|
PageSize: req.PageSize,
|
|
OrderBy: "created_at DESC",
|
|
}
|
|
if opts.Page == 0 {
|
|
opts.Page = 1
|
|
}
|
|
if opts.PageSize == 0 {
|
|
opts.PageSize = constants.DefaultPageSize
|
|
}
|
|
|
|
filters := make(map[string]interface{})
|
|
if req.ShopName != "" {
|
|
filters["shop_name"] = req.ShopName
|
|
}
|
|
if req.ShopCode != "" {
|
|
filters["shop_code"] = req.ShopCode
|
|
}
|
|
if req.ParentID != nil {
|
|
filters["parent_id"] = *req.ParentID
|
|
}
|
|
if req.Level != nil {
|
|
filters["level"] = *req.Level
|
|
}
|
|
if req.Status != nil {
|
|
filters["status"] = *req.Status
|
|
}
|
|
|
|
shops, total, err := s.shopStore.List(ctx, opts, filters)
|
|
if err != nil {
|
|
return nil, 0, errors.Wrap(errors.CodeInternalError, err, "查询店铺列表失败")
|
|
}
|
|
|
|
responses := make([]*dto.ShopResponse, 0, len(shops))
|
|
for _, shop := range shops {
|
|
responses = append(responses, &dto.ShopResponse{
|
|
ID: shop.ID,
|
|
ShopName: shop.ShopName,
|
|
ShopCode: shop.ShopCode,
|
|
ParentID: shop.ParentID,
|
|
Level: shop.Level,
|
|
ContactName: shop.ContactName,
|
|
ContactPhone: shop.ContactPhone,
|
|
Province: shop.Province,
|
|
City: shop.City,
|
|
District: shop.District,
|
|
Address: shop.Address,
|
|
Status: shop.Status,
|
|
CreatedAt: shop.CreatedAt.Format("2006-01-02 15:04:05"),
|
|
UpdatedAt: shop.UpdatedAt.Format("2006-01-02 15:04:05"),
|
|
})
|
|
}
|
|
|
|
return responses, total, nil
|
|
}
|
|
|
|
func (s *Service) List(ctx context.Context, opts *store.QueryOptions, filters map[string]interface{}) ([]*model.Shop, int64, error) {
|
|
return s.shopStore.List(ctx, opts, filters)
|
|
}
|
|
|
|
func (s *Service) Delete(ctx context.Context, id uint) error {
|
|
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
if currentUserID == 0 {
|
|
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
}
|
|
|
|
shop, err := s.shopStore.GetByID(ctx, id)
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return errors.New(errors.CodeShopNotFound, "店铺不存在")
|
|
}
|
|
return errors.Wrap(errors.CodeInternalError, err, "获取店铺失败")
|
|
}
|
|
|
|
accounts, err := s.accountStore.GetByShopID(ctx, shop.ID)
|
|
if err != nil {
|
|
return errors.Wrap(errors.CodeInternalError, err, "查询店铺账号失败")
|
|
}
|
|
|
|
if len(accounts) > 0 {
|
|
accountIDs := make([]uint, 0, len(accounts))
|
|
for _, account := range accounts {
|
|
accountIDs = append(accountIDs, account.ID)
|
|
}
|
|
if err := s.accountStore.BulkUpdateStatus(ctx, accountIDs, constants.StatusDisabled, currentUserID); err != nil {
|
|
return errors.Wrap(errors.CodeInternalError, err, "禁用店铺账号失败")
|
|
}
|
|
}
|
|
|
|
if err := s.shopStore.Delete(ctx, id); err != nil {
|
|
return errors.Wrap(errors.CodeInternalError, err, "删除店铺失败")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetSubordinateShopIDs 获取下级店铺 ID 列表(包含自己)
|
|
func (s *Service) GetSubordinateShopIDs(ctx context.Context, shopID uint) ([]uint, error) {
|
|
return s.shopStore.GetSubordinateShopIDs(ctx, shopID)
|
|
}
|