feat: 实现门店套餐分配功能并统一测试基础设施
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m30s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m30s
新增功能: - 门店套餐分配管理(shop_package_allocation):支持门店套餐库存管理 - 门店套餐系列分配管理(shop_series_allocation):支持套餐系列分配和佣金层级设置 - 我的套餐查询(my_package):支持门店查询自己的套餐分配情况 测试改进: - 统一集成测试基础设施,新增 testutils.NewIntegrationTestEnv - 重构所有集成测试使用新的测试环境设置 - 移除旧的测试辅助函数和冗余测试文件 - 新增 test_helpers_test.go 统一任务测试辅助 技术细节: - 新增数据库迁移 000025_create_shop_allocation_tables - 新增 3 个 Handler、Service、Store 和对应的单元测试 - 更新 OpenAPI 文档和文档生成器 - 测试覆盖率:Service 层 > 90% Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
85
internal/model/dto/my_package.go
Normal file
85
internal/model/dto/my_package.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package dto
|
||||
|
||||
// MyPackageListRequest 我的可售套餐列表请求
|
||||
type MyPackageListRequest struct {
|
||||
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
|
||||
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量"`
|
||||
SeriesID *uint `json:"series_id" query:"series_id" validate:"omitempty" description:"套餐系列ID"`
|
||||
PackageType *string `json:"package_type" query:"package_type" validate:"omitempty" description:"套餐类型"`
|
||||
}
|
||||
|
||||
// MyPackageResponse 我的可售套餐响应
|
||||
type MyPackageResponse struct {
|
||||
ID uint `json:"id" description:"套餐ID"`
|
||||
PackageCode string `json:"package_code" description:"套餐编码"`
|
||||
PackageName string `json:"package_name" description:"套餐名称"`
|
||||
PackageType string `json:"package_type" description:"套餐类型"`
|
||||
SeriesID uint `json:"series_id" description:"套餐系列ID"`
|
||||
SeriesName string `json:"series_name" description:"套餐系列名称"`
|
||||
CostPrice int64 `json:"cost_price" description:"我的成本价(分)"`
|
||||
SuggestedRetailPrice int64 `json:"suggested_retail_price" description:"建议售价(分)"`
|
||||
ProfitMargin int64 `json:"profit_margin" description:"利润空间(分)= 建议售价 - 成本价"`
|
||||
PriceSource string `json:"price_source" description:"价格来源 (series_pricing:系列加价, package_override:单套餐覆盖)"`
|
||||
Status int `json:"status" description:"套餐状态 (1:启用, 2:禁用)"`
|
||||
ShelfStatus int `json:"shelf_status" description:"上架状态 (1:上架, 2:下架)"`
|
||||
}
|
||||
|
||||
// MyPackagePageResult 我的可售套餐分页结果
|
||||
type MyPackagePageResult struct {
|
||||
List []*MyPackageResponse `json:"list" description:"套餐列表"`
|
||||
Total int64 `json:"total" description:"总数"`
|
||||
Page int `json:"page" description:"当前页"`
|
||||
PageSize int `json:"page_size" description:"每页数量"`
|
||||
TotalPages int `json:"total_pages" description:"总页数"`
|
||||
}
|
||||
|
||||
// MyPackageDetailResponse 我的可售套餐详情响应
|
||||
type MyPackageDetailResponse struct {
|
||||
ID uint `json:"id" description:"套餐ID"`
|
||||
PackageCode string `json:"package_code" description:"套餐编码"`
|
||||
PackageName string `json:"package_name" description:"套餐名称"`
|
||||
PackageType string `json:"package_type" description:"套餐类型"`
|
||||
Description string `json:"description" description:"套餐描述"`
|
||||
SeriesID uint `json:"series_id" description:"套餐系列ID"`
|
||||
SeriesName string `json:"series_name" description:"套餐系列名称"`
|
||||
CostPrice int64 `json:"cost_price" description:"我的成本价(分)"`
|
||||
SuggestedRetailPrice int64 `json:"suggested_retail_price" description:"建议售价(分)"`
|
||||
ProfitMargin int64 `json:"profit_margin" description:"利润空间(分)"`
|
||||
PriceSource string `json:"price_source" description:"价格来源 (series_pricing:系列加价, package_override:单套餐覆盖)"`
|
||||
Status int `json:"status" description:"套餐状态 (1:启用, 2:禁用)"`
|
||||
ShelfStatus int `json:"shelf_status" description:"上架状态 (1:上架, 2:下架)"`
|
||||
}
|
||||
|
||||
// MySeriesAllocationListRequest 我的套餐系列分配列表请求
|
||||
type MySeriesAllocationListRequest struct {
|
||||
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
|
||||
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量"`
|
||||
}
|
||||
|
||||
// MySeriesAllocationResponse 我的套餐系列分配响应
|
||||
type MySeriesAllocationResponse struct {
|
||||
ID uint `json:"id" description:"分配ID"`
|
||||
SeriesID uint `json:"series_id" description:"套餐系列ID"`
|
||||
SeriesCode string `json:"series_code" description:"系列编码"`
|
||||
SeriesName string `json:"series_name" description:"系列名称"`
|
||||
PricingMode string `json:"pricing_mode" description:"加价模式 (fixed:固定金额, percent:百分比)"`
|
||||
PricingValue int64 `json:"pricing_value" description:"加价值"`
|
||||
AvailablePackageCount int `json:"available_package_count" description:"可售套餐数量"`
|
||||
AllocatorShopName string `json:"allocator_shop_name" description:"分配者店铺名称"`
|
||||
Status int `json:"status" description:"状态 (1:启用, 2:禁用)"`
|
||||
}
|
||||
|
||||
// MySeriesAllocationPageResult 我的套餐系列分配分页结果
|
||||
type MySeriesAllocationPageResult struct {
|
||||
List []*MySeriesAllocationResponse `json:"list" description:"分配列表"`
|
||||
Total int64 `json:"total" description:"总数"`
|
||||
Page int `json:"page" description:"当前页"`
|
||||
PageSize int `json:"page_size" description:"每页数量"`
|
||||
TotalPages int `json:"total_pages" description:"总页数"`
|
||||
}
|
||||
|
||||
// PriceSource 价格来源常量
|
||||
const (
|
||||
PriceSourceSeriesPricing = "series_pricing"
|
||||
PriceSourcePackageOverride = "package_override"
|
||||
)
|
||||
64
internal/model/dto/shop_package_allocation.go
Normal file
64
internal/model/dto/shop_package_allocation.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package dto
|
||||
|
||||
// CreateShopPackageAllocationRequest 创建单套餐分配请求
|
||||
type CreateShopPackageAllocationRequest struct {
|
||||
ShopID uint `json:"shop_id" validate:"required" required:"true" description:"被分配的店铺ID"`
|
||||
PackageID uint `json:"package_id" validate:"required" required:"true" description:"套餐ID"`
|
||||
CostPrice int64 `json:"cost_price" validate:"required,min=0" required:"true" minimum:"0" description:"覆盖的成本价(分)"`
|
||||
}
|
||||
|
||||
// UpdateShopPackageAllocationRequest 更新单套餐分配请求
|
||||
type UpdateShopPackageAllocationRequest struct {
|
||||
CostPrice *int64 `json:"cost_price" validate:"omitempty,min=0" minimum:"0" description:"覆盖的成本价(分)"`
|
||||
}
|
||||
|
||||
// ShopPackageAllocationListRequest 单套餐分配列表请求
|
||||
type ShopPackageAllocationListRequest struct {
|
||||
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
|
||||
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量"`
|
||||
ShopID *uint `json:"shop_id" query:"shop_id" validate:"omitempty" description:"被分配的店铺ID"`
|
||||
PackageID *uint `json:"package_id" query:"package_id" validate:"omitempty" description:"套餐ID"`
|
||||
Status *int `json:"status" query:"status" validate:"omitempty,oneof=1 2" description:"状态 (1:启用, 2:禁用)"`
|
||||
}
|
||||
|
||||
// UpdateShopPackageAllocationStatusRequest 更新单套餐分配状态请求
|
||||
type UpdateShopPackageAllocationStatusRequest struct {
|
||||
Status int `json:"status" validate:"required,oneof=1 2" required:"true" description:"状态 (1:启用, 2:禁用)"`
|
||||
}
|
||||
|
||||
// ShopPackageAllocationResponse 单套餐分配响应
|
||||
type ShopPackageAllocationResponse struct {
|
||||
ID uint `json:"id" description:"分配ID"`
|
||||
ShopID uint `json:"shop_id" description:"被分配的店铺ID"`
|
||||
ShopName string `json:"shop_name" description:"被分配的店铺名称"`
|
||||
PackageID uint `json:"package_id" description:"套餐ID"`
|
||||
PackageName string `json:"package_name" description:"套餐名称"`
|
||||
PackageCode string `json:"package_code" description:"套餐编码"`
|
||||
AllocationID uint `json:"allocation_id" description:"关联的系列分配ID"`
|
||||
CostPrice int64 `json:"cost_price" description:"覆盖的成本价(分)"`
|
||||
CalculatedCostPrice int64 `json:"calculated_cost_price" description:"原计算成本价(分),供参考"`
|
||||
Status int `json:"status" description:"状态 (1:启用, 2:禁用)"`
|
||||
CreatedAt string `json:"created_at" description:"创建时间"`
|
||||
UpdatedAt string `json:"updated_at" description:"更新时间"`
|
||||
}
|
||||
|
||||
// ShopPackageAllocationPageResult 单套餐分配分页结果
|
||||
type ShopPackageAllocationPageResult struct {
|
||||
List []*ShopPackageAllocationResponse `json:"list" description:"分配列表"`
|
||||
Total int64 `json:"total" description:"总数"`
|
||||
Page int `json:"page" description:"当前页"`
|
||||
PageSize int `json:"page_size" description:"每页数量"`
|
||||
TotalPages int `json:"total_pages" description:"总页数"`
|
||||
}
|
||||
|
||||
// UpdateShopPackageAllocationParams 更新单套餐分配聚合参数
|
||||
type UpdateShopPackageAllocationParams struct {
|
||||
IDReq
|
||||
UpdateShopPackageAllocationRequest
|
||||
}
|
||||
|
||||
// UpdateShopPackageAllocationStatusParams 更新单套餐分配状态聚合参数
|
||||
type UpdateShopPackageAllocationStatusParams struct {
|
||||
IDReq
|
||||
UpdateShopPackageAllocationStatusRequest
|
||||
}
|
||||
150
internal/model/dto/shop_series_allocation.go
Normal file
150
internal/model/dto/shop_series_allocation.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package dto
|
||||
|
||||
// CreateShopSeriesAllocationRequest 创建套餐系列分配请求
|
||||
type CreateShopSeriesAllocationRequest struct {
|
||||
ShopID uint `json:"shop_id" validate:"required" required:"true" description:"被分配的店铺ID"`
|
||||
SeriesID uint `json:"series_id" validate:"required" required:"true" description:"套餐系列ID"`
|
||||
PricingMode string `json:"pricing_mode" validate:"required,oneof=fixed percent" required:"true" description:"加价模式 (fixed:固定金额, percent:百分比)"`
|
||||
PricingValue int64 `json:"pricing_value" validate:"required,min=0" required:"true" minimum:"0" description:"加价值(分或千分比,如100=10%)"`
|
||||
OneTimeCommissionTrigger string `json:"one_time_commission_trigger" validate:"omitempty,oneof=one_time_recharge accumulated_recharge" description:"一次性佣金触发类型 (one_time_recharge:单次充值, accumulated_recharge:累计充值)"`
|
||||
OneTimeCommissionThreshold int64 `json:"one_time_commission_threshold" validate:"omitempty,min=0" minimum:"0" description:"一次性佣金触发阈值(分)"`
|
||||
OneTimeCommissionAmount int64 `json:"one_time_commission_amount" validate:"omitempty,min=0" minimum:"0" description:"一次性佣金金额(分)"`
|
||||
}
|
||||
|
||||
// UpdateShopSeriesAllocationRequest 更新套餐系列分配请求
|
||||
type UpdateShopSeriesAllocationRequest struct {
|
||||
PricingMode *string `json:"pricing_mode" validate:"omitempty,oneof=fixed percent" description:"加价模式 (fixed:固定金额, percent:百分比)"`
|
||||
PricingValue *int64 `json:"pricing_value" validate:"omitempty,min=0" minimum:"0" description:"加价值(分或千分比)"`
|
||||
OneTimeCommissionTrigger *string `json:"one_time_commission_trigger" validate:"omitempty,oneof=one_time_recharge accumulated_recharge" description:"一次性佣金触发类型"`
|
||||
OneTimeCommissionThreshold *int64 `json:"one_time_commission_threshold" validate:"omitempty,min=0" minimum:"0" description:"一次性佣金触发阈值(分)"`
|
||||
OneTimeCommissionAmount *int64 `json:"one_time_commission_amount" validate:"omitempty,min=0" minimum:"0" description:"一次性佣金金额(分)"`
|
||||
}
|
||||
|
||||
// ShopSeriesAllocationListRequest 套餐系列分配列表请求
|
||||
type ShopSeriesAllocationListRequest struct {
|
||||
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
|
||||
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量"`
|
||||
ShopID *uint `json:"shop_id" query:"shop_id" validate:"omitempty" description:"被分配的店铺ID"`
|
||||
SeriesID *uint `json:"series_id" query:"series_id" validate:"omitempty" description:"套餐系列ID"`
|
||||
Status *int `json:"status" query:"status" validate:"omitempty,oneof=1 2" description:"状态 (1:启用, 2:禁用)"`
|
||||
}
|
||||
|
||||
// UpdateShopSeriesAllocationStatusRequest 更新套餐系列分配状态请求
|
||||
type UpdateShopSeriesAllocationStatusRequest struct {
|
||||
Status int `json:"status" validate:"required,oneof=1 2" required:"true" description:"状态 (1:启用, 2:禁用)"`
|
||||
}
|
||||
|
||||
// ShopSeriesAllocationResponse 套餐系列分配响应
|
||||
type ShopSeriesAllocationResponse struct {
|
||||
ID uint `json:"id" description:"分配ID"`
|
||||
ShopID uint `json:"shop_id" description:"被分配的店铺ID"`
|
||||
ShopName string `json:"shop_name" description:"被分配的店铺名称"`
|
||||
SeriesID uint `json:"series_id" description:"套餐系列ID"`
|
||||
SeriesName string `json:"series_name" description:"套餐系列名称"`
|
||||
AllocatorShopID uint `json:"allocator_shop_id" description:"分配者店铺ID"`
|
||||
AllocatorShopName string `json:"allocator_shop_name" description:"分配者店铺名称"`
|
||||
PricingMode string `json:"pricing_mode" description:"加价模式 (fixed:固定金额, percent:百分比)"`
|
||||
PricingValue int64 `json:"pricing_value" description:"加价值(分或千分比)"`
|
||||
CalculatedCostPrice int64 `json:"calculated_cost_price" description:"计算后的成本价(分)"`
|
||||
OneTimeCommissionTrigger string `json:"one_time_commission_trigger" description:"一次性佣金触发类型"`
|
||||
OneTimeCommissionThreshold int64 `json:"one_time_commission_threshold" description:"一次性佣金触发阈值(分)"`
|
||||
OneTimeCommissionAmount int64 `json:"one_time_commission_amount" description:"一次性佣金金额(分)"`
|
||||
Status int `json:"status" description:"状态 (1:启用, 2:禁用)"`
|
||||
CreatedAt string `json:"created_at" description:"创建时间"`
|
||||
UpdatedAt string `json:"updated_at" description:"更新时间"`
|
||||
}
|
||||
|
||||
// ShopSeriesAllocationPageResult 套餐系列分配分页结果
|
||||
type ShopSeriesAllocationPageResult struct {
|
||||
List []*ShopSeriesAllocationResponse `json:"list" description:"分配列表"`
|
||||
Total int64 `json:"total" description:"总数"`
|
||||
Page int `json:"page" description:"当前页"`
|
||||
PageSize int `json:"page_size" description:"每页数量"`
|
||||
TotalPages int `json:"total_pages" description:"总页数"`
|
||||
}
|
||||
|
||||
// UpdateShopSeriesAllocationParams 更新套餐系列分配聚合参数
|
||||
type UpdateShopSeriesAllocationParams struct {
|
||||
IDReq
|
||||
UpdateShopSeriesAllocationRequest
|
||||
}
|
||||
|
||||
// UpdateShopSeriesAllocationStatusParams 更新套餐系列分配状态聚合参数
|
||||
type UpdateShopSeriesAllocationStatusParams struct {
|
||||
IDReq
|
||||
UpdateShopSeriesAllocationStatusRequest
|
||||
}
|
||||
|
||||
// CreateCommissionTierRequest 创建梯度佣金请求
|
||||
type CreateCommissionTierRequest struct {
|
||||
TierType string `json:"tier_type" validate:"required,oneof=sales_count sales_amount" required:"true" description:"梯度类型 (sales_count:销量, sales_amount:销售额)"`
|
||||
PeriodType string `json:"period_type" validate:"required,oneof=monthly quarterly yearly custom" required:"true" description:"周期类型 (monthly:月度, quarterly:季度, yearly:年度, custom:自定义)"`
|
||||
PeriodStartDate *string `json:"period_start_date" validate:"omitempty" description:"自定义周期开始日期(YYYY-MM-DD),当周期类型为custom时必填"`
|
||||
PeriodEndDate *string `json:"period_end_date" validate:"omitempty" description:"自定义周期结束日期(YYYY-MM-DD),当周期类型为custom时必填"`
|
||||
ThresholdValue int64 `json:"threshold_value" validate:"required,min=1" required:"true" minimum:"1" description:"阈值(销量或金额分)"`
|
||||
CommissionAmount int64 `json:"commission_amount" validate:"required,min=1" required:"true" minimum:"1" description:"佣金金额(分)"`
|
||||
}
|
||||
|
||||
// UpdateCommissionTierRequest 更新梯度佣金请求
|
||||
type UpdateCommissionTierRequest struct {
|
||||
TierType *string `json:"tier_type" validate:"omitempty,oneof=sales_count sales_amount" description:"梯度类型"`
|
||||
PeriodType *string `json:"period_type" validate:"omitempty,oneof=monthly quarterly yearly custom" description:"周期类型"`
|
||||
PeriodStartDate *string `json:"period_start_date" validate:"omitempty" description:"自定义周期开始日期"`
|
||||
PeriodEndDate *string `json:"period_end_date" validate:"omitempty" description:"自定义周期结束日期"`
|
||||
ThresholdValue *int64 `json:"threshold_value" validate:"omitempty,min=1" minimum:"1" description:"阈值"`
|
||||
CommissionAmount *int64 `json:"commission_amount" validate:"omitempty,min=1" minimum:"1" description:"佣金金额(分)"`
|
||||
}
|
||||
|
||||
// CommissionTierResponse 梯度佣金响应
|
||||
type CommissionTierResponse struct {
|
||||
ID uint `json:"id" description:"梯度ID"`
|
||||
AllocationID uint `json:"allocation_id" description:"关联的分配ID"`
|
||||
TierType string `json:"tier_type" description:"梯度类型 (sales_count:销量, sales_amount:销售额)"`
|
||||
PeriodType string `json:"period_type" description:"周期类型 (monthly:月度, quarterly:季度, yearly:年度, custom:自定义)"`
|
||||
PeriodStartDate string `json:"period_start_date,omitempty" description:"自定义周期开始日期"`
|
||||
PeriodEndDate string `json:"period_end_date,omitempty" description:"自定义周期结束日期"`
|
||||
ThresholdValue int64 `json:"threshold_value" description:"阈值"`
|
||||
CommissionAmount int64 `json:"commission_amount" description:"佣金金额(分)"`
|
||||
CreatedAt string `json:"created_at" description:"创建时间"`
|
||||
UpdatedAt string `json:"updated_at" description:"更新时间"`
|
||||
}
|
||||
|
||||
// CreateCommissionTierParams 创建梯度佣金聚合参数
|
||||
type CreateCommissionTierParams struct {
|
||||
IDReq
|
||||
CreateCommissionTierRequest
|
||||
}
|
||||
|
||||
// UpdateCommissionTierParams 更新梯度佣金聚合参数
|
||||
type UpdateCommissionTierParams struct {
|
||||
AllocationIDReq
|
||||
TierIDReq
|
||||
UpdateCommissionTierRequest
|
||||
}
|
||||
|
||||
// DeleteCommissionTierParams 删除梯度佣金聚合参数
|
||||
type DeleteCommissionTierParams struct {
|
||||
AllocationIDReq
|
||||
TierIDReq
|
||||
}
|
||||
|
||||
// AllocationIDReq 分配ID路径参数
|
||||
type AllocationIDReq struct {
|
||||
ID uint `path:"id" description:"分配ID" required:"true"`
|
||||
}
|
||||
|
||||
// TierIDReq 梯度ID路径参数
|
||||
type TierIDReq struct {
|
||||
TierID uint `path:"tier_id" description:"梯度ID" required:"true"`
|
||||
}
|
||||
|
||||
// CommissionTierListResult 梯度佣金列表结果
|
||||
type CommissionTierListResult struct {
|
||||
List []*CommissionTierResponse `json:"list" description:"梯度佣金列表"`
|
||||
}
|
||||
|
||||
// TierIDParams 梯度ID路径参数组合
|
||||
type TierIDParams struct {
|
||||
AllocationIDReq
|
||||
TierIDReq
|
||||
}
|
||||
Reference in New Issue
Block a user