diff --git a/internal/api/admin/shipping_orders_admin.go b/internal/api/admin/shipping_orders_admin.go index 0db8deb..512cc56 100755 --- a/internal/api/admin/shipping_orders_admin.go +++ b/internal/api/admin/shipping_orders_admin.go @@ -216,11 +216,46 @@ func (h *handler) ListShippingOrders() core.HandlerFunc { } } - // 获取商品信息(去重并计数) - pidCounts := make(map[int64]int64) - for _, pid := range a.pid { - pidCounts[pid]++ + // 获取商品信息(去重并计数,使用发货记录中的价格快照) + // 按商品ID聚合价格和数量 + type productInfo struct { + Name string + Image string + Price int64 // 使用发货记录中的快照价格 + Count int64 } + productMap := make(map[int64]*productInfo) + + // 查询发货记录获取每个商品的快照价格 + if len(a.recordIDs) > 0 { + records, _ := h.readDB.ShippingRecords.WithContext(ctx.RequestContext()).ReadDB(). + Where(h.readDB.ShippingRecords.ID.In(a.recordIDs...)). + Find() + + for _, r := range records { + if r.ProductID <= 0 { + continue + } + if info, ok := productMap[r.ProductID]; ok { + info.Count++ + } else { + // 查询商品名称和图片 + var prodName, prodImage string + if prod, _ := h.readDB.Products.WithContext(ctx.RequestContext()).ReadDB(). + Where(h.readDB.Products.ID.Eq(r.ProductID)).First(); prod != nil { + prodName = prod.Name + prodImage = prod.ImagesJSON + } + productMap[r.ProductID] = &productInfo{ + Name: prodName, + Image: prodImage, + Price: r.Price, // 使用发货记录中的快照价格 + Count: 1, + } + } + } + } + var products []struct { ID int64 `json:"id"` Name string `json:"name"` @@ -228,22 +263,20 @@ func (h *handler) ListShippingOrders() core.HandlerFunc { Price int64 `json:"price"` Count int64 `json:"count"` } - for pid, count := range pidCounts { - if prod, _ := h.readDB.Products.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Products.ID.Eq(pid)).First(); prod != nil { - products = append(products, struct { - ID int64 `json:"id"` - Name string `json:"name"` - Image string `json:"image"` - Price int64 `json:"price"` - Count int64 `json:"count"` - }{ - ID: prod.ID, - Name: prod.Name, - Image: prod.ImagesJSON, // 商品图片JSON - Price: prod.Price, - Count: count, - }) - } + for pid, info := range productMap { + products = append(products, struct { + ID int64 `json:"id"` + Name string `json:"name"` + Image string `json:"image"` + Price int64 `json:"price"` + Count int64 `json:"count"` + }{ + ID: pid, + Name: info.Name, + Image: info.Image, + Price: info.Price, // 使用快照价格 + Count: info.Count, + }) } items = append(items, &ShippingOrderGroup{ diff --git a/internal/service/user/address_share.go b/internal/service/user/address_share.go index c8c0a83..85039d8 100755 --- a/internal/service/user/address_share.go +++ b/internal/service/user/address_share.go @@ -200,11 +200,19 @@ func (s *service) SubmitAddressShare(ctx context.Context, shareToken string, nam } // c. 创建发货记录 (归属于 targetUserID) - var price int64 - if inv.ProductID > 0 { + // 使用资产价值快照,确保价格与分解时一致 + price := inv.ValueCents + if price <= 0 && inv.ProductID > 0 { + // 如果没有快照价格,回退到商品当前价格并记录快照 var p model.Products if err := tx.Table("products").Where("id = ?", inv.ProductID).First(&p).Error; err == nil { price = p.Price + // 回写资产价值快照 + tx.Table("user_inventory").Where("id = ?", inv.ID).Updates(map[string]interface{}{ + "value_cents": price, + "value_source": 2, + "value_snapshot_at": time.Now(), + }) } } @@ -265,10 +273,15 @@ func (s *service) RequestShippingWithBatch(ctx context.Context, userID int64, in addrID = addr.ID } - var price int64 - if inv.ProductID > 0 { + // 使用资产价值快照,确保价格与分解时一致 + price := inv.ValueCents + if price <= 0 && inv.ProductID > 0 { + // 如果没有快照价格,回退到商品当前价格并记录快照 if p, e := s.readDB.Products.WithContext(ctx).Where(s.readDB.Products.ID.Eq(inv.ProductID)).First(); e == nil && p != nil { price = p.Price + // 回写资产价值快照 + s.repo.GetDbW().Exec("UPDATE user_inventory SET value_cents=?, value_source=?, value_snapshot_at=NOW(3) WHERE id=? AND user_id=?", + price, 2, inventoryID, userID) } } @@ -443,8 +456,26 @@ func (s *service) RequestShippings(ctx context.Context, userID int64, inventoryI return addrID, batchNo, success, skipped, failed, nil } - // 7. 批量查询products获取价格(一次查询替代N次) + // 7. 批量查询products获取价格(用于没有快照价格的资产) productMap := make(map[int64]int64) // productID -> price + // 收集需要回写快照的资产 + type valueFix struct { + ID int64 + ValueCents int64 + ValueSource int32 + } + valueFixes := make([]valueFix, 0) + + // 先检查哪些资产没有快照价格 + for _, inv := range validInvs { + if inv.ValueCents <= 0 && inv.ProductID > 0 { + if _, ok := productIDSet[inv.ProductID]; !ok { + productIDSet[inv.ProductID] = struct{}{} + productIDs = append(productIDs, inv.ProductID) + } + } + } + if len(productIDs) > 0 { prods, _ := s.readDB.Products.WithContext(ctx).Where(s.readDB.Products.ID.In(productIDs...)).Find() for _, p := range prods { @@ -455,9 +486,22 @@ func (s *service) RequestShippings(ctx context.Context, userID int64, inventoryI // 8. 单事务批量处理 validIDs := make([]int64, 0, len(validInvs)) err = s.repo.GetDbW().Transaction(func(tx *gorm.DB) error { - // 批量插入shipping_records + // 批量插入shipping_records(使用资产价值快照) for _, inv := range validInvs { - price := productMap[inv.ProductID] + // 优先使用资产价值快照,确保与分解价格一致 + price := inv.ValueCents + valueSource := inv.ValueSource + if price <= 0 && inv.ProductID > 0 { + // 如果没有快照价格,回退到商品当前价格 + price = productMap[inv.ProductID] + valueSource = 2 + // 记录需要回写快照的资产 + valueFixes = append(valueFixes, valueFix{ + ID: inv.ID, + ValueCents: price, + ValueSource: valueSource, + }) + } if errExec := tx.Exec( "INSERT INTO shipping_records (user_id, order_id, order_item_id, inventory_id, product_id, quantity, price, address_id, status, batch_no, remark) VALUES (?,?,?,?,?,?,?,?,?,?,?)", userID, inv.OrderID, 0, inv.ID, inv.ProductID, 1, price, addrID, 1, batchNo, "batch_request_shipping", @@ -467,6 +511,16 @@ func (s *service) RequestShippings(ctx context.Context, userID int64, inventoryI validIDs = append(validIDs, inv.ID) } + // 回写资产价值快照(用于之前没有快照的资产) + for _, fix := range valueFixes { + if err := tx.Exec( + "UPDATE user_inventory SET value_cents=?, value_source=?, value_snapshot_at=NOW(3) WHERE id=? AND user_id=?", + fix.ValueCents, fix.ValueSource, fix.ID, userID, + ).Error; err != nil { + return err + } + } + // 批量更新inventory状态(一次UPDATE替代N次) if len(validIDs) > 0 { if errExec := tx.Exec(