added: Cost price adjustment

This commit is contained in:
2025-11-13 21:28:08 +08:00
parent 00a3f0f1e9
commit 1bfeac0504
5 changed files with 146 additions and 17 deletions

View File

@@ -2,6 +2,7 @@ package product
import (
"context"
"fmt"
"github.com/shopspring/decimal"
"go.uber.org/zap"
@@ -45,6 +46,22 @@ func (s *SubscriptionApplicationServiceImpl) UpdateSubscriptionPrice(ctx context
// BatchUpdateSubscriptionPrices 一键改价
// 业务流程1. 获取用户所有订阅 2. 根据范围筛选 3. 批量更新价格
func (s *SubscriptionApplicationServiceImpl) BatchUpdateSubscriptionPrices(ctx context.Context, cmd *commands.BatchUpdateSubscriptionPricesCommand) error {
// 记录请求参数
s.logger.Info("开始批量更新订阅价格",
zap.String("user_id", cmd.UserID),
zap.String("adjustment_type", cmd.AdjustmentType),
zap.Float64("discount", cmd.Discount),
zap.Float64("cost_multiple", cmd.CostMultiple),
zap.String("scope", cmd.Scope))
// 验证调整方式对应的参数
if cmd.AdjustmentType == "discount" && cmd.Discount <= 0 {
return fmt.Errorf("按售价折扣调整时折扣比例必须大于0")
}
if cmd.AdjustmentType == "cost_multiple" && cmd.CostMultiple <= 0 {
return fmt.Errorf("按成本价倍数调整时倍数必须大于0")
}
subscriptions, _, err := s.productSubscriptionService.ListSubscriptions(ctx, &repoQueries.ListSubscriptionsQuery{
UserID: cmd.UserID,
Page: 1,
@@ -54,6 +71,9 @@ func (s *SubscriptionApplicationServiceImpl) BatchUpdateSubscriptionPrices(ctx c
return err
}
s.logger.Info("获取到订阅列表",
zap.Int("total_subscriptions", len(subscriptions)))
// 根据范围筛选订阅
var targetSubscriptions []*entities.Subscription
for _, sub := range subscriptions {
@@ -69,24 +89,64 @@ func (s *SubscriptionApplicationServiceImpl) BatchUpdateSubscriptionPrices(ctx c
}
// 批量更新价格
updatedCount := 0
skippedCount := 0
for _, sub := range targetSubscriptions {
if sub.Product != nil {
// 计算折扣后的价格
discountRatio := cmd.Discount / 10
newPrice := sub.Product.Price.Mul(decimal.NewFromFloat(discountRatio))
// 四舍五入到2位小数
newPrice = newPrice.Round(2)
if sub.Product == nil {
skippedCount++
continue
}
err := s.productSubscriptionService.UpdateSubscriptionPrice(ctx, sub.ID, newPrice.InexactFloat64())
if err != nil {
s.logger.Error("批量更新订阅价格失败",
var newPrice decimal.Decimal
if cmd.AdjustmentType == "discount" {
// 按售价折扣调整
discountRatio := cmd.Discount / 10
newPrice = sub.Product.Price.Mul(decimal.NewFromFloat(discountRatio))
} else if cmd.AdjustmentType == "cost_multiple" {
// 按成本价倍数调整
// 检查成本价是否有效必须大于0
// 使用严格检查成本价必须大于0
if !sub.Product.CostPrice.GreaterThan(decimal.Zero) {
// 跳过没有成本价或成本价为0的产品
skippedCount++
s.logger.Info("跳过未设置成本价或成本价为0的订阅",
zap.String("subscription_id", sub.ID),
zap.Error(err))
// 继续处理其他订阅,不中断整个流程
zap.String("product_id", sub.ProductID),
zap.String("product_name", sub.Product.Name),
zap.String("cost_price", sub.Product.CostPrice.String()))
continue
}
// 计算成本价倍数后的价格
newPrice = sub.Product.CostPrice.Mul(decimal.NewFromFloat(cmd.CostMultiple))
} else {
s.logger.Warn("未知的调整方式",
zap.String("adjustment_type", cmd.AdjustmentType),
zap.String("subscription_id", sub.ID))
skippedCount++
continue
}
// 四舍五入到2位小数
newPrice = newPrice.Round(2)
err := s.productSubscriptionService.UpdateSubscriptionPrice(ctx, sub.ID, newPrice.InexactFloat64())
if err != nil {
s.logger.Error("批量更新订阅价格失败",
zap.String("subscription_id", sub.ID),
zap.Error(err))
skippedCount++
// 继续处理其他订阅,不中断整个流程
} else {
updatedCount++
}
}
s.logger.Info("批量更新订阅价格完成",
zap.Int("total", len(targetSubscriptions)),
zap.Int("updated", updatedCount),
zap.Int("skipped", skippedCount))
return nil
}
@@ -129,7 +189,7 @@ func (s *SubscriptionApplicationServiceImpl) ListSubscriptions(ctx context.Conte
}
items := make([]responses.SubscriptionInfoResponse, len(subscriptions))
for i := range subscriptions {
resp := s.convertToSubscriptionInfoResponse(subscriptions[i])
resp := s.convertToSubscriptionInfoResponseForAdmin(subscriptions[i])
if resp != nil {
items[i] = *resp // 解引用指针
}
@@ -300,6 +360,65 @@ func (s *SubscriptionApplicationServiceImpl) convertToProductSimpleResponse(prod
}
}
// convertToSubscriptionInfoResponseForAdmin 转换为订阅信息响应(管理员端,包含成本价)
func (s *SubscriptionApplicationServiceImpl) convertToSubscriptionInfoResponseForAdmin(subscription *entities.Subscription) *responses.SubscriptionInfoResponse {
// 查询用户信息
var userInfo *responses.UserSimpleResponse
if subscription.UserID != "" {
user, err := s.userRepo.GetByIDWithEnterpriseInfo(context.Background(), subscription.UserID)
if err == nil {
companyName := "未知公司"
if user.EnterpriseInfo != nil {
companyName = user.EnterpriseInfo.CompanyName
}
userInfo = &responses.UserSimpleResponse{
ID: user.ID,
CompanyName: companyName,
Phone: user.Phone,
}
}
}
var productAdminResponse *responses.ProductSimpleAdminResponse
if subscription.Product != nil {
productAdminResponse = s.convertToProductSimpleAdminResponse(subscription.Product)
}
return &responses.SubscriptionInfoResponse{
ID: subscription.ID,
UserID: subscription.UserID,
ProductID: subscription.ProductID,
Price: subscription.Price.InexactFloat64(),
User: userInfo,
ProductAdmin: productAdminResponse,
APIUsed: subscription.APIUsed,
CreatedAt: subscription.CreatedAt,
UpdatedAt: subscription.UpdatedAt,
}
}
// convertToProductSimpleAdminResponse 转换为管理员产品简单信息响应(包含成本价)
func (s *SubscriptionApplicationServiceImpl) convertToProductSimpleAdminResponse(product *entities.Product) *responses.ProductSimpleAdminResponse {
var categoryResponse *responses.CategorySimpleResponse
if product.Category != nil {
categoryResponse = s.convertToCategorySimpleResponse(product.Category)
}
return &responses.ProductSimpleAdminResponse{
ProductSimpleResponse: responses.ProductSimpleResponse{
ID: product.ID,
OldID: product.OldID,
Name: product.Name,
Code: product.Code,
Description: product.Description,
Price: product.Price.InexactFloat64(),
Category: categoryResponse,
IsPackage: product.IsPackage,
},
CostPrice: product.CostPrice.InexactFloat64(),
}
}
// convertToCategorySimpleResponse 转换为分类简单信息响应
func (s *SubscriptionApplicationServiceImpl) convertToCategorySimpleResponse(category *entities.ProductCategory) *responses.CategorySimpleResponse {
if category == nil {