From c1b872c8a2b9d554fdc6c9409a707c2bd03be66b Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Sat, 11 Oct 2025 14:22:08 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E6=89=A3=E5=87=8F=E4=B8=AD=E9=97=B4=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sync/ProductQuantityConsumption.java | 52 ++++++ .../impl/ShopOrderBaseServiceImpl.java | 6 +- .../controller/SyncThirdDataController.java | 9 ++ .../mall/shop/sync/keymanage/RedisKey.java | 2 +- .../ProductQuantityConsumptionMapper.java | 11 ++ .../ProductQuantityConsumptionService.java | 8 + .../sync/service/SyncThirdDataService.java | 11 +- ...ProductQuantityConsumptionServiceImpl.java | 18 +++ .../impl/SyncThirdDataServiceImpl.java | 152 ++++++++++++++---- sql/shop/dev/20250929_ddl.sql | 12 ++ 10 files changed, 244 insertions(+), 37 deletions(-) create mode 100644 mall-common/src/main/java/com/suisung/mall/common/modules/sync/ProductQuantityConsumption.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/ProductQuantityConsumptionMapper.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ProductQuantityConsumptionService.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductQuantityConsumptionServiceImpl.java create mode 100644 sql/shop/dev/20250929_ddl.sql diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/sync/ProductQuantityConsumption.java b/mall-common/src/main/java/com/suisung/mall/common/modules/sync/ProductQuantityConsumption.java new file mode 100644 index 00000000..ddfdc6a7 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/sync/ProductQuantityConsumption.java @@ -0,0 +1,52 @@ +package com.suisung.mall.common.modules.sync; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 同步消费中间表,同步数据时保存数据到这个表中,消费完更新已消费,证明数据已经同步给思迅客户端 + */ +@Data +@TableName("product_quantity_consumption") +@ApiModel("商品数量消费表") +public class ProductQuantityConsumption { + @TableId(value = "consume_id", type = IdType.INPUT) + @ApiModelProperty("自定义主键ID") + private String consumeId; + + @TableField(value = "order_id",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty("订单编号") + private String orderId; + + @TableField(value = "product_number", updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty("商品编号") + private String productNumber; + + @ApiModelProperty("数量(正数表示入库/增加,负数表示出库/减少)") + @TableField(value = "quantity",updateStrategy = FieldStrategy.NOT_EMPTY) + private BigDecimal quantity; + + @ApiModelProperty("消费状态:0-未消费,1-已消费") + @TableField(value = "status",updateStrategy = FieldStrategy.NOT_EMPTY) + private Integer status; + + @TableField(value = "store_id",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty("店铺ID") + private Integer storeId; + + @TableField(value = "update_time",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty("创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @TableField(value = "update_time",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty(value = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java index d77a68ae..d31786eb 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java @@ -3303,7 +3303,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>(); - stockDeltaMap.put(item_src_id, -order_item_quantity); + stockDeltaMap.put(item_src_id+"_"+order_item_row.getOrder_id(), -order_item_quantity); syncThirdDataService.incrProductStockToRedis(stockDeltaMap); } } @@ -4226,7 +4226,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>(); - stockDeltaMap.put(Convert.toStr(shopProductItem.getItem_src_id()), order_item_quantity); + stockDeltaMap.put(shopProductItem.getItem_src_id()+"_"+order_item_row.getOrder_id(), order_item_quantity); syncThirdDataService.incrProductStockToRedis(stockDeltaMap); } } @@ -7244,7 +7244,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>(); - stockDeltaMap.put(Convert.toStr(item_src_id), -cart_quantity); + stockDeltaMap.put(item_src_id+"_"+order_id, -cart_quantity); syncThirdDataService.incrProductStockToRedis(stockDeltaMap); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java index 86e26bb8..f5716fcc 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java @@ -137,6 +137,15 @@ public class SyncThirdDataController { return syncThirdDataService.getStoreDataRelease(appKey,sign); } + @ApiOperation(value = "扣完库存之后做确认", notes = "扣完库存之后做确认") + @RequestMapping(value = "/syncStoreDataReleaseResponse", method = RequestMethod.POST) + public ThirdApiRes syncStoreDataReleaseResponse(@RequestBody List consumIds, + @RequestParam String appKey, + @RequestParam String sign) { + syncThirdDataService.getStoreDataReleaseResponse(appKey,sign,consumIds); + return new ThirdApiRes().success("请求成功!"); + } + @ApiOperation(value = "通知上传商品文件到cos", notes = "通知上传商品文件到cos") @RequestMapping(value = "/uploudToCos", method = RequestMethod.POST) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java index d89a5068..64f746ef 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java @@ -4,7 +4,7 @@ public class RedisKey { //public static final String SXCLIENTKEYVERSION="sxclientKey:version";//客户端版本 - public static final String STOREDATARELEASE="storedata:release"; + public static final String STOREDATARELEASE="shopQuality:release"; public static final String STOREDATAPRODUCTMAPING="storedata:productMaping"; diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/ProductQuantityConsumptionMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/ProductQuantityConsumptionMapper.java new file mode 100644 index 00000000..bf59ed3a --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/ProductQuantityConsumptionMapper.java @@ -0,0 +1,11 @@ + + +package com.suisung.mall.shop.sync.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.sync.ProductQuantityConsumption; +import org.springframework.stereotype.Repository; + +@Repository +public interface ProductQuantityConsumptionMapper extends BaseMapper { +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ProductQuantityConsumptionService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ProductQuantityConsumptionService.java new file mode 100644 index 00000000..0ba0e209 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/ProductQuantityConsumptionService.java @@ -0,0 +1,8 @@ +package com.suisung.mall.shop.sync.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.suisung.mall.common.modules.sync.ProductQuantityConsumption; + +public interface ProductQuantityConsumptionService extends IService { + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java index 878c0a0d..8857053c 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java @@ -12,6 +12,7 @@ import cn.hutool.json.JSONArray; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.modules.base.ShopBaseProductBrand; import com.suisung.mall.common.modules.base.ShopBaseProductCategory; +import com.suisung.mall.common.modules.sync.ProductQuantityConsumption; import com.suisung.mall.common.pojo.req.SyncThirdMemberReq; import com.suisung.mall.common.pojo.res.ThirdApiRes; import org.springframework.core.io.Resource; @@ -110,6 +111,14 @@ public interface SyncThirdDataService { */ ThirdApiRes getStoreDataRelease(String appKey, String sign); + /** + * 扣减完库存之后做确认,确认完成更新队列表为已消费 + * @param appKey + * @param sign + * @return + */ + ThirdApiRes getStoreDataReleaseResponse(String appKey, String sign,List consumIds); + /** * 保存一个或多个商品刚刚变化的库存到 redis hash 缓存 * @@ -122,7 +131,7 @@ public interface SyncThirdDataService { * * @return */ - Map getProductStockFromRedis(String storeId); + List getProductStockFromRedis(String storeId,Map redisHash); /** * 下单或支付后,批量累加减商品库存,使用 Redis Hash 的原子自增操作,保证并发安全 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductQuantityConsumptionServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductQuantityConsumptionServiceImpl.java new file mode 100644 index 00000000..1b87dc10 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductQuantityConsumptionServiceImpl.java @@ -0,0 +1,18 @@ +package com.suisung.mall.shop.sync.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.suisung.mall.common.modules.sync.ProductQuantityConsumption; + +import com.suisung.mall.shop.sync.mapper.ProductQuantityConsumptionMapper; +import com.suisung.mall.shop.sync.service.ProductQuantityConsumptionService; +import org.springframework.stereotype.Service; + +import org.springframework.transaction.annotation.Transactional; + + +@Service +@lombok.extern.slf4j.Slf4j +@Transactional +public class ProductQuantityConsumptionServiceImpl extends ServiceImpl implements ProductQuantityConsumptionService { + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java index 28d94b10..fa39feb8 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java @@ -13,10 +13,7 @@ import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateUtil; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.CharsetUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.ZipUtil; +import cn.hutool.core.util.*; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; @@ -27,6 +24,7 @@ import com.google.gson.GsonBuilder; import com.qcloud.cos.model.COSObjectSummary; import com.suisung.mall.common.adapter.BigDecimalTypeAdapter; import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.api.ResultCode; import com.suisung.mall.common.api.StateCode; import com.suisung.mall.common.enums.DicEnum; import com.suisung.mall.common.exception.ApiException; @@ -42,10 +40,7 @@ import com.suisung.mall.common.modules.sixun.SxSyncGoods; import com.suisung.mall.common.modules.sixun.SxSyncVip; import com.suisung.mall.common.modules.store.ShopStoreActivityBase; import com.suisung.mall.common.modules.store.ShopStoreActivityItem; -import com.suisung.mall.common.modules.sync.StoreDbConfig; -import com.suisung.mall.common.modules.sync.SyncApp; -import com.suisung.mall.common.modules.sync.SyncConfig; -import com.suisung.mall.common.modules.sync.SyncFileLog; +import com.suisung.mall.common.modules.sync.*; import com.suisung.mall.common.pojo.req.SyncThirdMemberReq; import com.suisung.mall.common.pojo.res.ThirdApiRes; import com.suisung.mall.common.utils.I18nUtil; @@ -77,6 +72,7 @@ import com.suisung.mall.shop.sync.dto.BrandModel; import com.suisung.mall.shop.sync.keymanage.RedisKey; import com.suisung.mall.shop.sync.service.*; import io.seata.spring.annotation.GlobalTransactional; +import org.apache.commons.collections4.ListUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -180,10 +176,12 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements @Autowired private ProductMappingService productMappingService; - @Autowired private ShopProductIndexService shopProductIndexService; + @Autowired + private ProductQuantityConsumptionService productQuantityConsumptionService; + /** * 批量保存商品的分类 * @@ -780,18 +778,91 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements if (syncAppO == null) { return new ThirdApiRes().fail(1001, I18nUtil._("签名有误!")); } + //持久化保存数据到数据库队列 + String key=RedisKey.STOREDATARELEASE+":"+syncAppO.getStore_id(); + Map redisHash = redisTemplate.opsForHash().entries(key); + List productQuantityConsumptionList=getProductStockFromRedis(syncAppO.getStore_id(),redisHash); + List productQuantityConsumptions=new ArrayList<>(); + if (!productQuantityConsumptionList.isEmpty()) { + boolean result= productQuantityConsumptionService.saveBatch(productQuantityConsumptionList,productQuantityConsumptionList.size()); + if(result){ + Set fields = redisHash.keySet(); + redisTemplate.opsForHash().delete(key, fields.toArray());//清除持久化数据 + } + } + //查询未消费数据 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("store_id", syncAppO.getStore_id()); + queryWrapper.eq("status",0);//未消费数据 + productQuantityConsumptions=productQuantityConsumptionService.list(queryWrapper); + return new ThirdApiRes().success("success", productQuantityConsumptions); + } -// Object obRst = redisService.get(RedisKey.STOREDATARELEASE);//商品库存扣减 -// Map storeDataResultMap = new HashMap(); -// if (obRst != null) { -// Map resultMap = (Map) obRst; -// Set sme = resultMap.entrySet(); -// storeDataResultMap = sme.stream().filter(m -> !(m.getValue().equals((double) 0))).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); -// } + @Override + public ThirdApiRes getStoreDataReleaseResponse(String appKey, String sign,List consumIds) { + if (StrUtil.isBlank(appKey) || StrUtil.isBlank(sign)) { + logger.info("缺少必要参数!"); + } + Gson gson = new Gson(); + String jsonStr = gson.toJson(consumIds); + SyncApp syncApp = syncAppService.checkAppSign(appKey, sign, jsonStr); // 验签、appid,必要参数判断 + if (syncApp == null) { + logger.info("签名有误!"); + return new ThirdApiRes().fail(250,"签名有误"); + } + if(consumIds!=null){ + ExecutorService executor = Executors.newFixedThreadPool(6); + List> futures = new ArrayList<>(); + Integer pages=CommonUtil.getPagesCount(consumIds.size(),limitCnt); + List> pagesPatition = ListUtils.partition(consumIds, limitCnt); + AtomicInteger success = new AtomicInteger(); + AtomicInteger fails = new AtomicInteger(); + for (int i = 1; i <=pages ; i++) { + int finalI = i; + int finalI1 = i; + futures.add(executor.submit(() -> { + try { + List pageData = pagesPatition.get(finalI - 1); + productQuantityConsumptionPageSave(pageData); + success.getAndIncrement(); + return "成功执行"+ finalI1; + }catch (Exception e){ + fails.getAndIncrement(); + return "执行失败:"+finalI+":"+e.getMessage(); + } + })); + } + // 等待所有任务完成 + for (Future future : futures) { + try { + logger.info("任务结果: {}", future.get()); + } catch (Exception e) { + logger.error("任务执行异常: {}", e.getMessage()); + } + } + executor.shutdown(); + logger.info("总任务数据:{}", pagesPatition.size()); + logger.info("成功数据:{}", success.get()); + logger.info("失败数据:{}", fails.get()); + } + return new ThirdApiRes().success("success"); + } - Map storeDataResultMap = getProductStockFromRedis(syncAppO.getStore_id()); - - return new ThirdApiRes().success("success", storeDataResultMap); + /** + * 更新消费状态 + * @param consumIds + */ + private void productQuantityConsumptionPageSave(List consumIds){ + List productQuantityConsumptionList=new ArrayList<>(); + for (String consumeId : consumIds) { + ProductQuantityConsumption productQuantityConsumption=new ProductQuantityConsumption(); + productQuantityConsumption.setConsumeId(consumeId); + productQuantityConsumption.setStatus(1); + productQuantityConsumptionList.add(productQuantityConsumption); + } + if(!productQuantityConsumptionList.isEmpty()){ + productQuantityConsumptionService.updateBatchById(productQuantityConsumptionList,productQuantityConsumptionList.size()); + } } // @Override @@ -813,23 +884,34 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements @Override - public Map getProductStockFromRedis(String storeId) { + public List getProductStockFromRedis(String storeId,Map redisHash) { try { // 从 Redis 获取 hash 结构的所有键值对 - String key=RedisKey.STOREDATARELEASE+":"+storeId; - Map redisHash = redisTemplate.opsForHash().entries(key); if (redisHash.isEmpty()) { - return Collections.emptyMap(); + return Collections.emptyList(); } - // 转换为 Map - return redisHash.entrySet().stream() - .collect(Collectors.toMap( - entry -> String.valueOf(entry.getKey()), - entry -> Convert.toDouble(entry.getValue(), 0.00) // 转换失败时默认为 0 - )); + // 转换为 list + List productQuantityConsumptionList=new ArrayList<>(); + redisHash.forEach((k, v)->{ + ProductQuantityConsumption productQuantityConsumption=new ProductQuantityConsumption(); + String[] productKeyArrys=k.split("_"); + if(productKeyArrys.length!=2){ + return; + } + String productNumber=productKeyArrys[0]; + String orderId=productKeyArrys[1]; + productQuantityConsumption.setConsumeId(IdUtil.getSnowflakeNextIdStr()); + productQuantityConsumption.setOrderId(orderId); + productQuantityConsumption.setProductNumber(productNumber); + productQuantityConsumption.setQuantity(Convert.toBigDecimal(v)); + productQuantityConsumption.setStoreId(Integer.valueOf(storeId)); + productQuantityConsumption.setStatus(0); + productQuantityConsumptionList.add(productQuantityConsumption); + }); + return productQuantityConsumptionList; } catch (Exception e) { logger.error("从 Redis 获取商品库存失败: {}", e.getMessage(), e); - return Collections.emptyMap(); + return Collections.emptyList(); } } @@ -847,9 +929,15 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements if (StrUtil.isBlank(productKey) || delta == null) { continue; } + String[] productKeyArrys=productKey.split("_"); + if(productKeyArrys.length!=2){ + continue; + } try { + String item_src_id=productKeyArrys[0]; + String orderId=productKeyArrys[1]; QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("item_src_id", productKey); + queryWrapper.eq("item_src_id", item_src_id); List shopProductItems=shopProductItemService.list(queryWrapper); if(shopProductItems==null || shopProductItems.isEmpty()){ continue; @@ -871,7 +959,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements String name = Convert.toStr(item.get("name")); BigDecimal itemQuaryty = getBigDecimal(delta, name); // 使用 Redis 的 HINCRBY 保证原子性和高性能 - redisTemplate.opsForHash().increment(key, itemId, itemQuaryty.doubleValue()); + redisTemplate.opsForHash().increment(key, itemId+"_"+orderId, itemQuaryty.doubleValue()); } catch (Exception e) { logger.error("库存累计失败,productKey={}, delta={}, error={}", productKey, delta, e.getMessage(), e); } diff --git a/sql/shop/dev/20250929_ddl.sql b/sql/shop/dev/20250929_ddl.sql new file mode 100644 index 00000000..5ec58add --- /dev/null +++ b/sql/shop/dev/20250929_ddl.sql @@ -0,0 +1,12 @@ +CREATE TABLE `product_quantity_consumption` ( + `consume_id` varchar(32) NOT NULL COMMENT '自定义主键ID', + `product_number` varchar(50) NOT NULL COMMENT '商品编号', + `order_id` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT '' COMMENT '订单编号', + `quantity` decimal(15,3) NOT NULL COMMENT '数量(正数表示入库/增加,负数表示出库/减少)', + `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '消费状态:0-未消费,1-已消费', + `store_id` int(20) NOT NULL COMMENT '店铺ID', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`consume_id`), + KEY `idx_shop_product` (`store_id`, `product_number`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品数量消费表'; From 3e7c7f8cd9b27ca22b52ffef6c0165c06e51f5ab Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Sat, 11 Oct 2025 14:23:57 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E6=89=A3=E5=87=8F=E4=B8=AD=E9=97=B4=E8=A1=A8=E7=9A=84=E6=B6=88?= =?UTF-8?q?=E8=B4=B9=EF=BC=8C=E4=BF=9D=E8=AF=81=E5=BA=93=E5=AD=98=E7=9A=84?= =?UTF-8?q?=E6=89=A3=E5=87=8F=E4=B8=80=E8=87=B4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/small/client/dao/SxDataDao.java | 46 ++++++++++++++++--- .../dto/ProductQuantityConsumptionDto.java | 42 +++++++++++++++++ .../client/service/imp/SxDataServiceImp.java | 19 +++++++- 3 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 client/src/main/java/com/small/client/dto/ProductQuantityConsumptionDto.java diff --git a/client/src/main/java/com/small/client/dao/SxDataDao.java b/client/src/main/java/com/small/client/dao/SxDataDao.java index 88fb3f51..5329c644 100644 --- a/client/src/main/java/com/small/client/dao/SxDataDao.java +++ b/client/src/main/java/com/small/client/dao/SxDataDao.java @@ -3,16 +3,25 @@ package com.small.client.dao; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.google.gson.Gson; import com.small.client.Utils.BigDecimalFormatter; +import com.small.client.Utils.CommonUtil; +import com.small.client.Utils.HttpUtils; import com.small.client.dto.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; import java.math.BigDecimal; import java.math.RoundingMode; import java.sql.*; import java.util.*; +import java.util.stream.Collectors; /** * 考虑到每个思迅软件都是自己的数据,所以采用动态获取的方式获取数据 @@ -22,7 +31,10 @@ import java.util.*; @Service @Slf4j public class SxDataDao extends BaseDao{ - + @Autowired + private RestTemplate restTemplate; + @Value("${remoteIp}") + private String remoteIp; private final static String T_BD_ITEM_CLS="t_bd_item_cls";//商品分类 private final static String T_BD_ITEM_INFO="t_bd_item_info b";//商品表 private final static String T_RM_VIP_INFO="t_rm_vip_info";//会员表 @@ -458,7 +470,8 @@ public class SxDataDao extends BaseDao{ * @param dataBaseInfo * @param map */ - public void updateStoreData(DataBaseInfo dataBaseInfo, Map map){ + public void updateStoreData(DataBaseInfo dataBaseInfo, Map map,List productQuantityConsumptionDtoList, + CommentModel commentModel){ if(CollectionUtil.isEmpty(map)){ log.info("同步数据为空"); return; @@ -482,8 +495,12 @@ public class SxDataDao extends BaseDao{ int count = 0; Set sme=map.entrySet(); for (Map.Entry entry : sme) { + BigDecimal stock_qty= (BigDecimal) entry.getValue(); + if(stock_qty.compareTo(BigDecimal.ZERO)==0){ + continue; + } ps.setString(1, (String) entry.getKey()); - ps.setDouble(2, (double) entry.getValue()); + ps.setDouble(2, stock_qty.doubleValue()); ps.setTimestamp(3, timestamp); ps.addBatch(); // 添加至批处理 count++; @@ -495,22 +512,37 @@ public class SxDataDao extends BaseDao{ } // 执行剩余未满 batchSize 的批次 int[] remainingCounts = ps.executeBatch(); + + List consumIds=productQuantityConsumptionDtoList + .stream() + .map(ProductQuantityConsumptionDto::getConsumeId) + .collect(Collectors.toList()); + Gson gson=new Gson(); + String jsonString=gson.toJson(consumIds); + JSONArray jsonArray = JSONUtil.parseArray(jsonString); + String sign= CommonUtil.generateOpenSign(jsonString,commentModel.getAppKey(),commentModel.getAppId()); + String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_POST_STORE_DATA_RESPONSE + +"?appKey="+commentModel.getAppKey() + +"&sign="+sign, jsonArray); + if (!HttpUtils.SUCCESSCODE.equals(code)) { + throw new Exception("服务器异常"); + } log.info("剩余批次更新数: {}", Arrays.toString(remainingCounts)); conn.commit(); // 最终提交事务 log.info("批量更新完成,总记录数: {}" , count); //baseUpdateImBrancStock(dataBaseInfo); - } catch (SQLException e) { + } catch (Exception e) { conn.rollback(); // 出错时回滚整个事务 - e.printStackTrace(); + log.info("业务失败:: {}", e.getMessage()); } } catch (SQLException e) { - e.printStackTrace(); + log.info("sql失败:: {}", e.getMessage()); }finally { if(conn!=null){ try { conn.close(); } catch (SQLException e) { - throw new RuntimeException(e); + log.info("最后sql失败:: {}", e.getMessage()); } } } diff --git a/client/src/main/java/com/small/client/dto/ProductQuantityConsumptionDto.java b/client/src/main/java/com/small/client/dto/ProductQuantityConsumptionDto.java new file mode 100644 index 00000000..70cb361e --- /dev/null +++ b/client/src/main/java/com/small/client/dto/ProductQuantityConsumptionDto.java @@ -0,0 +1,42 @@ +package com.small.client.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 同步消费中间表,同步数据时保存数据到这个表中,消费完更新已消费,证明数据已经同步给思迅客户端 + */ +@Data +@ApiModel("商品数量消费表") +public class ProductQuantityConsumptionDto { + @ApiModelProperty("自定义主键ID") + private String consumeId; + + @ApiModelProperty("订单编号") + private String orderId; + + @ApiModelProperty("商品编号") + private String productNumber; + + @ApiModelProperty("数量(正数表示入库/增加,负数表示出库/减少)") + private BigDecimal quantity; + + @ApiModelProperty("消费状态:0-未消费,1-已消费") + private Integer status; + + @ApiModelProperty("店铺ID") + private Integer storeId; + + @ApiModelProperty("创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @ApiModelProperty(value = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java b/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java index ebd3a212..afb3e193 100644 --- a/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java +++ b/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java @@ -218,6 +218,7 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_MEMBER +"?appKey="+commentModel.getAppKey() +"&sign="+sign,memberList);//todo 后期改为文件传输 + if (!HttpUtils.SUCCESSCODE.equals(code)) { continue; } @@ -739,8 +740,22 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService +"?appKey="+commentModel.getAppKey() +"&sign="+commentModel.getAppId(),JSONObject.class); if(null!=jsonObject.get("result")){ - Map map=(Map)jsonObject.get("result"); - sxDataDao.updateStoreData(dataBaseInfo,map); + // Map map=(Map)jsonObject.get("result"); + List productQuantityConsumptionDtoList = + JSONUtil.toList(jsonObject.getStr("result"),ProductQuantityConsumptionDto.class); + if(!productQuantityConsumptionDtoList.isEmpty()){ + Map map = productQuantityConsumptionDtoList.stream() + .collect(Collectors.groupingBy( + ProductQuantityConsumptionDto::getProductNumber, + Collectors.reducing( + BigDecimal.ZERO, + ProductQuantityConsumptionDto::getQuantity, + BigDecimal::add + ) + )); + sxDataDao.updateStoreData(dataBaseInfo,map,productQuantityConsumptionDtoList,commentModel); + } + } } From 13ec66d045cc98687667258f3182e31da11dca2d Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Sat, 11 Oct 2025 15:50:12 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=8E=A8=E9=80=81?= =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E5=BE=AE=E4=BF=A1=E5=8F=B7=E5=8E=BB=E9=87=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../message/service/impl/ShopMessageTemplateServiceImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java index fff857fb..65ac9764 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java @@ -762,7 +762,9 @@ public class ShopMessageTemplateServiceImpl extends BaseServiceImpl> futures = new ArrayList<>(); for (int i = 1; i <= pages; i++) { - List finalList = accountService.getAllBindPage(CommonConstant.BIND_SUB_TMPL_SKILL, i, BATCH_SIZE); + List finalList = accountService.getAllBindPage(CommonConstant.BIND_SUB_TMPL_SKILL, i, BATCH_SIZE) + .stream().filter(com.suisung.mall.common.utils.CommonUtil.distinctByKey(AccountUserBindConnect::getBind_openid)) + .collect(Collectors.toList()); int finalI = i; futures.add(executor.submit(() -> { finalList.forEach(accountUserBindConnect -> { From 2758b5ac622b4d9e5baef8ec653af7d91aa8b594 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Mon, 13 Oct 2025 14:48:43 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=80=9D=E8=BF=85=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E5=AF=B9=E6=8E=A5=E6=AC=A1=E6=97=A5=E6=9B=B4=E6=96=B0=E5=BA=93?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/sync/service/impl/SyncBaseThirdSxAbstract.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncBaseThirdSxAbstract.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncBaseThirdSxAbstract.java index e1fc6441..8b0450ee 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncBaseThirdSxAbstract.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncBaseThirdSxAbstract.java @@ -811,6 +811,7 @@ public abstract class SyncBaseThirdSxAbstract{ shopProductBase.setProduct_add_time(currentDate.getTime()); shopProductBase.setProduct_unit_price(BigDecimal.valueOf(jsonObj.getDouble("retail_price"))); String isSpecial=jsonObj.getStr("isSpecial",DicEnum.YESORNO_0.getCode()); + String is_open_automatic=DicEnum.YESORNO_0.getCode(); if(ObjectUtil.equals(isSpecial,DicEnum.YESORNO_0.getCode())||ObjectUtil.equals(isSpecial,DicEnum.YESORNO_1.getCode())){ shopProductBase.setIs_special(isSpecial);//是否特价商品 特价商品需要手动上架 todo 是否要开发定时上架功能 }else { @@ -830,6 +831,7 @@ public abstract class SyncBaseThirdSxAbstract{ shopProductBase.setUnit_name(jsonObj.getStr("unit")); shopProductBase.setProduct_state_id(StateCode.PRODUCT_STATE_OFF_THE_SHELF_UNCHECK); shopProductBase.setUnit_price(BigDecimal.valueOf(jsonObj.getDouble("retail_price"))); + is_open_automatic=DicEnum.YESORNO_1.getCode(); }else { shopProductBase.setShop_weight(stock); if(null!= jsonObj.getJSONArray("product_spec")){ @@ -944,6 +946,10 @@ public abstract class SyncBaseThirdSxAbstract{ shopProductItem.setItem_is_default(1); shopProductItem.setItem_enable(StateCode.PRODUCT_STATE_NORMAL); + shopProductItem.setIs_open_automatic(is_open_automatic); + if(is_open_automatic.equals(DicEnum.YESORNO_1.getCode())){ + shopProductItem.setAutomatic(stock.intValue()); + } //shopProductImage ShopProductImage shopProductImage = new ShopProductImage(); shopProductImage.setStore_id(storeIdInt);