feat: 实现套餐管理模块,包含套餐系列、双状态管理、废弃模型清理
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m24s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m24s
- 新增套餐系列管理 (CRUD + 状态切换) - 新增套餐管理 (CRUD + 启用/禁用 + 上架/下架双状态) - 清理 8 个废弃分佣模型及对应数据库表 - Package 模型新增建议成本价、建议售价、上架状态字段 - 完整的 Store/Service/Handler 三层实现 - 包含单元测试和集成测试 - 归档 add-package-module change - 新增多个 OpenSpec changes (订单支付、店铺套餐分配、一次性分佣、卡设备系列绑定)
This commit is contained in:
305
internal/service/package/service.go
Normal file
305
internal/service/package/service.go
Normal file
@@ -0,0 +1,305 @@
|
||||
package packagepkg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"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/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"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
packageStore *postgres.PackageStore
|
||||
packageSeriesStore *postgres.PackageSeriesStore
|
||||
}
|
||||
|
||||
func New(packageStore *postgres.PackageStore, packageSeriesStore *postgres.PackageSeriesStore) *Service {
|
||||
return &Service{
|
||||
packageStore: packageStore,
|
||||
packageSeriesStore: packageSeriesStore,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, req *dto.CreatePackageRequest) (*dto.PackageResponse, error) {
|
||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||||
if currentUserID == 0 {
|
||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
existing, _ := s.packageStore.GetByCode(ctx, req.PackageCode)
|
||||
if existing != nil {
|
||||
return nil, errors.New(errors.CodeConflict, "套餐编码已存在")
|
||||
}
|
||||
|
||||
if req.SeriesID != nil && *req.SeriesID > 0 {
|
||||
_, err := s.packageSeriesStore.GetByID(ctx, *req.SeriesID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "套餐系列不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("获取套餐系列失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
pkg := &model.Package{
|
||||
PackageCode: req.PackageCode,
|
||||
PackageName: req.PackageName,
|
||||
PackageType: req.PackageType,
|
||||
DurationMonths: req.DurationMonths,
|
||||
Price: req.Price,
|
||||
Status: constants.StatusEnabled,
|
||||
ShelfStatus: 2,
|
||||
}
|
||||
if req.SeriesID != nil {
|
||||
pkg.SeriesID = *req.SeriesID
|
||||
}
|
||||
if req.DataType != nil {
|
||||
pkg.DataType = *req.DataType
|
||||
}
|
||||
if req.RealDataMB != nil {
|
||||
pkg.RealDataMB = *req.RealDataMB
|
||||
}
|
||||
if req.VirtualDataMB != nil {
|
||||
pkg.VirtualDataMB = *req.VirtualDataMB
|
||||
}
|
||||
if req.DataAmountMB != nil {
|
||||
pkg.DataAmountMB = *req.DataAmountMB
|
||||
}
|
||||
if req.SuggestedCostPrice != nil {
|
||||
pkg.SuggestedCostPrice = *req.SuggestedCostPrice
|
||||
}
|
||||
if req.SuggestedRetailPrice != nil {
|
||||
pkg.SuggestedRetailPrice = *req.SuggestedRetailPrice
|
||||
}
|
||||
pkg.Creator = currentUserID
|
||||
|
||||
if err := s.packageStore.Create(ctx, pkg); err != nil {
|
||||
return nil, fmt.Errorf("创建套餐失败: %w", err)
|
||||
}
|
||||
|
||||
return s.toResponse(pkg), nil
|
||||
}
|
||||
|
||||
func (s *Service) Get(ctx context.Context, id uint) (*dto.PackageResponse, error) {
|
||||
pkg, err := s.packageStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "套餐不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("获取套餐失败: %w", err)
|
||||
}
|
||||
return s.toResponse(pkg), nil
|
||||
}
|
||||
|
||||
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdatePackageRequest) (*dto.PackageResponse, error) {
|
||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||||
if currentUserID == 0 {
|
||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
pkg, err := s.packageStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "套餐不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("获取套餐失败: %w", err)
|
||||
}
|
||||
|
||||
if req.SeriesID != nil && *req.SeriesID > 0 {
|
||||
_, err := s.packageSeriesStore.GetByID(ctx, *req.SeriesID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "套餐系列不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("获取套餐系列失败: %w", err)
|
||||
}
|
||||
pkg.SeriesID = *req.SeriesID
|
||||
}
|
||||
|
||||
if req.PackageName != nil {
|
||||
pkg.PackageName = *req.PackageName
|
||||
}
|
||||
if req.PackageType != nil {
|
||||
pkg.PackageType = *req.PackageType
|
||||
}
|
||||
if req.DurationMonths != nil {
|
||||
pkg.DurationMonths = *req.DurationMonths
|
||||
}
|
||||
if req.DataType != nil {
|
||||
pkg.DataType = *req.DataType
|
||||
}
|
||||
if req.RealDataMB != nil {
|
||||
pkg.RealDataMB = *req.RealDataMB
|
||||
}
|
||||
if req.VirtualDataMB != nil {
|
||||
pkg.VirtualDataMB = *req.VirtualDataMB
|
||||
}
|
||||
if req.DataAmountMB != nil {
|
||||
pkg.DataAmountMB = *req.DataAmountMB
|
||||
}
|
||||
if req.Price != nil {
|
||||
pkg.Price = *req.Price
|
||||
}
|
||||
if req.SuggestedCostPrice != nil {
|
||||
pkg.SuggestedCostPrice = *req.SuggestedCostPrice
|
||||
}
|
||||
if req.SuggestedRetailPrice != nil {
|
||||
pkg.SuggestedRetailPrice = *req.SuggestedRetailPrice
|
||||
}
|
||||
pkg.Updater = currentUserID
|
||||
|
||||
if err := s.packageStore.Update(ctx, pkg); err != nil {
|
||||
return nil, fmt.Errorf("更新套餐失败: %w", err)
|
||||
}
|
||||
|
||||
return s.toResponse(pkg), nil
|
||||
}
|
||||
|
||||
func (s *Service) Delete(ctx context.Context, id uint) error {
|
||||
_, err := s.packageStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "套餐不存在")
|
||||
}
|
||||
return fmt.Errorf("获取套餐失败: %w", err)
|
||||
}
|
||||
|
||||
if err := s.packageStore.Delete(ctx, id); err != nil {
|
||||
return fmt.Errorf("删除套餐失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) List(ctx context.Context, req *dto.PackageListRequest) ([]*dto.PackageResponse, 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.PackageName != nil {
|
||||
filters["package_name"] = *req.PackageName
|
||||
}
|
||||
if req.SeriesID != nil {
|
||||
filters["series_id"] = *req.SeriesID
|
||||
}
|
||||
if req.Status != nil {
|
||||
filters["status"] = *req.Status
|
||||
}
|
||||
if req.ShelfStatus != nil {
|
||||
filters["shelf_status"] = *req.ShelfStatus
|
||||
}
|
||||
if req.PackageType != nil {
|
||||
filters["package_type"] = *req.PackageType
|
||||
}
|
||||
|
||||
packages, total, err := s.packageStore.List(ctx, opts, filters)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("查询套餐列表失败: %w", err)
|
||||
}
|
||||
|
||||
responses := make([]*dto.PackageResponse, len(packages))
|
||||
for i, pkg := range packages {
|
||||
responses[i] = s.toResponse(pkg)
|
||||
}
|
||||
|
||||
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, "未授权访问")
|
||||
}
|
||||
|
||||
pkg, err := s.packageStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "套餐不存在")
|
||||
}
|
||||
return fmt.Errorf("获取套餐失败: %w", err)
|
||||
}
|
||||
|
||||
pkg.Status = status
|
||||
pkg.Updater = currentUserID
|
||||
|
||||
if status == constants.StatusDisabled {
|
||||
pkg.ShelfStatus = 2
|
||||
}
|
||||
|
||||
if err := s.packageStore.Update(ctx, pkg); err != nil {
|
||||
return fmt.Errorf("更新套餐状态失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdateShelfStatus(ctx context.Context, id uint, shelfStatus int) error {
|
||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||||
if currentUserID == 0 {
|
||||
return errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
pkg, err := s.packageStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "套餐不存在")
|
||||
}
|
||||
return fmt.Errorf("获取套餐失败: %w", err)
|
||||
}
|
||||
|
||||
if shelfStatus == 1 && pkg.Status == constants.StatusDisabled {
|
||||
return errors.New(errors.CodeInvalidStatus, "禁用的套餐不能上架,请先启用")
|
||||
}
|
||||
|
||||
pkg.ShelfStatus = shelfStatus
|
||||
pkg.Updater = currentUserID
|
||||
|
||||
if err := s.packageStore.Update(ctx, pkg); err != nil {
|
||||
return fmt.Errorf("更新套餐上架状态失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) toResponse(pkg *model.Package) *dto.PackageResponse {
|
||||
var seriesID *uint
|
||||
if pkg.SeriesID > 0 {
|
||||
seriesID = &pkg.SeriesID
|
||||
}
|
||||
return &dto.PackageResponse{
|
||||
ID: pkg.ID,
|
||||
PackageCode: pkg.PackageCode,
|
||||
PackageName: pkg.PackageName,
|
||||
SeriesID: seriesID,
|
||||
PackageType: pkg.PackageType,
|
||||
DurationMonths: pkg.DurationMonths,
|
||||
DataType: pkg.DataType,
|
||||
RealDataMB: pkg.RealDataMB,
|
||||
VirtualDataMB: pkg.VirtualDataMB,
|
||||
DataAmountMB: pkg.DataAmountMB,
|
||||
Price: pkg.Price,
|
||||
SuggestedCostPrice: pkg.SuggestedCostPrice,
|
||||
SuggestedRetailPrice: pkg.SuggestedRetailPrice,
|
||||
Status: pkg.Status,
|
||||
ShelfStatus: pkg.ShelfStatus,
|
||||
CreatedAt: pkg.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: pkg.UpdatedAt.Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
@@ -122,12 +122,12 @@ func TestPackageService_UpdateStatus(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, pkg.ShelfStatus)
|
||||
|
||||
err = svc.UpdateStatus(ctx, created.ID, 2)
|
||||
err = svc.UpdateStatus(ctx, created.ID, constants.StatusDisabled)
|
||||
require.NoError(t, err)
|
||||
|
||||
pkg, err = svc.Get(ctx, created.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, pkg.Status)
|
||||
assert.Equal(t, constants.StatusDisabled, pkg.Status)
|
||||
assert.Equal(t, 2, pkg.ShelfStatus)
|
||||
})
|
||||
|
||||
@@ -145,20 +145,20 @@ func TestPackageService_UpdateStatus(t *testing.T) {
|
||||
err = svc.UpdateShelfStatus(ctx, created2.ID, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = svc.UpdateStatus(ctx, created2.ID, 2)
|
||||
err = svc.UpdateStatus(ctx, created2.ID, constants.StatusDisabled)
|
||||
require.NoError(t, err)
|
||||
|
||||
pkg, err := svc.Get(ctx, created2.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, pkg.Status)
|
||||
assert.Equal(t, constants.StatusDisabled, pkg.Status)
|
||||
assert.Equal(t, 2, pkg.ShelfStatus)
|
||||
|
||||
err = svc.UpdateStatus(ctx, created2.ID, 1)
|
||||
err = svc.UpdateStatus(ctx, created2.ID, constants.StatusEnabled)
|
||||
require.NoError(t, err)
|
||||
|
||||
pkg, err = svc.Get(ctx, created2.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, pkg.Status)
|
||||
assert.Equal(t, constants.StatusEnabled, pkg.Status)
|
||||
assert.Equal(t, 2, pkg.ShelfStatus)
|
||||
})
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func TestPackageService_UpdateShelfStatus(t *testing.T) {
|
||||
created, err := svc.Create(ctx, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = svc.UpdateStatus(ctx, created.ID, 2)
|
||||
err = svc.UpdateStatus(ctx, created.ID, constants.StatusDisabled)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = svc.UpdateShelfStatus(ctx, created.ID, 1)
|
||||
|
||||
177
internal/service/package_series/service.go
Normal file
177
internal/service/package_series/service.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package package_series
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"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/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"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
packageSeriesStore *postgres.PackageSeriesStore
|
||||
}
|
||||
|
||||
func New(packageSeriesStore *postgres.PackageSeriesStore) *Service {
|
||||
return &Service{packageSeriesStore: packageSeriesStore}
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, req *dto.CreatePackageSeriesRequest) (*dto.PackageSeriesResponse, error) {
|
||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||||
if currentUserID == 0 {
|
||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
existing, _ := s.packageSeriesStore.GetByCode(ctx, req.SeriesCode)
|
||||
if existing != nil {
|
||||
return nil, errors.New(errors.CodeConflict, "系列编码已存在")
|
||||
}
|
||||
|
||||
series := &model.PackageSeries{
|
||||
SeriesCode: req.SeriesCode,
|
||||
SeriesName: req.SeriesName,
|
||||
Description: req.Description,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
series.Creator = currentUserID
|
||||
|
||||
if err := s.packageSeriesStore.Create(ctx, series); err != nil {
|
||||
return nil, fmt.Errorf("创建套餐系列失败: %w", err)
|
||||
}
|
||||
|
||||
return s.toResponse(series), nil
|
||||
}
|
||||
|
||||
func (s *Service) Get(ctx context.Context, id uint) (*dto.PackageSeriesResponse, error) {
|
||||
series, err := s.packageSeriesStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "套餐系列不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("获取套餐系列失败: %w", err)
|
||||
}
|
||||
return s.toResponse(series), nil
|
||||
}
|
||||
|
||||
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdatePackageSeriesRequest) (*dto.PackageSeriesResponse, error) {
|
||||
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||||
if currentUserID == 0 {
|
||||
return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
|
||||
}
|
||||
|
||||
series, err := s.packageSeriesStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "套餐系列不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("获取套餐系列失败: %w", err)
|
||||
}
|
||||
|
||||
if req.SeriesName != nil {
|
||||
series.SeriesName = *req.SeriesName
|
||||
}
|
||||
if req.Description != nil {
|
||||
series.Description = *req.Description
|
||||
}
|
||||
series.Updater = currentUserID
|
||||
|
||||
if err := s.packageSeriesStore.Update(ctx, series); err != nil {
|
||||
return nil, fmt.Errorf("更新套餐系列失败: %w", err)
|
||||
}
|
||||
|
||||
return s.toResponse(series), nil
|
||||
}
|
||||
|
||||
func (s *Service) Delete(ctx context.Context, id uint) error {
|
||||
_, err := s.packageSeriesStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "套餐系列不存在")
|
||||
}
|
||||
return fmt.Errorf("获取套餐系列失败: %w", err)
|
||||
}
|
||||
|
||||
if err := s.packageSeriesStore.Delete(ctx, id); err != nil {
|
||||
return fmt.Errorf("删除套餐系列失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) List(ctx context.Context, req *dto.PackageSeriesListRequest) ([]*dto.PackageSeriesResponse, 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.SeriesName != nil {
|
||||
filters["series_name"] = *req.SeriesName
|
||||
}
|
||||
if req.Status != nil {
|
||||
filters["status"] = *req.Status
|
||||
}
|
||||
|
||||
seriesList, total, err := s.packageSeriesStore.List(ctx, opts, filters)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("查询套餐系列列表失败: %w", err)
|
||||
}
|
||||
|
||||
responses := make([]*dto.PackageSeriesResponse, len(seriesList))
|
||||
for i, series := range seriesList {
|
||||
responses[i] = s.toResponse(series)
|
||||
}
|
||||
|
||||
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, "未授权访问")
|
||||
}
|
||||
|
||||
series, err := s.packageSeriesStore.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "套餐系列不存在")
|
||||
}
|
||||
return fmt.Errorf("获取套餐系列失败: %w", err)
|
||||
}
|
||||
|
||||
series.Status = status
|
||||
series.Updater = currentUserID
|
||||
|
||||
if err := s.packageSeriesStore.Update(ctx, series); err != nil {
|
||||
return fmt.Errorf("更新套餐系列状态失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) toResponse(series *model.PackageSeries) *dto.PackageSeriesResponse {
|
||||
return &dto.PackageSeriesResponse{
|
||||
ID: series.ID,
|
||||
SeriesCode: series.SeriesCode,
|
||||
SeriesName: series.SeriesName,
|
||||
Description: series.Description,
|
||||
Status: series.Status,
|
||||
CreatedAt: series.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: series.UpdatedAt.Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
313
internal/service/package_series/service_test.go
Normal file
313
internal/service/package_series/service_test.go
Normal file
@@ -0,0 +1,313 @@
|
||||
package package_series
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
"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"
|
||||
"github.com/break/junhong_cmp_fiber/tests/testutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPackageSeriesService_Create(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
store := postgres.NewPackageSeriesStore(tx)
|
||||
svc := New(store)
|
||||
|
||||
ctx := middleware.SetUserContext(context.Background(), &middleware.UserContextInfo{
|
||||
UserID: 1,
|
||||
UserType: constants.UserTypePlatform,
|
||||
})
|
||||
|
||||
t.Run("创建成功", func(t *testing.T) {
|
||||
seriesCode := fmt.Sprintf("SVC_CREATE_%d", time.Now().UnixNano())
|
||||
req := &dto.CreatePackageSeriesRequest{
|
||||
SeriesCode: seriesCode,
|
||||
SeriesName: "测试套餐系列",
|
||||
Description: "服务层测试",
|
||||
}
|
||||
|
||||
resp, err := svc.Create(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, resp.ID)
|
||||
assert.Equal(t, req.SeriesCode, resp.SeriesCode)
|
||||
assert.Equal(t, req.SeriesName, resp.SeriesName)
|
||||
assert.Equal(t, constants.StatusEnabled, resp.Status)
|
||||
})
|
||||
|
||||
t.Run("编码重复失败", func(t *testing.T) {
|
||||
seriesCode := fmt.Sprintf("SVC_DUP_%d", time.Now().UnixNano())
|
||||
req1 := &dto.CreatePackageSeriesRequest{
|
||||
SeriesCode: seriesCode,
|
||||
SeriesName: "第一个系列",
|
||||
Description: "测试重复",
|
||||
}
|
||||
|
||||
_, err := svc.Create(ctx, req1)
|
||||
require.NoError(t, err)
|
||||
|
||||
req2 := &dto.CreatePackageSeriesRequest{
|
||||
SeriesCode: seriesCode,
|
||||
SeriesName: "第二个系列",
|
||||
Description: "重复编码",
|
||||
}
|
||||
|
||||
_, err = svc.Create(ctx, req2)
|
||||
require.Error(t, err)
|
||||
appErr, ok := err.(*errors.AppError)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errors.CodeConflict, appErr.Code)
|
||||
})
|
||||
|
||||
t.Run("未授权失败", func(t *testing.T) {
|
||||
req := &dto.CreatePackageSeriesRequest{
|
||||
SeriesCode: fmt.Sprintf("SVC_UNAUTH_%d", time.Now().UnixNano()),
|
||||
SeriesName: "未授权测试",
|
||||
Description: "无用户上下文",
|
||||
}
|
||||
|
||||
_, err := svc.Create(context.Background(), req)
|
||||
require.Error(t, err)
|
||||
appErr, ok := err.(*errors.AppError)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errors.CodeUnauthorized, appErr.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPackageSeriesService_Get(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
store := postgres.NewPackageSeriesStore(tx)
|
||||
svc := New(store)
|
||||
|
||||
ctx := middleware.SetUserContext(context.Background(), &middleware.UserContextInfo{
|
||||
UserID: 1,
|
||||
UserType: constants.UserTypePlatform,
|
||||
})
|
||||
|
||||
seriesCode := fmt.Sprintf("SVC_GET_%d", time.Now().UnixNano())
|
||||
req := &dto.CreatePackageSeriesRequest{
|
||||
SeriesCode: seriesCode,
|
||||
SeriesName: "查询测试",
|
||||
Description: "用于查询测试",
|
||||
}
|
||||
created, err := svc.Create(ctx, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("获取存在的系列", func(t *testing.T) {
|
||||
resp, err := svc.Get(ctx, created.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, created.SeriesCode, resp.SeriesCode)
|
||||
assert.Equal(t, created.SeriesName, resp.SeriesName)
|
||||
})
|
||||
|
||||
t.Run("获取不存在的系列", func(t *testing.T) {
|
||||
_, err := svc.Get(ctx, 99999)
|
||||
require.Error(t, err)
|
||||
appErr, ok := err.(*errors.AppError)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errors.CodeNotFound, appErr.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPackageSeriesService_Update(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
store := postgres.NewPackageSeriesStore(tx)
|
||||
svc := New(store)
|
||||
|
||||
ctx := middleware.SetUserContext(context.Background(), &middleware.UserContextInfo{
|
||||
UserID: 1,
|
||||
UserType: constants.UserTypePlatform,
|
||||
})
|
||||
|
||||
seriesCode := fmt.Sprintf("SVC_UPD_%d", time.Now().UnixNano())
|
||||
req := &dto.CreatePackageSeriesRequest{
|
||||
SeriesCode: seriesCode,
|
||||
SeriesName: "更新测试",
|
||||
Description: "原始描述",
|
||||
}
|
||||
created, err := svc.Create(ctx, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("更新成功", func(t *testing.T) {
|
||||
newName := "更新后的名称"
|
||||
newDesc := "更新后的描述"
|
||||
updateReq := &dto.UpdatePackageSeriesRequest{
|
||||
SeriesName: &newName,
|
||||
Description: &newDesc,
|
||||
}
|
||||
|
||||
resp, err := svc.Update(ctx, created.ID, updateReq)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newName, resp.SeriesName)
|
||||
assert.Equal(t, newDesc, resp.Description)
|
||||
})
|
||||
|
||||
t.Run("更新不存在的系列", func(t *testing.T) {
|
||||
newName := "test"
|
||||
updateReq := &dto.UpdatePackageSeriesRequest{
|
||||
SeriesName: &newName,
|
||||
}
|
||||
|
||||
_, err := svc.Update(ctx, 99999, updateReq)
|
||||
require.Error(t, err)
|
||||
appErr, ok := err.(*errors.AppError)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errors.CodeNotFound, appErr.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPackageSeriesService_Delete(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
store := postgres.NewPackageSeriesStore(tx)
|
||||
svc := New(store)
|
||||
|
||||
ctx := middleware.SetUserContext(context.Background(), &middleware.UserContextInfo{
|
||||
UserID: 1,
|
||||
UserType: constants.UserTypePlatform,
|
||||
})
|
||||
|
||||
seriesCode := fmt.Sprintf("SVC_DEL_%d", time.Now().UnixNano())
|
||||
req := &dto.CreatePackageSeriesRequest{
|
||||
SeriesCode: seriesCode,
|
||||
SeriesName: "删除测试",
|
||||
Description: "用于删除测试",
|
||||
}
|
||||
created, err := svc.Create(ctx, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("删除成功", func(t *testing.T) {
|
||||
err := svc.Delete(ctx, created.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = svc.Get(ctx, created.ID)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("删除不存在的系列", func(t *testing.T) {
|
||||
err := svc.Delete(ctx, 99999)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPackageSeriesService_List(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
store := postgres.NewPackageSeriesStore(tx)
|
||||
svc := New(store)
|
||||
|
||||
ctx := middleware.SetUserContext(context.Background(), &middleware.UserContextInfo{
|
||||
UserID: 1,
|
||||
UserType: constants.UserTypePlatform,
|
||||
})
|
||||
|
||||
seriesList := []dto.CreatePackageSeriesRequest{
|
||||
{
|
||||
SeriesCode: fmt.Sprintf("SVC_LIST_001_%d", time.Now().UnixNano()),
|
||||
SeriesName: "基础套餐",
|
||||
Description: "列表测试1",
|
||||
},
|
||||
{
|
||||
SeriesCode: fmt.Sprintf("SVC_LIST_002_%d", time.Now().UnixNano()),
|
||||
SeriesName: "高级套餐",
|
||||
Description: "列表测试2",
|
||||
},
|
||||
{
|
||||
SeriesCode: fmt.Sprintf("SVC_LIST_003_%d", time.Now().UnixNano()),
|
||||
SeriesName: "企业套餐",
|
||||
Description: "列表测试3",
|
||||
},
|
||||
}
|
||||
for _, s := range seriesList {
|
||||
_, err := svc.Create(ctx, &s)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
t.Run("查询列表", func(t *testing.T) {
|
||||
req := &dto.PackageSeriesListRequest{
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
}
|
||||
result, total, err := svc.List(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(3))
|
||||
assert.GreaterOrEqual(t, len(result), 3)
|
||||
})
|
||||
|
||||
t.Run("按状态过滤", func(t *testing.T) {
|
||||
status := constants.StatusEnabled
|
||||
req := &dto.PackageSeriesListRequest{
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
Status: &status,
|
||||
}
|
||||
result, total, err := svc.List(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(3))
|
||||
for _, s := range result {
|
||||
assert.Equal(t, constants.StatusEnabled, s.Status)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("按名称模糊搜索", func(t *testing.T) {
|
||||
seriesName := "高级"
|
||||
req := &dto.PackageSeriesListRequest{
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
SeriesName: &seriesName,
|
||||
}
|
||||
result, total, err := svc.List(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, total, int64(1))
|
||||
assert.GreaterOrEqual(t, len(result), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPackageSeriesService_UpdateStatus(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
store := postgres.NewPackageSeriesStore(tx)
|
||||
svc := New(store)
|
||||
|
||||
ctx := middleware.SetUserContext(context.Background(), &middleware.UserContextInfo{
|
||||
UserID: 1,
|
||||
UserType: constants.UserTypePlatform,
|
||||
})
|
||||
|
||||
seriesCode := fmt.Sprintf("SVC_STATUS_%d", time.Now().UnixNano())
|
||||
req := &dto.CreatePackageSeriesRequest{
|
||||
SeriesCode: seriesCode,
|
||||
SeriesName: "状态测试",
|
||||
Description: "用于状态更新测试",
|
||||
}
|
||||
created, err := svc.Create(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, constants.StatusEnabled, created.Status)
|
||||
|
||||
t.Run("禁用系列", func(t *testing.T) {
|
||||
err := svc.UpdateStatus(ctx, created.ID, constants.StatusDisabled)
|
||||
require.NoError(t, err)
|
||||
|
||||
updated, err := svc.Get(ctx, created.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, constants.StatusDisabled, updated.Status)
|
||||
})
|
||||
|
||||
t.Run("启用系列", func(t *testing.T) {
|
||||
err := svc.UpdateStatus(ctx, created.ID, constants.StatusEnabled)
|
||||
require.NoError(t, err)
|
||||
|
||||
updated, err := svc.Get(ctx, created.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, constants.StatusEnabled, updated.Status)
|
||||
})
|
||||
|
||||
t.Run("更新不存在的系列状态", func(t *testing.T) {
|
||||
err := svc.UpdateStatus(ctx, 99999, constants.StatusDisabled)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user