refactor: 删除旧套餐系列分配和套餐分配 Service
业务逻辑已全部迁移至 shop_series_grant/service.go,旧 Service 层完整删除。底层 Store(shop_series_allocation_store、shop_package_allocation_store)保留,仍被佣金计算、订单服务和 Grant Service 使用。 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -1,432 +0,0 @@
|
|||||||
package shop_package_allocation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"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 {
|
|
||||||
packageAllocationStore *postgres.ShopPackageAllocationStore
|
|
||||||
seriesAllocationStore *postgres.ShopSeriesAllocationStore
|
|
||||||
priceHistoryStore *postgres.ShopPackageAllocationPriceHistoryStore
|
|
||||||
shopStore *postgres.ShopStore
|
|
||||||
packageStore *postgres.PackageStore
|
|
||||||
packageSeriesStore *postgres.PackageSeriesStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(
|
|
||||||
packageAllocationStore *postgres.ShopPackageAllocationStore,
|
|
||||||
seriesAllocationStore *postgres.ShopSeriesAllocationStore,
|
|
||||||
priceHistoryStore *postgres.ShopPackageAllocationPriceHistoryStore,
|
|
||||||
shopStore *postgres.ShopStore,
|
|
||||||
packageStore *postgres.PackageStore,
|
|
||||||
packageSeriesStore *postgres.PackageSeriesStore,
|
|
||||||
) *Service {
|
|
||||||
return &Service{
|
|
||||||
packageAllocationStore: packageAllocationStore,
|
|
||||||
seriesAllocationStore: seriesAllocationStore,
|
|
||||||
priceHistoryStore: priceHistoryStore,
|
|
||||||
shopStore: shopStore,
|
|
||||||
packageStore: packageStore,
|
|
||||||
packageSeriesStore: packageSeriesStore,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) Create(ctx context.Context, req *dto.CreateShopPackageAllocationRequest) (*dto.ShopPackageAllocationResponse, error) {
|
|
||||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
||||||
if currentUserID == 0 {
|
|
||||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
||||||
}
|
|
||||||
|
|
||||||
userType := middleware.GetUserTypeFromContext(ctx)
|
|
||||||
allocatorShopID := middleware.GetShopIDFromContext(ctx)
|
|
||||||
|
|
||||||
if userType == constants.UserTypeAgent && allocatorShopID == 0 {
|
|
||||||
return nil, errors.New(errors.CodeUnauthorized, "当前用户不属于任何店铺")
|
|
||||||
}
|
|
||||||
|
|
||||||
targetShop, err := s.shopStore.GetByID(ctx, req.ShopID)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeNotFound, "目标店铺不存在")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取店铺失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
if userType == constants.UserTypeAgent {
|
|
||||||
if targetShop.ParentID == nil || *targetShop.ParentID != allocatorShopID {
|
|
||||||
return nil, errors.New(errors.CodeForbidden, "只能为直属下级分配套餐")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pkg, err := s.packageStore.GetByID(ctx, req.PackageID)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeNotFound, "套餐不存在")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
existing, _ := s.packageAllocationStore.GetByShopAndPackage(ctx, req.ShopID, req.PackageID)
|
|
||||||
if existing != nil {
|
|
||||||
return nil, errors.New(errors.CodeConflict, "该店铺已有此套餐的分配配置")
|
|
||||||
}
|
|
||||||
|
|
||||||
seriesAllocation, err := s.seriesAllocationStore.GetByShopAndSeries(ctx, req.ShopID, pkg.SeriesID)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeInvalidParam, "请先分配该套餐所属的系列")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取系列分配失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
allocation := &model.ShopPackageAllocation{
|
|
||||||
ShopID: req.ShopID,
|
|
||||||
PackageID: req.PackageID,
|
|
||||||
AllocatorShopID: allocatorShopID,
|
|
||||||
CostPrice: req.CostPrice,
|
|
||||||
SeriesAllocationID: &seriesAllocation.ID,
|
|
||||||
Status: constants.StatusEnabled,
|
|
||||||
}
|
|
||||||
allocation.Creator = currentUserID
|
|
||||||
|
|
||||||
if err := s.packageAllocationStore.Create(ctx, allocation); err != nil {
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "创建分配失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.buildResponse(ctx, allocation, targetShop.ShopName, pkg.PackageName, pkg.PackageCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) Get(ctx context.Context, id uint) (*dto.ShopPackageAllocationResponse, error) {
|
|
||||||
allocation, err := s.packageAllocationStore.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeNotFound, "分配记录不存在")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
shop, _ := s.shopStore.GetByID(ctx, allocation.ShopID)
|
|
||||||
pkg, _ := s.packageStore.GetByIDUnscoped(ctx, allocation.PackageID)
|
|
||||||
|
|
||||||
shopName := ""
|
|
||||||
packageName := ""
|
|
||||||
packageCode := ""
|
|
||||||
if shop != nil {
|
|
||||||
shopName = shop.ShopName
|
|
||||||
}
|
|
||||||
if pkg != nil {
|
|
||||||
packageName = pkg.PackageName
|
|
||||||
packageCode = pkg.PackageCode
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.buildResponse(ctx, allocation, shopName, packageName, packageCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateShopPackageAllocationRequest) (*dto.ShopPackageAllocationResponse, error) {
|
|
||||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
||||||
if currentUserID == 0 {
|
|
||||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
||||||
}
|
|
||||||
|
|
||||||
allocation, err := s.packageAllocationStore.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeNotFound, "分配记录不存在")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.CostPrice != nil {
|
|
||||||
allocation.CostPrice = *req.CostPrice
|
|
||||||
}
|
|
||||||
allocation.Updater = currentUserID
|
|
||||||
|
|
||||||
if err := s.packageAllocationStore.Update(ctx, allocation); err != nil {
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "更新分配失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
shop, _ := s.shopStore.GetByID(ctx, allocation.ShopID)
|
|
||||||
pkg, _ := s.packageStore.GetByIDUnscoped(ctx, allocation.PackageID)
|
|
||||||
|
|
||||||
shopName := ""
|
|
||||||
packageName := ""
|
|
||||||
packageCode := ""
|
|
||||||
if shop != nil {
|
|
||||||
shopName = shop.ShopName
|
|
||||||
}
|
|
||||||
if pkg != nil {
|
|
||||||
packageName = pkg.PackageName
|
|
||||||
packageCode = pkg.PackageCode
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.buildResponse(ctx, allocation, shopName, packageName, packageCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) Delete(ctx context.Context, id uint) error {
|
|
||||||
_, err := s.packageAllocationStore.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return errors.New(errors.CodeNotFound, "分配记录不存在")
|
|
||||||
}
|
|
||||||
return errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.packageAllocationStore.Delete(ctx, id); err != nil {
|
|
||||||
return errors.Wrap(errors.CodeInternalError, err, "删除分配失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) List(ctx context.Context, req *dto.ShopPackageAllocationListRequest) ([]*dto.ShopPackageAllocationResponse, int64, error) {
|
|
||||||
opts := &store.QueryOptions{
|
|
||||||
Page: req.Page,
|
|
||||||
PageSize: req.PageSize,
|
|
||||||
OrderBy: "id DESC",
|
|
||||||
}
|
|
||||||
if opts.Page == 0 {
|
|
||||||
opts.Page = 1
|
|
||||||
}
|
|
||||||
if opts.PageSize == 0 {
|
|
||||||
opts.PageSize = constants.DefaultPageSize
|
|
||||||
}
|
|
||||||
|
|
||||||
filters := make(map[string]interface{})
|
|
||||||
if req.ShopID != nil {
|
|
||||||
filters["shop_id"] = *req.ShopID
|
|
||||||
}
|
|
||||||
if req.PackageID != nil {
|
|
||||||
filters["package_id"] = *req.PackageID
|
|
||||||
}
|
|
||||||
if req.SeriesAllocationID != nil {
|
|
||||||
filters["series_allocation_id"] = *req.SeriesAllocationID
|
|
||||||
}
|
|
||||||
if req.AllocatorShopID != nil {
|
|
||||||
filters["allocator_shop_id"] = *req.AllocatorShopID
|
|
||||||
}
|
|
||||||
if req.Status != nil {
|
|
||||||
filters["status"] = *req.Status
|
|
||||||
}
|
|
||||||
|
|
||||||
allocations, total, err := s.packageAllocationStore.List(ctx, opts, filters)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, errors.Wrap(errors.CodeInternalError, err, "查询分配列表失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
responses := make([]*dto.ShopPackageAllocationResponse, len(allocations))
|
|
||||||
for i, a := range allocations {
|
|
||||||
shop, _ := s.shopStore.GetByID(ctx, a.ShopID)
|
|
||||||
pkg, _ := s.packageStore.GetByIDUnscoped(ctx, a.PackageID)
|
|
||||||
|
|
||||||
shopName := ""
|
|
||||||
packageName := ""
|
|
||||||
packageCode := ""
|
|
||||||
if shop != nil {
|
|
||||||
shopName = shop.ShopName
|
|
||||||
}
|
|
||||||
if pkg != nil {
|
|
||||||
packageName = pkg.PackageName
|
|
||||||
packageCode = pkg.PackageCode
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, _ := s.buildResponse(ctx, a, shopName, packageName, packageCode)
|
|
||||||
responses[i] = resp
|
|
||||||
}
|
|
||||||
|
|
||||||
return responses, total, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
|
|
||||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
||||||
if currentUserID == 0 {
|
|
||||||
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 任务 3.2:所有者校验 —— 代理只能修改自己创建的分配记录的 status
|
|
||||||
// 使用系统级查询(不带数据权限过滤),再做业务层权限判断以返回正确的 403
|
|
||||||
allocation, err := s.packageAllocationStore.GetByIDForSystem(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return errors.New(errors.CodeNotFound, "分配记录不存在")
|
|
||||||
}
|
|
||||||
return errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
userType := middleware.GetUserTypeFromContext(ctx)
|
|
||||||
if userType == constants.UserTypeAgent {
|
|
||||||
callerShopID := middleware.GetShopIDFromContext(ctx)
|
|
||||||
// 代理用户只能修改自己作为 allocator 的记录
|
|
||||||
if allocation.AllocatorShopID != callerShopID {
|
|
||||||
return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.packageAllocationStore.UpdateStatus(ctx, id, status, currentUserID); err != nil {
|
|
||||||
return errors.Wrap(errors.CodeInternalError, err, "更新状态失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateShelfStatus 更新分配记录的上下架状态
|
|
||||||
// 代理独立控制自己客户侧套餐可见性,不影响平台全局状态
|
|
||||||
func (s *Service) UpdateShelfStatus(ctx context.Context, allocationID uint, shelfStatus int) error {
|
|
||||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
||||||
if currentUserID == 0 {
|
|
||||||
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
||||||
}
|
|
||||||
|
|
||||||
allocation, err := s.packageAllocationStore.GetByID(ctx, allocationID)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return errors.New(errors.CodeNotFound, "分配记录不存在")
|
|
||||||
}
|
|
||||||
return errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上架时检查套餐全局状态:禁用的套餐不允许上架
|
|
||||||
if shelfStatus == constants.ShelfStatusOn {
|
|
||||||
pkg, err := s.packageStore.GetByID(ctx, allocation.PackageID)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return errors.New(errors.CodeNotFound, "套餐不存在")
|
|
||||||
}
|
|
||||||
return errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
|
|
||||||
}
|
|
||||||
if pkg.Status == constants.StatusDisabled {
|
|
||||||
return errors.New(errors.CodeInvalidStatus, "套餐已禁用,无法上架")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.packageAllocationStore.UpdateShelfStatus(ctx, allocationID, shelfStatus, currentUserID); err != nil {
|
|
||||||
return errors.Wrap(errors.CodeInternalError, err, "更新上下架状态失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) buildResponse(ctx context.Context, a *model.ShopPackageAllocation, shopName, packageName, packageCode string) (*dto.ShopPackageAllocationResponse, error) {
|
|
||||||
var seriesID uint
|
|
||||||
seriesName := ""
|
|
||||||
|
|
||||||
pkg, _ := s.packageStore.GetByIDUnscoped(ctx, a.PackageID)
|
|
||||||
if pkg != nil {
|
|
||||||
seriesID = pkg.SeriesID
|
|
||||||
series, _ := s.packageSeriesStore.GetByIDUnscoped(ctx, pkg.SeriesID)
|
|
||||||
if series != nil {
|
|
||||||
seriesName = series.SeriesName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allocatorShopName := ""
|
|
||||||
if a.AllocatorShopID > 0 {
|
|
||||||
allocatorShop, _ := s.shopStore.GetByID(ctx, a.AllocatorShopID)
|
|
||||||
if allocatorShop != nil {
|
|
||||||
allocatorShopName = allocatorShop.ShopName
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
allocatorShopName = "平台"
|
|
||||||
}
|
|
||||||
|
|
||||||
return &dto.ShopPackageAllocationResponse{
|
|
||||||
ID: a.ID,
|
|
||||||
ShopID: a.ShopID,
|
|
||||||
ShopName: shopName,
|
|
||||||
PackageID: a.PackageID,
|
|
||||||
PackageName: packageName,
|
|
||||||
PackageCode: packageCode,
|
|
||||||
SeriesID: seriesID,
|
|
||||||
SeriesName: seriesName,
|
|
||||||
SeriesAllocationID: a.SeriesAllocationID,
|
|
||||||
AllocatorShopID: a.AllocatorShopID,
|
|
||||||
AllocatorShopName: allocatorShopName,
|
|
||||||
CostPrice: a.CostPrice,
|
|
||||||
Status: a.Status,
|
|
||||||
ShelfStatus: a.ShelfStatus,
|
|
||||||
CreatedAt: a.CreatedAt.Format(time.RFC3339),
|
|
||||||
UpdatedAt: a.UpdatedAt.Format(time.RFC3339),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) UpdateCostPrice(ctx context.Context, id uint, newCostPrice int64, changeReason string) (*dto.ShopPackageAllocationResponse, error) {
|
|
||||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
||||||
if currentUserID == 0 {
|
|
||||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
||||||
}
|
|
||||||
|
|
||||||
allocation, err := s.packageAllocationStore.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeNotFound, "分配记录不存在")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
if allocation.CostPrice == newCostPrice {
|
|
||||||
return nil, errors.New(errors.CodeInvalidParam, "新成本价与当前成本价相同")
|
|
||||||
}
|
|
||||||
|
|
||||||
oldCostPrice := allocation.CostPrice
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
priceHistory := &model.ShopPackageAllocationPriceHistory{
|
|
||||||
AllocationID: allocation.ID,
|
|
||||||
OldCostPrice: oldCostPrice,
|
|
||||||
NewCostPrice: newCostPrice,
|
|
||||||
ChangeReason: changeReason,
|
|
||||||
ChangedBy: currentUserID,
|
|
||||||
EffectiveFrom: now,
|
|
||||||
}
|
|
||||||
if err := s.priceHistoryStore.Create(ctx, priceHistory); err != nil {
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "创建价格历史记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
allocation.CostPrice = newCostPrice
|
|
||||||
allocation.Updater = currentUserID
|
|
||||||
if err := s.packageAllocationStore.Update(ctx, allocation); err != nil {
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "更新成本价失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
shop, _ := s.shopStore.GetByID(ctx, allocation.ShopID)
|
|
||||||
pkg, _ := s.packageStore.GetByIDUnscoped(ctx, allocation.PackageID)
|
|
||||||
|
|
||||||
shopName := ""
|
|
||||||
packageName := ""
|
|
||||||
packageCode := ""
|
|
||||||
if shop != nil {
|
|
||||||
shopName = shop.ShopName
|
|
||||||
}
|
|
||||||
if pkg != nil {
|
|
||||||
packageName = pkg.PackageName
|
|
||||||
packageCode = pkg.PackageCode
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.buildResponse(ctx, allocation, shopName, packageName, packageCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetPriceHistory(ctx context.Context, allocationID uint) ([]*model.ShopPackageAllocationPriceHistory, error) {
|
|
||||||
_, err := s.packageAllocationStore.GetByID(ctx, allocationID)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeNotFound, "分配记录不存在")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
history, err := s.priceHistoryStore.ListByAllocation(ctx, allocationID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取价格历史失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
return history, nil
|
|
||||||
}
|
|
||||||
@@ -1,353 +0,0 @@
|
|||||||
package shop_series_allocation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"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 {
|
|
||||||
seriesAllocationStore *postgres.ShopSeriesAllocationStore
|
|
||||||
packageAllocationStore *postgres.ShopPackageAllocationStore
|
|
||||||
shopStore *postgres.ShopStore
|
|
||||||
packageSeriesStore *postgres.PackageSeriesStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(
|
|
||||||
seriesAllocationStore *postgres.ShopSeriesAllocationStore,
|
|
||||||
packageAllocationStore *postgres.ShopPackageAllocationStore,
|
|
||||||
shopStore *postgres.ShopStore,
|
|
||||||
packageSeriesStore *postgres.PackageSeriesStore,
|
|
||||||
) *Service {
|
|
||||||
return &Service{
|
|
||||||
seriesAllocationStore: seriesAllocationStore,
|
|
||||||
packageAllocationStore: packageAllocationStore,
|
|
||||||
shopStore: shopStore,
|
|
||||||
packageSeriesStore: packageSeriesStore,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) Create(ctx context.Context, req *dto.CreateShopSeriesAllocationRequest) (*dto.ShopSeriesAllocationResponse, error) {
|
|
||||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
||||||
if currentUserID == 0 {
|
|
||||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
||||||
}
|
|
||||||
|
|
||||||
userType := middleware.GetUserTypeFromContext(ctx)
|
|
||||||
allocatorShopID := middleware.GetShopIDFromContext(ctx)
|
|
||||||
|
|
||||||
if userType == constants.UserTypeAgent && allocatorShopID == 0 {
|
|
||||||
return nil, errors.New(errors.CodeUnauthorized, "当前用户不属于任何店铺")
|
|
||||||
}
|
|
||||||
|
|
||||||
targetShop, err := s.shopStore.GetByID(ctx, req.ShopID)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeNotFound, "目标店铺不存在")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取店铺失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
if userType == constants.UserTypeAgent {
|
|
||||||
if targetShop.ParentID == nil || *targetShop.ParentID != allocatorShopID {
|
|
||||||
return nil, errors.New(errors.CodeForbidden, "只能为直属下级分配套餐系列")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
series, err := s.packageSeriesStore.GetByID(ctx, req.SeriesID)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeNotFound, "套餐系列不存在")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取套餐系列失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否已存在分配
|
|
||||||
exists, err := s.seriesAllocationStore.ExistsByShopAndSeries(ctx, req.ShopID, req.SeriesID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "检查分配记录失败")
|
|
||||||
}
|
|
||||||
if exists {
|
|
||||||
return nil, errors.New(errors.CodeConflict, "该店铺已分配此套餐系列")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 代理用户:检查自己是否有该系列的分配权限,且金额不能超过上级给的上限
|
|
||||||
// 平台用户:无上限限制,可自由设定金额
|
|
||||||
if userType == constants.UserTypeAgent {
|
|
||||||
allocatorAllocation, err := s.seriesAllocationStore.GetByShopAndSeries(ctx, allocatorShopID, req.SeriesID)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeForbidden, "您没有该套餐系列的分配权限")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取分配权限失败")
|
|
||||||
}
|
|
||||||
if req.OneTimeCommissionAmount > allocatorAllocation.OneTimeCommissionAmount {
|
|
||||||
return nil, errors.New(errors.CodeInvalidParam, "一次性佣金金额不能超过您的分配上限")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allocation := &model.ShopSeriesAllocation{
|
|
||||||
ShopID: req.ShopID,
|
|
||||||
SeriesID: req.SeriesID,
|
|
||||||
AllocatorShopID: allocatorShopID,
|
|
||||||
OneTimeCommissionAmount: req.OneTimeCommissionAmount,
|
|
||||||
EnableOneTimeCommission: false,
|
|
||||||
OneTimeCommissionTrigger: "",
|
|
||||||
OneTimeCommissionThreshold: 0,
|
|
||||||
EnableForceRecharge: false,
|
|
||||||
ForceRechargeAmount: 0,
|
|
||||||
ForceRechargeTriggerType: 2,
|
|
||||||
Status: constants.StatusEnabled,
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.EnableOneTimeCommission != nil {
|
|
||||||
allocation.EnableOneTimeCommission = *req.EnableOneTimeCommission
|
|
||||||
}
|
|
||||||
if req.OneTimeCommissionTrigger != "" {
|
|
||||||
allocation.OneTimeCommissionTrigger = req.OneTimeCommissionTrigger
|
|
||||||
}
|
|
||||||
if req.OneTimeCommissionThreshold != nil {
|
|
||||||
allocation.OneTimeCommissionThreshold = *req.OneTimeCommissionThreshold
|
|
||||||
}
|
|
||||||
if req.EnableForceRecharge != nil {
|
|
||||||
allocation.EnableForceRecharge = *req.EnableForceRecharge
|
|
||||||
}
|
|
||||||
if req.ForceRechargeAmount != nil {
|
|
||||||
allocation.ForceRechargeAmount = *req.ForceRechargeAmount
|
|
||||||
}
|
|
||||||
if req.ForceRechargeTriggerType != nil {
|
|
||||||
allocation.ForceRechargeTriggerType = *req.ForceRechargeTriggerType
|
|
||||||
}
|
|
||||||
|
|
||||||
allocation.Creator = currentUserID
|
|
||||||
|
|
||||||
if err := s.seriesAllocationStore.Create(ctx, allocation); err != nil {
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "创建分配失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.buildResponse(ctx, allocation, targetShop.ShopName, series.SeriesName, series.SeriesCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) Get(ctx context.Context, id uint) (*dto.ShopSeriesAllocationResponse, error) {
|
|
||||||
allocation, err := s.seriesAllocationStore.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeNotFound, "分配记录不存在")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
shop, _ := s.shopStore.GetByID(ctx, allocation.ShopID)
|
|
||||||
series, _ := s.packageSeriesStore.GetByID(ctx, allocation.SeriesID)
|
|
||||||
|
|
||||||
shopName := ""
|
|
||||||
seriesName := ""
|
|
||||||
seriesCode := ""
|
|
||||||
if shop != nil {
|
|
||||||
shopName = shop.ShopName
|
|
||||||
}
|
|
||||||
if series != nil {
|
|
||||||
seriesName = series.SeriesName
|
|
||||||
seriesCode = series.SeriesCode
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.buildResponse(ctx, allocation, shopName, seriesName, seriesCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateShopSeriesAllocationRequest) (*dto.ShopSeriesAllocationResponse, error) {
|
|
||||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
|
||||||
if currentUserID == 0 {
|
|
||||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
|
||||||
}
|
|
||||||
|
|
||||||
userType := middleware.GetUserTypeFromContext(ctx)
|
|
||||||
allocatorShopID := middleware.GetShopIDFromContext(ctx)
|
|
||||||
|
|
||||||
allocation, err := s.seriesAllocationStore.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, errors.New(errors.CodeNotFound, "分配记录不存在")
|
|
||||||
}
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.OneTimeCommissionAmount != nil {
|
|
||||||
newAmount := *req.OneTimeCommissionAmount
|
|
||||||
if userType == constants.UserTypeAgent {
|
|
||||||
allocatorAllocation, err := s.seriesAllocationStore.GetByShopAndSeries(ctx, allocatorShopID, allocation.SeriesID)
|
|
||||||
if err == nil && allocatorAllocation != nil {
|
|
||||||
if newAmount > allocatorAllocation.OneTimeCommissionAmount {
|
|
||||||
return nil, errors.New(errors.CodeInvalidParam, "一次性佣金金额不能超过您的分配上限")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocation.OneTimeCommissionAmount = newAmount
|
|
||||||
}
|
|
||||||
if req.EnableOneTimeCommission != nil {
|
|
||||||
allocation.EnableOneTimeCommission = *req.EnableOneTimeCommission
|
|
||||||
}
|
|
||||||
if req.OneTimeCommissionTrigger != nil {
|
|
||||||
allocation.OneTimeCommissionTrigger = *req.OneTimeCommissionTrigger
|
|
||||||
}
|
|
||||||
if req.OneTimeCommissionThreshold != nil {
|
|
||||||
allocation.OneTimeCommissionThreshold = *req.OneTimeCommissionThreshold
|
|
||||||
}
|
|
||||||
if req.EnableForceRecharge != nil {
|
|
||||||
allocation.EnableForceRecharge = *req.EnableForceRecharge
|
|
||||||
}
|
|
||||||
if req.ForceRechargeAmount != nil {
|
|
||||||
allocation.ForceRechargeAmount = *req.ForceRechargeAmount
|
|
||||||
}
|
|
||||||
if req.ForceRechargeTriggerType != nil {
|
|
||||||
allocation.ForceRechargeTriggerType = *req.ForceRechargeTriggerType
|
|
||||||
}
|
|
||||||
if req.Status != nil {
|
|
||||||
allocation.Status = *req.Status
|
|
||||||
}
|
|
||||||
|
|
||||||
allocation.Updater = currentUserID
|
|
||||||
|
|
||||||
if err := s.seriesAllocationStore.Update(ctx, allocation); err != nil {
|
|
||||||
return nil, errors.Wrap(errors.CodeInternalError, err, "更新分配失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
shop, _ := s.shopStore.GetByID(ctx, allocation.ShopID)
|
|
||||||
series, _ := s.packageSeriesStore.GetByID(ctx, allocation.SeriesID)
|
|
||||||
|
|
||||||
shopName := ""
|
|
||||||
seriesName := ""
|
|
||||||
seriesCode := ""
|
|
||||||
if shop != nil {
|
|
||||||
shopName = shop.ShopName
|
|
||||||
}
|
|
||||||
if series != nil {
|
|
||||||
seriesName = series.SeriesName
|
|
||||||
seriesCode = series.SeriesCode
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.buildResponse(ctx, allocation, shopName, seriesName, seriesCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) Delete(ctx context.Context, id uint) error {
|
|
||||||
_, err := s.seriesAllocationStore.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return errors.New(errors.CodeNotFound, "分配记录不存在")
|
|
||||||
}
|
|
||||||
return errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
count, err := s.packageAllocationStore.CountBySeriesAllocationID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(errors.CodeInternalError, err, "检查关联套餐分配失败")
|
|
||||||
}
|
|
||||||
if count > 0 {
|
|
||||||
return errors.New(errors.CodeInvalidParam, "存在关联的套餐分配,无法删除")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.seriesAllocationStore.Delete(ctx, id); err != nil {
|
|
||||||
return errors.Wrap(errors.CodeInternalError, err, "删除分配失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) List(ctx context.Context, req *dto.ShopSeriesAllocationListRequest) ([]*dto.ShopSeriesAllocationResponse, int64, error) {
|
|
||||||
opts := &store.QueryOptions{
|
|
||||||
Page: req.Page,
|
|
||||||
PageSize: req.PageSize,
|
|
||||||
OrderBy: "id DESC",
|
|
||||||
}
|
|
||||||
if opts.Page == 0 {
|
|
||||||
opts.Page = 1
|
|
||||||
}
|
|
||||||
if opts.PageSize == 0 {
|
|
||||||
opts.PageSize = constants.DefaultPageSize
|
|
||||||
}
|
|
||||||
|
|
||||||
filters := make(map[string]interface{})
|
|
||||||
if req.ShopID != nil {
|
|
||||||
filters["shop_id"] = *req.ShopID
|
|
||||||
}
|
|
||||||
if req.SeriesID != nil {
|
|
||||||
filters["series_id"] = *req.SeriesID
|
|
||||||
}
|
|
||||||
if req.AllocatorShopID != nil {
|
|
||||||
filters["allocator_shop_id"] = *req.AllocatorShopID
|
|
||||||
}
|
|
||||||
if req.Status != nil {
|
|
||||||
filters["status"] = *req.Status
|
|
||||||
}
|
|
||||||
|
|
||||||
allocations, total, err := s.seriesAllocationStore.List(ctx, opts, filters)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, errors.Wrap(errors.CodeInternalError, err, "查询分配列表失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
responses := make([]*dto.ShopSeriesAllocationResponse, len(allocations))
|
|
||||||
for i, a := range allocations {
|
|
||||||
shop, _ := s.shopStore.GetByID(ctx, a.ShopID)
|
|
||||||
series, _ := s.packageSeriesStore.GetByID(ctx, a.SeriesID)
|
|
||||||
|
|
||||||
shopName := ""
|
|
||||||
seriesName := ""
|
|
||||||
seriesCode := ""
|
|
||||||
if shop != nil {
|
|
||||||
shopName = shop.ShopName
|
|
||||||
}
|
|
||||||
if series != nil {
|
|
||||||
seriesName = series.SeriesName
|
|
||||||
seriesCode = series.SeriesCode
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, _ := s.buildResponse(ctx, a, shopName, seriesName, seriesCode)
|
|
||||||
responses[i] = resp
|
|
||||||
}
|
|
||||||
|
|
||||||
return responses, total, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) buildResponse(ctx context.Context, a *model.ShopSeriesAllocation, shopName, seriesName, seriesCode string) (*dto.ShopSeriesAllocationResponse, error) {
|
|
||||||
allocatorShopName := ""
|
|
||||||
if a.AllocatorShopID > 0 {
|
|
||||||
allocatorShop, _ := s.shopStore.GetByID(ctx, a.AllocatorShopID)
|
|
||||||
if allocatorShop != nil {
|
|
||||||
allocatorShopName = allocatorShop.ShopName
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
allocatorShopName = "平台"
|
|
||||||
}
|
|
||||||
|
|
||||||
return &dto.ShopSeriesAllocationResponse{
|
|
||||||
ID: a.ID,
|
|
||||||
ShopID: a.ShopID,
|
|
||||||
ShopName: shopName,
|
|
||||||
SeriesID: a.SeriesID,
|
|
||||||
SeriesName: seriesName,
|
|
||||||
SeriesCode: seriesCode,
|
|
||||||
AllocatorShopID: a.AllocatorShopID,
|
|
||||||
AllocatorShopName: allocatorShopName,
|
|
||||||
OneTimeCommissionAmount: a.OneTimeCommissionAmount,
|
|
||||||
EnableOneTimeCommission: a.EnableOneTimeCommission,
|
|
||||||
OneTimeCommissionTrigger: a.OneTimeCommissionTrigger,
|
|
||||||
OneTimeCommissionThreshold: a.OneTimeCommissionThreshold,
|
|
||||||
EnableForceRecharge: a.EnableForceRecharge,
|
|
||||||
ForceRechargeAmount: a.ForceRechargeAmount,
|
|
||||||
ForceRechargeTriggerType: a.ForceRechargeTriggerType,
|
|
||||||
Status: a.Status,
|
|
||||||
CreatedAt: a.CreatedAt.Format(time.RFC3339),
|
|
||||||
UpdatedAt: a.UpdatedAt.Format(time.RFC3339),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetByShopAndSeries(ctx context.Context, shopID, seriesID uint) (*model.ShopSeriesAllocation, error) {
|
|
||||||
return s.seriesAllocationStore.GetByShopAndSeries(ctx, shopID, seriesID)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user