Browse Source

1. 批量清理脏数据

zhangwl 1 day ago
parent
commit
cccf1f9687

+ 19 - 3
src/main/java/zs/payment/controller/EweiShopGoodsController.java

@@ -43,13 +43,29 @@ public class EweiShopGoodsController {
      * 一键退还
      * @return
      */
-    @GetMapping("/deleteAll")
-    public Result deleteAll() {
-        commodityProcureService.deleteAll();
+    @GetMapping("/deleteAll/{uniacid}")
+    public Result deleteAll(@PathVariable Integer uniacid) {
+        commodityProcureService.deleteAll(uniacid);
         return Result.success();
     }
 
     /**
+     * 批量对账清理:扫描463已删除但云商城/MongoDB仍残留的供货商品,级联清理残留数据
+     */
+    @GetMapping("/reconcileDeleted")
+    public Result reconcileDeleted() {
+        return commodityProcureService.reconcileDeletedSuppliedGoods();
+    }
+
+    /**
+     * 批量清理 gd_desc 脏数据:扫描463供货中 gd_desc 含无效 nfs 链接的商品,级联清理残留数据
+     */
+    @GetMapping("/cleanupInvalidGdDesc")
+    public Result cleanupInvalidGdDesc() {
+        return commodityProcureService.cleanupInvalidGdDescSuppliedGoods();
+    }
+
+    /**
      * 供货/停止供货:把本地商品推送到云商城,或停止供货
      * issupply=1: 供货  issupply=0: 停止供货
      * 加入Redis分布式锁防止用户多次点击重复提交

+ 11 - 1
src/main/java/zs/payment/service/commodity/CommodityProcureService.java

@@ -35,5 +35,15 @@ public interface CommodityProcureService {
      */
     Result processSupplyItem(Long goodsId, Integer issupply, Integer cate, Integer uniacid, Integer storeid);
 
-    void deleteAll();
+    void deleteAll(Integer uniacid);
+
+    /**
+     * 批量对账清理:扫描463已删除但云商城/MongoDB仍残留的供货商品,级联清理残留数据
+     */
+    Result reconcileDeletedSuppliedGoods();
+
+    /**
+     * 批量清理 gd_desc 脏数据:扫描463供货中 gd_desc 含无效 nfs 链接的 SPU,按取消供货逻辑级联清理
+     */
+    Result cleanupInvalidGdDescSuppliedGoods();
 }

+ 145 - 5
src/main/java/zs/payment/service/commodity/impl/CommodityProcureServiceImpl.java

@@ -1266,13 +1266,26 @@ public class CommodityProcureServiceImpl implements CommodityProcureService {
 
         // 取消供货
         yunMallGoodsService.saveGoods(gdId.toString(), 2);
-        // 停止供货时,处理其他进货该商品的商城
-        // 1. 查询所有相同gd_id的其他进货商品(不包括当前商品,未删除,商品类型为1)
+        // 停止供货时,处理其他进货该商品的商城(排除当前商品)
+        cleanupProcuredGoodsByGdId(gdId, item.getId());
+    }
+
+    /**
+     * 清理所有进货了指定 gd_id 的本地商城商品及其关联数据(规格/选项/参数)
+     * 有订单则软删除,无订单则物理删除。
+     *
+     * @param gdId          云商城商品ID
+     * @param excludeGoodsId 需要排除的商品ID(如停止供货时排除当前商品);为 null 时不排除
+     */
+    private void cleanupProcuredGoodsByGdId(Integer gdId, Long excludeGoodsId) {
+        // 1. 查询所有相同gd_id的进货商品(未删除,商品类型为1)
         LambdaQueryWrapper<ImsEweiShopGoods> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(ImsEweiShopGoods::getGdId, gdId)
-                .ne(ImsEweiShopGoods::getId, item.getId())
                 .eq(ImsEweiShopGoods::getDeleted, 0)
                 .eq(ImsEweiShopGoods::getCommoditytype, 1);
+        if (excludeGoodsId != null) {
+            queryWrapper.ne(ImsEweiShopGoods::getId, excludeGoodsId);
+        }
         List<ImsEweiShopGoods> otherGoods = goodsService.list(queryWrapper);
 
         // 2. 逐个处理
@@ -1303,7 +1316,134 @@ public class CommodityProcureServiceImpl implements CommodityProcureService {
                 log.info("软删除其他进货商品 goodsId={}", goodsId);
             }
         }
+    }
+
+    /**
+     * 清理某个云商城商品(gd_id)在供货时产生的全部残留数据(对账清理专用)。
+     * 复用停止供货逻辑:SPU置is_del=1(保留)、SKU物理删除、货架/积分/悦店下架、
+     * 图片物理删除、MongoDB Goods物理删除、其他商城进货商品清理。
+     *
+     * @param gdId    云商城商品ID
+     * @param storeid 供货店铺ID(取自SPU的shop_id)
+     */
+    private void cleanupOrphanCloudData(Integer gdId, Integer storeid) {
+        // 1. SPU下架并软删除(is_on_sale=2, is_del=1,保留记录)
+        b2cGoodsStatusMapper.updateOnSaleStatus(gdId, 2);
+
+        // 2. 更新货架商品数量 + 软删除货架商品
+        updateShelfCountAndDelete(storeid, gdId);
+
+        // 3. 删除SKU + 清除Redis缓存
+        deleteSkusAndClearCache(gdId);
+
+        // 4. 下架悦店分销商品
+        b2cGoodsStatusMapper.offShelfYdDistributionGoods(gdId);
+
+        // 5. 下架积分商品(SPU + SKU + 商城)
+        b2cGoodsStatusMapper.offShelfJfGoodsSpu(gdId);
+        b2cGoodsStatusMapper.offShelfJfGoodsSku(gdId);
+        b2cGoodsStatusMapper.offShelfJfMallGoods(gdId);
+
+        // 6. 物理删除商品图片(补充清理,停止供货逻辑未覆盖)
+        b2cGoodsImgInOnlineService.remove(new LambdaQueryWrapper<B2cGoodsImgInOnline>()
+                .eq(B2cGoodsImgInOnline::getGdId, gdId));
+
+        // 7. 物理删除MongoDB生态商品
+        yunMallGoodsService.saveGoods(String.valueOf(gdId), 2);
+
+        // 8. 清理所有进货了该商品的其他商城(无排除项)
+        cleanupProcuredGoodsByGdId(gdId, null);
 
+        log.info("对账清理云商城残留数据完成 gdId={}, storeid={}", gdId, storeid);
+    }
+
+    @Override
+    public Result reconcileDeletedSuppliedGoods() {
+        // 1. 查询463供货产生的、仍处于有效状态(is_del=0)的SPU记录
+        List<B2cGoodsSpuInOnline> suppliedSpus = b2cGoodsSpuInOnlineService.list(
+                new LambdaQueryWrapper<B2cGoodsSpuInOnline>()
+                        .eq(B2cGoodsSpuInOnline::getUniacid, 463)
+                        .eq(B2cGoodsSpuInOnline::getIsDel, 0)
+                        .eq(B2cGoodsSpuInOnline::getCreateUsername, "企悦接口"));
+        if (suppliedSpus.isEmpty()) {
+            return Result.success("对账完成,无供货商品需要检查");
+        }
+
+        // 2. 收集goodsid,批量查询主库中仍存活(deleted=0)的商品ID集合
+        Set<Integer> goodsIds = suppliedSpus.stream()
+                .map(B2cGoodsSpuInOnline::getGoodsid)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        Set<Long> aliveGoodsIds = new HashSet<>();
+        if (!goodsIds.isEmpty()) {
+            List<ImsEweiShopGoods> aliveGoods = goodsService.list(new LambdaQueryWrapper<ImsEweiShopGoods>()
+                    .in(ImsEweiShopGoods::getId, goodsIds)
+                    .eq(ImsEweiShopGoods::getDeleted, 0));
+            for (ImsEweiShopGoods g : aliveGoods) {
+                aliveGoodsIds.add(g.getId());
+            }
+        }
+
+        // 3. goodsid 不在存活集合中的 SPU 判定为孤儿,逐个清理
+        int success = 0;
+        int fail = 0;
+        for (B2cGoodsSpuInOnline spu : suppliedSpus) {
+            Integer goodsid = spu.getGoodsid();
+            if (goodsid != null && aliveGoodsIds.contains(Long.valueOf(goodsid))) {
+                continue;
+            }
+            try {
+                cleanupOrphanCloudData(spu.getGdId(), spu.getShopId());
+                success++;
+            } catch (Exception e) {
+                fail++;
+                log.error("对账清理残留商品失败 gdId={}, goodsid={}", spu.getGdId(), goodsid, e);
+            }
+        }
+
+        log.info("对账清理完成,成功清理{}个残留商品,失败{}个", success, fail);
+        return Result.success("对账完成,清理 " + success + " 个残留商品,失败 " + fail + " 个");
+    }
+
+    @Override
+    public Result cleanupInvalidGdDescSuppliedGoods() {
+        final int BATCH = 500;
+        int success = 0, fail = 0;
+        Integer cursorGdId = 0;                 // 按主键 gd_id 游标推进,避免 is_del 翻转导致的偏移漂移/死循环
+        while (true) {
+            List<B2cGoodsSpuInOnline> batch = b2cGoodsSpuInOnlineService.list(
+                    new LambdaQueryWrapper<B2cGoodsSpuInOnline>()
+                            .eq(B2cGoodsSpuInOnline::getUniacid, 470)
+                            .eq(B2cGoodsSpuInOnline::getIsDel, 0)
+                            .like(B2cGoodsSpuInOnline::getGdDesc, "http://mall.zhongsou.com/nfs/")
+                            .gt(B2cGoodsSpuInOnline::getGdId, cursorGdId)
+                            .orderByAsc(B2cGoodsSpuInOnline::getGdId)
+                            .last("limit " + BATCH));
+            if (batch.isEmpty()) {
+                break;
+            }
+            for (B2cGoodsSpuInOnline spu : batch) {
+                cursorGdId = spu.getGdId();      // 无论成功失败都推进游标,保证每条只处理一次
+                try {
+                    cleanupOrphanCloudData(spu.getGdId(), spu.getShopId());
+                    // 遵循 stopSupply 语义:重置463源商品 issupply=0(保留商品本身)
+                    Integer sourceGoodsId = spu.getGoodsid();
+                    if (sourceGoodsId != null) {
+                        ImsEweiShopGoods update = new ImsEweiShopGoods();
+                        update.setId(Long.valueOf(sourceGoodsId));
+                        update.setIssupply(0);
+                        goodsService.updateById(update);
+                    }
+                    success++;
+                } catch (Exception e) {
+                    fail++;
+                    log.error("清理gd_desc脏数据失败 gdId={}, goodsid={}", spu.getGdId(), spu.getGoodsid(), e);
+                }
+            }
+            log.info("gd_desc脏数据清理进度:成功{},失败{},cursorGdId={}", success, fail, cursorGdId);
+        }
+        log.info("gd_desc脏数据清理完成,成功{},失败{}", success, fail);
+        return Result.success("gd_desc脏数据清理完成,清理 " + success + " 条,失败 " + fail + " 条");
     }
 
     /**
@@ -1427,12 +1567,12 @@ public class CommodityProcureServiceImpl implements CommodityProcureService {
 
 
     @Override
-    public void deleteAll() {
+    public void deleteAll(Integer uniacid) {
 
         LambdaQueryWrapper<ImsEweiShopGoods> queryWrapper = new LambdaQueryWrapper<>();
         //ne(ImsEweiShopGoods::getMerchid,0)  代表是进货的商品
         queryWrapper.ne(ImsEweiShopGoods::getMerchid,0)
-            .eq(ImsEweiShopGoods::getUniacid, 470)
+            .eq(ImsEweiShopGoods::getUniacid, uniacid)
             .eq(ImsEweiShopGoods::getDeleted, 0);
 //        queryWrapper.last("limit 1");
         List<ImsEweiShopGoods> otherGoods = goodsService.list(queryWrapper);