Files
junhong_cmp_fiber/internal/service/shop/service.go
2026-07-24 16:07:18 +08:00

327 lines
9.2 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"
"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) 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
}
parentShopName := ""
if shop.ParentID != nil {
parentShop, err := s.shopStore.GetByID(ctx, *shop.ParentID)
if err == nil {
parentShopName = parentShop.ShopName
}
}
return &dto.ShopResponse{
ID: shop.ID,
ShopName: shop.ShopName,
ShopCode: shop.ShopCode,
ParentID: shop.ParentID,
ParentShopName: parentShopName,
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"),
}, 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.ContactPhone != "" {
filters["contact_phone"] = req.ContactPhone
}
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, "查询店铺列表失败")
}
parentShopNameMap, err := s.buildParentShopNameMap(ctx, shops)
if err != nil {
return nil, 0, errors.Wrap(errors.CodeInternalError, err, "查询上级店铺名称失败")
}
responses := make([]*dto.ShopResponse, 0, len(shops))
for _, shop := range shops {
parentShopName := ""
if shop.ParentID != nil {
parentShopName = parentShopNameMap[*shop.ParentID]
}
responses = append(responses, &dto.ShopResponse{
ID: shop.ID,
ShopName: shop.ShopName,
ShopCode: shop.ShopCode,
ParentID: shop.ParentID,
ParentShopName: parentShopName,
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"),
})
}
return responses, total, nil
}
// buildParentShopNameMap 批量查询店铺上级名称,避免列表接口出现 N+1 查询。
func (s *Service) buildParentShopNameMap(ctx context.Context, shops []*model.Shop) (map[uint]string, error) {
parentIDs := make([]uint, 0, len(shops))
parentIDSet := make(map[uint]struct{}, len(shops))
for _, shop := range shops {
if shop.ParentID == nil {
continue
}
if _, exists := parentIDSet[*shop.ParentID]; exists {
continue
}
parentIDSet[*shop.ParentID] = struct{}{}
parentIDs = append(parentIDs, *shop.ParentID)
}
if len(parentIDs) == 0 {
return map[uint]string{}, nil
}
parentShops, err := s.shopStore.GetByIDs(ctx, parentIDs)
if err != nil {
return nil, err
}
parentShopNameMap := make(map[uint]string, len(parentShops))
for _, parentShop := range parentShops {
parentShopNameMap[parentShop.ID] = parentShop.ShopName
}
return parentShopNameMap, 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)
}
// ListCascade 联级查询店铺,用于新增店铺或其他场景选择上级店铺
func (s *Service) ListCascade(ctx context.Context, req *dto.ShopCascadeRequest) ([]*dto.ShopCascadeItem, error) {
shops, err := s.shopStore.ListForCascade(ctx, req.ShopName, req.ParentID, req.ExcludeSelf)
if err != nil {
return nil, errors.Wrap(errors.CodeInternalError, err, "查询店铺失败")
}
if len(shops) == 0 {
return []*dto.ShopCascadeItem{}, nil
}
ids := make([]uint, 0, len(shops))
for _, shop := range shops {
ids = append(ids, shop.ID)
}
childParentIDs, err := s.shopStore.GetParentIDsWithChildren(ctx, ids)
if err != nil {
return nil, errors.Wrap(errors.CodeInternalError, err, "查询下级店铺失败")
}
hasChildrenSet := make(map[uint]bool, len(childParentIDs))
for _, pid := range childParentIDs {
hasChildrenSet[pid] = true
}
result := make([]*dto.ShopCascadeItem, 0, len(shops))
for _, shop := range shops {
result = append(result, &dto.ShopCascadeItem{
ID: shop.ID,
ShopName: shop.ShopName,
HasChildren: hasChildrenSet[shop.ID],
})
}
return result, nil
}
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)
}