From 749464c03e2e18dda53f637d80ecfe2ba59181c9 Mon Sep 17 00:00:00 2001 From: win Date: Wed, 11 Mar 2026 16:51:27 +0800 Subject: [PATCH] =?UTF-8?q?revert:=20=E7=A7=BB=E9=99=A4=E8=BD=AC=E8=B5=A0?= =?UTF-8?q?=E8=B5=84=E4=BA=A7=E7=A6=81=E6=AD=A2=E5=85=91=E6=8D=A2=E7=A7=AF?= =?UTF-8?q?=E5=88=86=E7=9A=84=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 经数据核实,转赠后兑换积分属于合法行为(资产转赠后归接收方所有)。 并发漏洞虽然产生了重复转赠/发货记录,但实际经济损失为 0 元: - 18 个重复发货资产中,没有任何一个真正被两方都发了货 - 没有任何资产被重复兑换积分 保留前两个并发修复(SELECT FOR UPDATE + RowsAffected 检查), 回退第三个业务限制(禁止转赠资产兑换积分)。 --- internal/service/user/address_share.go | 35 -------------------------- 1 file changed, 35 deletions(-) diff --git a/internal/service/user/address_share.go b/internal/service/user/address_share.go index 987a4df..4405f2e 100755 --- a/internal/service/user/address_share.go +++ b/internal/service/user/address_share.go @@ -569,16 +569,6 @@ func (s *service) RedeemInventoryToPoints(ctx context.Context, userID int64, inv return 0, err } - // 校验转赠来源:通过转赠获得的资产不允许兑换积分(防薅积分漏洞) - // 使用写库查询,避免主从延迟导致校验被绕过 - var transferCnt int64 - if err := s.repo.GetDbW().Raw("SELECT COUNT(*) FROM user_inventory_transfers WHERE inventory_id = ? AND to_user_id = ?", inventoryID, userID).Scan(&transferCnt).Error; err != nil { - return 0, err - } - if transferCnt > 0 { - return 0, fmt.Errorf("transfer_inventory_cannot_redeem") - } - valueCents := inv.ValueCents valueSource := inv.ValueSource valueSnapshotAt := inv.ValueSnapshotAt @@ -659,31 +649,6 @@ func (s *service) RedeemInventoriesToPoints(ctx context.Context, userID int64, i return 0, fmt.Errorf("no_valid_inventory") } - // 3.5 排除通过转赠获得的资产(防薅积分漏洞) - // 使用写库查询,避免主从延迟导致校验被绕过 - invIDs := make([]int64, 0, len(invList)) - for _, inv := range invList { - invIDs = append(invIDs, inv.ID) - } - var transferredInvs []*model.UserInventoryTransfers - if err := s.repo.GetDbW().Raw("SELECT * FROM user_inventory_transfers WHERE inventory_id IN ? AND to_user_id = ?", invIDs, userID).Scan(&transferredInvs).Error; err != nil { - return 0, err - } - transferredSet := make(map[int64]struct{}, len(transferredInvs)) - for _, t := range transferredInvs { - transferredSet[t.InventoryID] = struct{}{} - } - filteredInvList := make([]*model.UserInventory, 0, len(invList)) - for _, inv := range invList { - if _, isTransferred := transferredSet[inv.ID]; !isTransferred { - filteredInvList = append(filteredInvList, inv) - } - } - if len(filteredInvList) == 0 { - return 0, fmt.Errorf("transfer_inventory_cannot_redeem") - } - invList = filteredInvList - // 4. 按资产快照计算总积分,缺失快照时回退商品价格并回写 productIDs := make([]int64, 0, len(invList)) productIDSet := make(map[int64]struct{})