暂存一下,防止丢失
This commit is contained in:
251
internal/application/shop/create.go
Normal file
251
internal/application/shop/create.go
Normal file
@@ -0,0 +1,251 @@
|
||||
// Package shop 提供店铺创建与业务员归属的简单写事务脚本。
|
||||
package shop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"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"
|
||||
)
|
||||
|
||||
// CreateService 收口平台与代理创建店铺的完整事务。
|
||||
type CreateService struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewCreateService 创建店铺创建事务脚本。
|
||||
func NewCreateService(db *gorm.DB) *CreateService {
|
||||
return &CreateService{db: db}
|
||||
}
|
||||
|
||||
// Create 按操作者类型执行平台显式归属或代理安全继承。
|
||||
func (s *CreateService) Create(ctx context.Context, request *dto.CreateShopRequest) (*dto.ShopResponse, error) {
|
||||
userType := middleware.GetUserTypeFromContext(ctx)
|
||||
operatorID := middleware.GetUserIDFromContext(ctx)
|
||||
if operatorID == 0 {
|
||||
return nil, errors.New(errors.CodeUnauthorized)
|
||||
}
|
||||
resolver := resolvePlatformBusinessOwner
|
||||
switch userType {
|
||||
case constants.UserTypeSuperAdmin, constants.UserTypePlatform:
|
||||
case constants.UserTypeAgent:
|
||||
if request.BusinessOwnerAccountIDSet {
|
||||
return nil, errors.New(errors.CodeForbidden, "无权限设置店铺业务员")
|
||||
}
|
||||
if request.ParentID == nil {
|
||||
return nil, errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||
}
|
||||
if err := middleware.CanManageShop(ctx, *request.ParentID); err != nil {
|
||||
return nil, errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||
}
|
||||
resolver = resolveInheritedBusinessOwner
|
||||
default:
|
||||
return nil, errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||
}
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(request.InitPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeInternalError, err, "密码哈希失败")
|
||||
}
|
||||
|
||||
var response *dto.ShopResponse
|
||||
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
created, createErr := createShop(tx, request, operatorID, string(hashedPassword), resolver)
|
||||
if createErr != nil {
|
||||
return createErr
|
||||
}
|
||||
response = created
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
type businessOwnerResolver func(*gorm.DB, *dto.CreateShopRequest, *model.Shop) (*uint, error)
|
||||
|
||||
func createShop(tx *gorm.DB, request *dto.CreateShopRequest, operatorID uint, hashedPassword string, resolveOwner businessOwnerResolver) (*dto.ShopResponse, error) {
|
||||
if exists, err := recordExists(tx, &model.Shop{}, "shop_code = ?", request.ShopCode); err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "校验店铺编号失败")
|
||||
} else if exists {
|
||||
return nil, errors.New(errors.CodeShopCodeExists, "店铺编号已存在")
|
||||
}
|
||||
if exists, err := recordExists(tx, &model.Account{}, "username = ?", request.InitUsername); err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "校验初始账号用户名失败")
|
||||
} else if exists {
|
||||
return nil, errors.New(errors.CodeUsernameExists, "初始账号用户名已存在")
|
||||
}
|
||||
if exists, err := recordExists(tx, &model.Account{}, "phone = ?", request.InitPhone); err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "校验初始账号手机号失败")
|
||||
} else if exists {
|
||||
return nil, errors.New(errors.CodePhoneExists, "初始账号手机号已存在")
|
||||
}
|
||||
|
||||
parent, level, err := resolveParent(tx, request.ParentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ownerID, err := resolveOwner(tx, request, parent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var role model.Role
|
||||
if err := tx.Where("id = ? AND role_type = ? AND status = ?", request.DefaultRoleID, constants.RoleTypeCustomer, constants.StatusEnabled).First(&role).Error; err != nil {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "请选择启用的客户角色")
|
||||
}
|
||||
|
||||
shop := &model.Shop{
|
||||
ShopName: request.ShopName, ShopCode: request.ShopCode, ParentID: request.ParentID,
|
||||
BusinessOwnerAccountID: ownerID, Level: level, ContactName: request.ContactName,
|
||||
ContactPhone: request.ContactPhone, Province: request.Province, City: request.City,
|
||||
District: request.District, Address: request.Address, Status: constants.ShopStatusEnabled,
|
||||
}
|
||||
shop.Creator = operatorID
|
||||
shop.Updater = operatorID
|
||||
if err := tx.Create(shop).Error; err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "创建店铺失败")
|
||||
}
|
||||
|
||||
account := &model.Account{
|
||||
Username: request.InitUsername, Phone: request.InitPhone, Password: hashedPassword,
|
||||
UserType: constants.UserTypeAgent, ShopID: &shop.ID, Status: constants.StatusEnabled, IsPrimary: true,
|
||||
}
|
||||
account.Creator = operatorID
|
||||
account.Updater = operatorID
|
||||
if err := tx.Create(account).Error; err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "创建初始账号失败")
|
||||
}
|
||||
if err := tx.Create(&model.AccountRole{
|
||||
AccountID: account.ID, RoleID: request.DefaultRoleID, Status: constants.StatusEnabled,
|
||||
Creator: operatorID, Updater: operatorID,
|
||||
}).Error; err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "为初始账号分配角色失败")
|
||||
}
|
||||
if err := tx.Create(&model.ShopRole{
|
||||
ShopID: shop.ID, RoleID: request.DefaultRoleID, Status: constants.StatusEnabled,
|
||||
Creator: operatorID, Updater: operatorID,
|
||||
}).Error; err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "设置店铺默认角色失败")
|
||||
}
|
||||
if err := tx.Create([]*model.AgentWallet{
|
||||
{
|
||||
ShopID: shop.ID, WalletType: constants.AgentWalletTypeMain,
|
||||
CreditEnabled: role.DefaultCreditEnabled, CreditLimit: role.DefaultCreditLimit,
|
||||
Currency: "CNY", Status: constants.AgentWalletStatusNormal, ShopIDTag: shop.ID,
|
||||
},
|
||||
{
|
||||
ShopID: shop.ID, WalletType: constants.AgentWalletTypeCommission,
|
||||
CreditEnabled: false, CreditLimit: 0,
|
||||
Currency: "CNY", Status: constants.AgentWalletStatusNormal, ShopIDTag: shop.ID,
|
||||
},
|
||||
}).Error; err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "初始化店铺钱包失败")
|
||||
}
|
||||
|
||||
parentName := ""
|
||||
if parent != nil {
|
||||
parentName = parent.ShopName
|
||||
}
|
||||
response := newShopResponse(shop, parentName)
|
||||
if err := fillBusinessOwnerResponse(tx, shop, response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func resolveParent(tx *gorm.DB, parentID *uint) (*model.Shop, int, error) {
|
||||
if parentID == nil {
|
||||
return nil, 1, nil
|
||||
}
|
||||
var parent model.Shop
|
||||
if err := tx.First(&parent, *parentID).Error; err != nil {
|
||||
return nil, 0, errors.New(errors.CodeInvalidParentID, "上级店铺不存在或无效")
|
||||
}
|
||||
level := parent.Level + 1
|
||||
if level > constants.ShopMaxLevel {
|
||||
return nil, 0, errors.New(errors.CodeShopLevelExceeded, "店铺层级不能超过 7 级")
|
||||
}
|
||||
return &parent, level, nil
|
||||
}
|
||||
|
||||
func resolvePlatformBusinessOwner(tx *gorm.DB, request *dto.CreateShopRequest, parent *model.Shop) (*uint, error) {
|
||||
if !request.BusinessOwnerAccountIDSet {
|
||||
if parent == nil || parent.BusinessOwnerAccountID == nil {
|
||||
return nil, nil
|
||||
}
|
||||
ownerID := *parent.BusinessOwnerAccountID
|
||||
return &ownerID, nil
|
||||
}
|
||||
if request.BusinessOwnerAccountID == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if *request.BusinessOwnerAccountID == 0 {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "业务员账号无效")
|
||||
}
|
||||
var account model.Account
|
||||
if err := tx.Where("id = ? AND user_type = ? AND status = ?", *request.BusinessOwnerAccountID, constants.UserTypePlatform, constants.StatusEnabled).
|
||||
First(&account).Error; err != nil {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "业务员账号无效或不可用")
|
||||
}
|
||||
ownerID := account.ID
|
||||
return &ownerID, nil
|
||||
}
|
||||
|
||||
func resolveInheritedBusinessOwner(_ *gorm.DB, _ *dto.CreateShopRequest, parent *model.Shop) (*uint, error) {
|
||||
if parent == nil || parent.BusinessOwnerAccountID == nil {
|
||||
return nil, nil
|
||||
}
|
||||
ownerID := *parent.BusinessOwnerAccountID
|
||||
return &ownerID, nil
|
||||
}
|
||||
|
||||
func recordExists(tx *gorm.DB, target any, query string, value any) (bool, error) {
|
||||
var count int64
|
||||
err := tx.Model(target).Where(query, value).Count(&count).Error
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
func newShopResponse(shop *model.Shop, parentName string) *dto.ShopResponse {
|
||||
return &dto.ShopResponse{
|
||||
ID: shop.ID, ShopName: shop.ShopName, ShopCode: shop.ShopCode, ParentID: shop.ParentID,
|
||||
BusinessOwnerAccountID: shop.BusinessOwnerAccountID,
|
||||
ParentShopName: parentName, Level: shop.Level, ContactName: shop.ContactName,
|
||||
ContactPhone: shop.ContactPhone, Province: shop.Province, City: shop.City,
|
||||
District: shop.District, Address: shop.Address, Status: shop.Status,
|
||||
StatusName: constants.GetStatusName(shop.Status), CreatedAt: shop.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
UpdatedAt: shop.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
}
|
||||
|
||||
func fillBusinessOwnerResponse(tx *gorm.DB, shop *model.Shop, response *dto.ShopResponse) error {
|
||||
if shop.BusinessOwnerAccountID == nil {
|
||||
return nil
|
||||
}
|
||||
var account model.Account
|
||||
err := tx.Unscoped().Where("id = ?", *shop.BusinessOwnerAccountID).First(&account).Error
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "查询业务员摘要失败")
|
||||
}
|
||||
response.BusinessOwnerUsername = account.Username
|
||||
response.BusinessOwnerPhoneSummary = maskBusinessOwnerPhone(account.Phone)
|
||||
response.BusinessOwnerAvailable = account.UserType == constants.UserTypePlatform && account.Status == constants.StatusEnabled && !account.DeletedAt.Valid
|
||||
return nil
|
||||
}
|
||||
|
||||
func maskBusinessOwnerPhone(phone string) string {
|
||||
phone = strings.TrimSpace(phone)
|
||||
if len(phone) < 7 {
|
||||
return ""
|
||||
}
|
||||
return phone[:3] + "****" + phone[len(phone)-4:]
|
||||
}
|
||||
Reference in New Issue
Block a user