feat(fragment): 商品成本价 + 活动奖品单次产出数量
- products 表新增 cost_price 字段(成本价/分) - activity_reward_settings 新增 drop_quantity(单次产出数量,默认1) 和 cost_snapshot_cents(成本价快照) - 奖品创建/修改时自动快照成本价,drop_quantity 限制 1-100 - 抽奖发放逻辑按 drop_quantity 循环创建多个库存项 - 抽奖结果接口按 drop_quantity 返回多条 item,前端自动合并显示 - 抽奖记录接口返回 drop_quantity 字段 - 商品管理 API 全链路支持 cost_price
This commit is contained in:
parent
1f8f3f7fad
commit
46b9555823
@ -32,6 +32,7 @@ type drawLogItem struct {
|
||||
IsWinner int32 `json:"is_winner"`
|
||||
Level int32 `json:"level"`
|
||||
CurrentLevel int32 `json:"current_level"`
|
||||
DropQuantity int32 `json:"drop_quantity"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
@ -142,6 +143,7 @@ func (h *handler) ListDrawLogs() core.HandlerFunc {
|
||||
// 批量查询奖品与商品信息
|
||||
rewardNameMap := make(map[int64]string)
|
||||
rewardImageMap := make(map[int64]string)
|
||||
rewardDropQtyMap := make(map[int64]int32)
|
||||
if len(rewardIDs) > 0 {
|
||||
rewards, err := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityRewardSettings.ID.In(rewardIDs...)).Find()
|
||||
if err == nil {
|
||||
@ -150,6 +152,7 @@ func (h *handler) ListDrawLogs() core.HandlerFunc {
|
||||
rewardProductMap := make(map[int64]int64)
|
||||
|
||||
for _, r := range rewards {
|
||||
rewardDropQtyMap[r.ID] = r.DropQuantity
|
||||
// 不再使用 r.Name,只通过 ProductID 关联查询商品名称
|
||||
if r.ProductID > 0 {
|
||||
if _, ok := productSet[r.ProductID]; !ok {
|
||||
@ -208,6 +211,7 @@ func (h *handler) ListDrawLogs() core.HandlerFunc {
|
||||
IsWinner: v.IsWinner,
|
||||
Level: v.Level,
|
||||
CurrentLevel: v.CurrentLevel,
|
||||
DropQuantity: rewardDropQtyMap[v.RewardID],
|
||||
CreatedAt: v.CreatedAt,
|
||||
}
|
||||
}
|
||||
@ -257,7 +261,9 @@ func (h *handler) ListDrawLogsByLevel() core.HandlerFunc {
|
||||
// 收集所有 ProductID 用于批量查询商品名称
|
||||
productIDs := make([]int64, 0, len(rewards))
|
||||
rewardProductMap := make(map[int64]int64) // rewardID -> productID
|
||||
rewardDropQtyMap2 := make(map[int64]int32)
|
||||
for _, r := range rewards {
|
||||
rewardDropQtyMap2[r.ID] = r.DropQuantity
|
||||
if r.ProductID > 0 {
|
||||
productIDs = append(productIDs, r.ProductID)
|
||||
rewardProductMap[r.ID] = r.ProductID
|
||||
@ -304,6 +310,7 @@ func (h *handler) ListDrawLogsByLevel() core.HandlerFunc {
|
||||
IsWinner: v.IsWinner,
|
||||
Level: v.Level,
|
||||
CurrentLevel: v.CurrentLevel,
|
||||
DropQuantity: rewardDropQtyMap2[v.RewardID],
|
||||
}
|
||||
groupsMap[v.Level] = append(groupsMap[v.Level], item)
|
||||
}
|
||||
|
||||
@ -159,7 +159,11 @@ func (h *handler) LotteryResultByOrder() core.HandlerFunc {
|
||||
drawLogIDs = append(drawLogIDs, lg.ID)
|
||||
image := ""
|
||||
name := ""
|
||||
dropQty := int32(1)
|
||||
if rw, ok := rewardMap[lg.RewardID]; ok {
|
||||
if rw.DropQuantity > 1 {
|
||||
dropQty = rw.DropQuantity
|
||||
}
|
||||
if p, ok := productMap[rw.ProductID]; ok {
|
||||
name = p.Name
|
||||
if p.ImagesJSON != "" {
|
||||
@ -171,13 +175,15 @@ func (h *handler) LotteryResultByOrder() core.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
items = append(items, orderResultItem{
|
||||
RewardID: lg.RewardID,
|
||||
RewardName: name,
|
||||
Level: lg.Level,
|
||||
DrawIndex: int32(lg.DrawIndex + 1),
|
||||
Image: image,
|
||||
})
|
||||
for q := int32(0); q < dropQty; q++ {
|
||||
items = append(items, orderResultItem{
|
||||
RewardID: lg.RewardID,
|
||||
RewardName: name,
|
||||
Level: lg.Level,
|
||||
DrawIndex: int32(lg.DrawIndex + 1),
|
||||
Image: image,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 处理翻倍奖励
|
||||
|
||||
@ -24,6 +24,7 @@ type rewardItem struct {
|
||||
IsBoss int32 `json:"is_boss"`
|
||||
ProductImage string `json:"product_image"`
|
||||
MinScore int64 `json:"min_score"`
|
||||
DropQuantity int32 `json:"drop_quantity"`
|
||||
}
|
||||
|
||||
type listRewardsResponse struct {
|
||||
@ -130,6 +131,7 @@ func (h *handler) ListIssueRewards() core.HandlerFunc {
|
||||
IsBoss: v.IsBoss,
|
||||
ProductImage: imageMap[v.ProductID],
|
||||
MinScore: v.MinScore,
|
||||
DropQuantity: v.DropQuantity,
|
||||
}
|
||||
}
|
||||
ctx.Payload(res)
|
||||
|
||||
@ -15,6 +15,7 @@ type createProductRequest struct {
|
||||
CategoryID int64 `json:"category_id" binding:"required"`
|
||||
ImagesJSON string `json:"images_json"`
|
||||
Price int64 `json:"price" binding:"required"`
|
||||
CostPrice int64 `json:"cost_price"`
|
||||
Stock int64 `json:"stock" binding:"required"`
|
||||
Status int32 `json:"status"`
|
||||
Description string `json:"description"`
|
||||
@ -48,7 +49,7 @@ func (h *handler) CreateProduct() core.HandlerFunc {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作"))
|
||||
return
|
||||
}
|
||||
item, err := h.product.CreateProduct(ctx.RequestContext(), prodsvc.CreateProductInput{Name: req.Name, CategoryID: req.CategoryID, ImagesJSON: req.ImagesJSON, Price: req.Price, Stock: req.Stock, Status: req.Status, Description: req.Description, ShowInMiniapp: req.ShowInMiniapp})
|
||||
item, err := h.product.CreateProduct(ctx.RequestContext(), prodsvc.CreateProductInput{Name: req.Name, CategoryID: req.CategoryID, ImagesJSON: req.ImagesJSON, Price: req.Price, CostPrice: req.CostPrice, Stock: req.Stock, Status: req.Status, Description: req.Description, ShowInMiniapp: req.ShowInMiniapp})
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error()))
|
||||
return
|
||||
@ -64,6 +65,7 @@ type modifyProductRequest struct {
|
||||
CategoryID *int64 `json:"category_id"`
|
||||
ImagesJSON *string `json:"images_json"`
|
||||
Price *int64 `json:"price"`
|
||||
CostPrice *int64 `json:"cost_price"`
|
||||
Stock *int64 `json:"stock"`
|
||||
Status *int32 `json:"status"`
|
||||
Description *string `json:"description"`
|
||||
@ -94,7 +96,7 @@ func (h *handler) ModifyProduct() core.HandlerFunc {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作"))
|
||||
return
|
||||
}
|
||||
if err := h.product.ModifyProduct(ctx.RequestContext(), id, prodsvc.ModifyProductInput{Name: req.Name, CategoryID: req.CategoryID, ImagesJSON: req.ImagesJSON, Price: req.Price, Stock: req.Stock, Status: req.Status, Description: req.Description, ShowInMiniapp: req.ShowInMiniapp}); err != nil {
|
||||
if err := h.product.ModifyProduct(ctx.RequestContext(), id, prodsvc.ModifyProductInput{Name: req.Name, CategoryID: req.CategoryID, ImagesJSON: req.ImagesJSON, Price: req.Price, CostPrice: req.CostPrice, Stock: req.Stock, Status: req.Status, Description: req.Description, ShowInMiniapp: req.ShowInMiniapp}); err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error()))
|
||||
return
|
||||
}
|
||||
@ -143,6 +145,7 @@ type productItem struct {
|
||||
CategoryID int64 `json:"category_id"`
|
||||
ImagesJSON string `json:"images_json"`
|
||||
Price int64 `json:"price"`
|
||||
CostPrice int64 `json:"cost_price"`
|
||||
Stock int64 `json:"stock"`
|
||||
Sales int64 `json:"sales"`
|
||||
Status int32 `json:"status"`
|
||||
@ -188,7 +191,7 @@ func (h *handler) ListProducts() core.HandlerFunc {
|
||||
res.Total = total
|
||||
res.List = make([]productItem, len(items))
|
||||
for i, it := range items {
|
||||
res.List[i] = productItem{ID: it.ID, Name: it.Name, CategoryID: it.CategoryID, ImagesJSON: it.ImagesJSON, Price: it.Price, Stock: it.Stock, Sales: it.Sales, Status: it.Status, Description: it.Description, ShowInMiniapp: it.ShowInMiniapp}
|
||||
res.List[i] = productItem{ID: it.ID, Name: it.Name, CategoryID: it.CategoryID, ImagesJSON: it.ImagesJSON, Price: it.Price, CostPrice: it.CostPrice, Stock: it.Stock, Sales: it.Sales, Status: it.Status, Description: it.Description, ShowInMiniapp: it.ShowInMiniapp}
|
||||
}
|
||||
ctx.Payload(res)
|
||||
}
|
||||
|
||||
@ -21,11 +21,13 @@ type rewardItem struct {
|
||||
Sort int32 `json:"sort"`
|
||||
IsBoss int32 `json:"is_boss"`
|
||||
MinScore int64 `json:"min_score"`
|
||||
DropQuantity int32 `json:"drop_quantity"`
|
||||
ProductName string `json:"product_name"`
|
||||
ProductImageUrl string `json:"product_image_url"`
|
||||
ProductPrice float64 `json:"product_price"` // 兼容:返回配置快照价
|
||||
ProductPriceSnapshot float64 `json:"product_price_snapshot"`
|
||||
ProductPriceCurrent float64 `json:"product_price_current"`
|
||||
CostSnapshotCents int64 `json:"cost_snapshot_cents"`
|
||||
}
|
||||
|
||||
type createRewardsRequest struct {
|
||||
@ -73,15 +75,16 @@ func (h *handler) CreateIssueRewards() core.HandlerFunc {
|
||||
var rewards []activitysvc.CreateRewardInput
|
||||
for _, r := range req.Rewards {
|
||||
rewards = append(rewards, activitysvc.CreateRewardInput{
|
||||
ProductID: r.ProductID,
|
||||
Name: "", // Name 不再从前端传递,后续可从商品表获取
|
||||
Weight: int32(r.Weight),
|
||||
Quantity: r.Quantity,
|
||||
OriginalQty: r.OriginalQty,
|
||||
Level: r.Level,
|
||||
Sort: r.Sort,
|
||||
IsBoss: r.IsBoss,
|
||||
MinScore: r.MinScore,
|
||||
ProductID: r.ProductID,
|
||||
Name: "", // Name 不再从前端传递,后续可从商品表获取
|
||||
Weight: int32(r.Weight),
|
||||
Quantity: r.Quantity,
|
||||
OriginalQty: r.OriginalQty,
|
||||
Level: r.Level,
|
||||
Sort: r.Sort,
|
||||
IsBoss: r.IsBoss,
|
||||
MinScore: r.MinScore,
|
||||
DropQuantity: r.DropQuantity,
|
||||
})
|
||||
}
|
||||
|
||||
@ -138,15 +141,17 @@ func (h *handler) ListIssueRewards() core.HandlerFunc {
|
||||
res.List = make([]rewardItem, len(items))
|
||||
for i, v := range items {
|
||||
it := rewardItem{
|
||||
ID: v.ID,
|
||||
ProductID: v.ProductID,
|
||||
Weight: float64(v.Weight),
|
||||
Quantity: v.Quantity,
|
||||
OriginalQty: v.OriginalQty,
|
||||
Level: v.Level,
|
||||
Sort: v.Sort,
|
||||
IsBoss: v.IsBoss,
|
||||
MinScore: v.MinScore,
|
||||
ID: v.ID,
|
||||
ProductID: v.ProductID,
|
||||
Weight: float64(v.Weight),
|
||||
Quantity: v.Quantity,
|
||||
OriginalQty: v.OriginalQty,
|
||||
Level: v.Level,
|
||||
Sort: v.Sort,
|
||||
IsBoss: v.IsBoss,
|
||||
MinScore: v.MinScore,
|
||||
DropQuantity: v.DropQuantity,
|
||||
CostSnapshotCents: v.CostSnapshotCents,
|
||||
}
|
||||
|
||||
if v.ProductID > 0 {
|
||||
@ -165,14 +170,15 @@ func (h *handler) ListIssueRewards() core.HandlerFunc {
|
||||
}
|
||||
|
||||
type modifyRewardRequest struct {
|
||||
ProductID *int64 `json:"product_id"`
|
||||
Weight *float64 `json:"weight"`
|
||||
Quantity *int64 `json:"quantity"`
|
||||
OriginalQty *int64 `json:"original_qty"`
|
||||
Level *int32 `json:"level"`
|
||||
Sort *int32 `json:"sort"`
|
||||
IsBoss *int32 `json:"is_boss"`
|
||||
MinScore *int64 `json:"min_score"`
|
||||
ProductID *int64 `json:"product_id"`
|
||||
Weight *float64 `json:"weight"`
|
||||
Quantity *int64 `json:"quantity"`
|
||||
OriginalQty *int64 `json:"original_qty"`
|
||||
Level *int32 `json:"level"`
|
||||
Sort *int32 `json:"sort"`
|
||||
IsBoss *int32 `json:"is_boss"`
|
||||
MinScore *int64 `json:"min_score"`
|
||||
DropQuantity *int32 `json:"drop_quantity"`
|
||||
}
|
||||
|
||||
// ModifyIssueReward 更新期数奖励
|
||||
@ -206,15 +212,16 @@ func (h *handler) ModifyIssueReward() core.HandlerFunc {
|
||||
return
|
||||
}
|
||||
in := activitysvc.ModifyRewardInput{
|
||||
ProductID: req.ProductID,
|
||||
Name: "", // Name 不再使用
|
||||
Weight: req.Weight,
|
||||
Quantity: req.Quantity,
|
||||
OriginalQty: req.OriginalQty,
|
||||
Level: req.Level,
|
||||
Sort: req.Sort,
|
||||
IsBoss: req.IsBoss,
|
||||
MinScore: req.MinScore,
|
||||
ProductID: req.ProductID,
|
||||
Name: "", // Name 不再使用
|
||||
Weight: req.Weight,
|
||||
Quantity: req.Quantity,
|
||||
OriginalQty: req.OriginalQty,
|
||||
Level: req.Level,
|
||||
Sort: req.Sort,
|
||||
IsBoss: req.IsBoss,
|
||||
MinScore: req.MinScore,
|
||||
DropQuantity: req.DropQuantity,
|
||||
}
|
||||
if err := h.activity.ModifyIssueReward(ctx.RequestContext(), rewardID, in); err != nil {
|
||||
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateIssueRewardsError, err.Error()))
|
||||
|
||||
@ -28,7 +28,9 @@ type ActivityRewardSettings struct {
|
||||
Sort int32 `gorm:"column:sort;comment:排序" json:"sort"` // 排序
|
||||
IsBoss int32 `gorm:"column:is_boss;comment:Boss 1 是 0 不是" json:"is_boss"` // Boss 1 是 0 不是
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at" json:"deleted_at"`
|
||||
MinScore int64 `gorm:"column:min_score;not null;comment:最低得分/碰数要求" json:"min_score"` // 最低得分/碰数要求
|
||||
MinScore int64 `gorm:"column:min_score;not null;comment:最低得分/碰数要求" json:"min_score"` // 最低得分/碰数要求
|
||||
DropQuantity int32 `gorm:"column:drop_quantity;not null;default:1;comment:单次抽中产出数量" json:"drop_quantity"` // 单次抽中产出数量
|
||||
CostSnapshotCents int64 `gorm:"column:cost_snapshot_cents;not null;default:0;comment:奖品配置时成本价快照(分)" json:"cost_snapshot_cents"` // 奖品配置时成本价快照(分)
|
||||
}
|
||||
|
||||
// TableName ActivityRewardSettings's table name
|
||||
|
||||
@ -21,6 +21,7 @@ type Products struct {
|
||||
CategoryID int64 `gorm:"column:category_id;comment:单一主分类ID(product_categories.id)" json:"category_id"` // 单一主分类ID(product_categories.id)
|
||||
ImagesJSON string `gorm:"column:images_json;comment:商品图片JSON(数组)" json:"images_json"` // 商品图片JSON(数组)
|
||||
Price int64 `gorm:"column:price;not null;comment:商品售价(分)" json:"price"` // 商品售价(分)
|
||||
CostPrice int64 `gorm:"column:cost_price;not null;default:0;comment:成本价(分)" json:"cost_price"` // 成本价(分)
|
||||
Stock int64 `gorm:"column:stock;not null;comment:可售库存" json:"stock"` // 可售库存
|
||||
Sales int64 `gorm:"column:sales;not null;comment:已售数量" json:"sales"` // 已售数量
|
||||
Status int32 `gorm:"column:status;not null;default:1;comment:上下架状态:1上架 2下架" json:"status"` // 上下架状态:1上架 2下架
|
||||
|
||||
@ -232,6 +232,8 @@ type CreateRewardInput struct {
|
||||
// IsBoss 是否Boss奖励
|
||||
IsBoss int32
|
||||
MinScore int64
|
||||
// DropQuantity 单次抽中产出数量(默认1)
|
||||
DropQuantity int32
|
||||
}
|
||||
|
||||
type ModifyRewardInput struct {
|
||||
@ -252,4 +254,6 @@ type ModifyRewardInput struct {
|
||||
// IsBoss 是否Boss奖励
|
||||
IsBoss *int32
|
||||
MinScore *int64
|
||||
// DropQuantity 单次抽中产出数量
|
||||
DropQuantity *int32
|
||||
}
|
||||
|
||||
@ -168,8 +168,20 @@ func (s *service) ProcessOrderLottery(ctx context.Context, orderID int64) error
|
||||
var effectLogs []*model.ActivityDrawLogs // 需要处理道具卡效果的日志
|
||||
|
||||
// 无限赏模式下使用总数检测(因为inventory.RewardID=0)
|
||||
// 如果已发放总数已达到开奖数量,说明已完成发放,跳过后续逻辑
|
||||
if invTotalCount >= dc {
|
||||
// 计算期望发放总数(考虑 drop_quantity)
|
||||
expectedTotal := int64(0)
|
||||
for _, lg := range existingLogs {
|
||||
if rw, ok := rewardMap[lg.RewardID]; ok {
|
||||
dq := int64(rw.DropQuantity)
|
||||
if dq < 1 {
|
||||
dq = 1
|
||||
}
|
||||
expectedTotal += dq
|
||||
} else {
|
||||
expectedTotal++
|
||||
}
|
||||
}
|
||||
if invTotalCount >= expectedTotal && expectedTotal > 0 {
|
||||
// s.logger.Info("奖励已全部发放,跳过重复发放", zap.Int64("order_id", orderID), zap.Int64("dc", dc), zap.Int64("invTotalCount", invTotalCount))
|
||||
} else {
|
||||
for i := int64(0); i < dc; i++ {
|
||||
@ -178,19 +190,29 @@ func (s *service) ProcessOrderLottery(ctx context.Context, orderID int64) error
|
||||
continue
|
||||
}
|
||||
|
||||
// 统计该 RewardID 应得数量
|
||||
needed := int64(0)
|
||||
rw := rewardMap[log.RewardID]
|
||||
if rw == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
dropQty := int64(rw.DropQuantity)
|
||||
if dropQty < 1 {
|
||||
dropQty = 1
|
||||
}
|
||||
|
||||
// 统计该 RewardID 应得库存数量(命中次数 × 单次产出数量)
|
||||
hitCount := int64(0)
|
||||
for j := int64(0); j <= i; j++ {
|
||||
if l, ok := logMap[j]; ok && l.RewardID == log.RewardID {
|
||||
needed++
|
||||
hitCount++
|
||||
}
|
||||
}
|
||||
needed := hitCount * dropQty
|
||||
|
||||
// 检查是否需要发放
|
||||
if invCountMap[log.RewardID] < needed {
|
||||
rw := rewardMap[log.RewardID]
|
||||
if rw != nil {
|
||||
rewardIDRef := &log.RewardID
|
||||
rewardIDRef := &log.RewardID
|
||||
for q := int64(0); q < dropQty; q++ {
|
||||
batchItems = append(batchItems, usersvc.BatchRewardItem{
|
||||
ProductID: rw.ProductID,
|
||||
RewardID: rewardIDRef,
|
||||
@ -198,12 +220,12 @@ func (s *service) ProcessOrderLottery(ctx context.Context, orderID int64) error
|
||||
ActivityID: aid,
|
||||
Remark: productNameMap[rw.ProductID],
|
||||
})
|
||||
invCountMap[log.RewardID]++ // 内存计数同步
|
||||
}
|
||||
invCountMap[log.RewardID] += dropQty // 内存计数同步
|
||||
|
||||
// 记录需要处理道具卡效果的日志
|
||||
if act != nil && act.AllowItemCards && icID > 0 {
|
||||
effectLogs = append(effectLogs, log)
|
||||
}
|
||||
// 记录需要处理道具卡效果的日志
|
||||
if act != nil && act.AllowItemCards && icID > 0 {
|
||||
effectLogs = append(effectLogs, log)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ func (s *service) CreateIssueRewards(ctx context.Context, issueID int64, rewards
|
||||
}
|
||||
}
|
||||
productPriceMap := make(map[int64]int64)
|
||||
productCostMap := make(map[int64]int64)
|
||||
if len(productIDs) > 0 {
|
||||
ids := make([]int64, 0, len(productIDs))
|
||||
for id := range productIDs {
|
||||
@ -31,15 +32,25 @@ func (s *service) CreateIssueRewards(ctx context.Context, issueID int64, rewards
|
||||
}
|
||||
for _, p := range products {
|
||||
productPriceMap[p.ID] = p.Price
|
||||
productCostMap[p.ID] = p.CostPrice
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range rewards {
|
||||
dropQty := r.DropQuantity
|
||||
if dropQty < 1 {
|
||||
dropQty = 1
|
||||
}
|
||||
if dropQty > 100 {
|
||||
dropQty = 100
|
||||
}
|
||||
item := &model.ActivityRewardSettings{
|
||||
IssueID: issueID,
|
||||
ProductID: r.ProductID,
|
||||
PriceSnapshotCents: productPriceMap[r.ProductID],
|
||||
PriceSnapshotAt: time.Now(),
|
||||
CostSnapshotCents: productCostMap[r.ProductID],
|
||||
DropQuantity: dropQty,
|
||||
Weight: r.Weight,
|
||||
Quantity: r.Quantity,
|
||||
OriginalQty: r.OriginalQty,
|
||||
|
||||
@ -18,14 +18,17 @@ func (s *service) ModifyIssueReward(ctx context.Context, rewardID int64, in Modi
|
||||
if in.ProductID != nil {
|
||||
item.ProductID = *in.ProductID
|
||||
priceSnapshot := int64(0)
|
||||
costSnapshot := int64(0)
|
||||
if *in.ProductID > 0 {
|
||||
product, err := s.readDB.Products.WithContext(ctx).Where(s.readDB.Products.ID.Eq(*in.ProductID)).First()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
priceSnapshot = product.Price
|
||||
costSnapshot = product.CostPrice
|
||||
}
|
||||
item.PriceSnapshotCents = priceSnapshot
|
||||
item.CostSnapshotCents = costSnapshot
|
||||
item.PriceSnapshotAt = time.Now()
|
||||
}
|
||||
if in.Weight != nil {
|
||||
@ -49,6 +52,16 @@ func (s *service) ModifyIssueReward(ctx context.Context, rewardID int64, in Modi
|
||||
if in.MinScore != nil {
|
||||
item.MinScore = *in.MinScore
|
||||
}
|
||||
if in.DropQuantity != nil {
|
||||
dq := *in.DropQuantity
|
||||
if dq < 1 {
|
||||
dq = 1
|
||||
}
|
||||
if dq > 100 {
|
||||
dq = 100
|
||||
}
|
||||
item.DropQuantity = dq
|
||||
}
|
||||
item.UpdatedAt = time.Now()
|
||||
return s.writeDB.ActivityRewardSettings.WithContext(ctx).Save(item)
|
||||
}
|
||||
|
||||
@ -128,6 +128,7 @@ type CreateProductInput struct {
|
||||
CategoryID int64
|
||||
ImagesJSON string
|
||||
Price int64
|
||||
CostPrice int64
|
||||
Stock int64
|
||||
Status int32
|
||||
Description string
|
||||
@ -139,6 +140,7 @@ type ModifyProductInput struct {
|
||||
CategoryID *int64
|
||||
ImagesJSON *string
|
||||
Price *int64
|
||||
CostPrice *int64
|
||||
Stock *int64
|
||||
Status *int32
|
||||
Description *string
|
||||
@ -192,7 +194,7 @@ func (s *service) CreateProduct(ctx context.Context, in CreateProductInput) (*mo
|
||||
if in.ShowInMiniapp != nil {
|
||||
showInMiniapp = *in.ShowInMiniapp
|
||||
}
|
||||
m := &model.Products{Name: in.Name, CategoryID: in.CategoryID, ImagesJSON: normalizeJSON(in.ImagesJSON), Price: in.Price, Stock: in.Stock, Status: in.Status, Description: in.Description, ShowInMiniapp: showInMiniapp}
|
||||
m := &model.Products{Name: in.Name, CategoryID: in.CategoryID, ImagesJSON: normalizeJSON(in.ImagesJSON), Price: in.Price, CostPrice: in.CostPrice, Stock: in.Stock, Status: in.Status, Description: in.Description, ShowInMiniapp: showInMiniapp}
|
||||
if err := s.writeDB.Products.WithContext(ctx).Create(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -214,6 +216,9 @@ func (s *service) ModifyProduct(ctx context.Context, id int64, in ModifyProductI
|
||||
if in.Price != nil {
|
||||
set["price"] = *in.Price
|
||||
}
|
||||
if in.CostPrice != nil {
|
||||
set["cost_price"] = *in.CostPrice
|
||||
}
|
||||
if in.Stock != nil {
|
||||
set["stock"] = *in.Stock
|
||||
}
|
||||
|
||||
8
migrations/20260323_fragment_cost_and_drop_qty.sql
Normal file
8
migrations/20260323_fragment_cost_and_drop_qty.sql
Normal file
@ -0,0 +1,8 @@
|
||||
-- 1. products 表新增成本价
|
||||
ALTER TABLE products
|
||||
ADD COLUMN cost_price BIGINT NOT NULL DEFAULT 0 COMMENT '成本价(分)';
|
||||
|
||||
-- 2. activity_reward_settings 新增单次产出数量 + 成本价快照
|
||||
ALTER TABLE activity_reward_settings
|
||||
ADD COLUMN drop_quantity INT NOT NULL DEFAULT 1 COMMENT '单次抽中产出数量',
|
||||
ADD COLUMN cost_snapshot_cents BIGINT NOT NULL DEFAULT 0 COMMENT '奖品配置时成本价快照(分)';
|
||||
Loading…
x
Reference in New Issue
Block a user