商品库存累加减公共方法修正

This commit is contained in:
Jack 2025-06-13 10:36:38 +08:00
parent c665492023
commit bce489fe55
5 changed files with 407 additions and 205 deletions

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
* Vestibulum commodo. Ut rhoncus gravida arcu.
*/
package com.suisung.mall.common.service;
/**
* 通用服务接口
*/
public interface CommonService {
/**
* 尝试获取分布式锁
*
* @param lockKey 锁的key
* @param expireSeconds 锁过期时间
* @return 锁标识解锁时需用加锁失败返回null
*/
String tryDistributedLock(String lockKey, long expireSeconds);
/**
* 释放分布式锁
*
* @param lockKey 锁的key
* @param lockValue 加锁时返回的value确保只有持有锁的线程能解锁
* @return 是否释放成功
*/
boolean releaseLock(String lockKey, String lockValue);
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
* Vestibulum commodo. Ut rhoncus gravida arcu.
*/
package com.suisung.mall.common.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class CommonServiceImpl {
@Lazy
@Resource
private RedisTemplate redisTemplate;
/**
* 尝试获取分布式锁
* <p>
* 使用实例
* String lockKey = "order:123";
* long expireSeconds = 10;
* String lockValue = commonServiceImpl.tryDistributedLock(lockKey, expireSeconds);
* if (lockValue != null) {
* try {
* // 执行业务逻辑
* } finally {
* commonServiceImpl.releaseLock(lockKey, lockValue);
* }
* } else {
* // 获取锁失败做相应处理
* }
*
* @param lockKey 锁的key
* @param expireSeconds 锁过期时间
* @return 锁标识解锁时需用加锁失败返回null
*/
public String tryDistributedLock(String lockKey, long expireSeconds) {
String lockValue = UUID.randomUUID().toString();
try {
Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, expireSeconds, TimeUnit.SECONDS);
return Boolean.TRUE.equals(success) ? lockValue : null;
} catch (Exception e) {
// 记录异常日志实际项目可用Logger
e.printStackTrace();
return null;
}
}
/**
* 释放分布式锁
*
* @param lockKey 锁的key
* @param lockValue 加锁时返回的value确保只有持有锁的线程能解锁
* @return 是否释放成功
*/
public boolean releaseLock(String lockKey, String lockValue) {
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
try {
DefaultRedisScript<Long> script = new DefaultRedisScript<>(luaScript, Long.class);
Long result = (Long) redisTemplate.execute(script, Collections.singletonList(lockKey), lockValue);
return result != null && result > 0;
} catch (Exception e) {
log.error("释放分布式锁异常key={}, error={}", lockKey, e.getMessage(), e);
return false;
}
}
}

View File

@ -28,6 +28,16 @@ redis:
separator: ":" separator: ":"
expire: 3600 expire: 3600
redisson:
address: redis://@redis.host@:@redis.port@
database: @redis.database@ # Redis 库索引
password: @redis.password@ # Redis 密码
connectionPoolSize: 64 # 连接池大小
connectionMinimumIdleSize: 10 # 最小空闲连接数
idleConnectionTimeout: 10000 # 空闲连接超时时间(毫秒)
connectTimeout: 10000 # 连接超时时间(毫秒)
timeout: 3000 # 命令等待超时时间(毫秒)
baidu: baidu:
map: map:
app_id: 116444176 app_id: 116444176

View File

@ -10,14 +10,12 @@ package com.suisung.mall.shop.sync.service;
import cn.hutool.json.JSONArray; import cn.hutool.json.JSONArray;
import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.api.CommonResult;
import com.suisung.mall.common.modules.sync.StoreDbConfig;
import com.suisung.mall.common.pojo.req.SyncThirdMemberReq; import com.suisung.mall.common.pojo.req.SyncThirdMemberReq;
import com.suisung.mall.common.pojo.res.ThirdApiRes; import com.suisung.mall.common.pojo.res.ThirdApiRes;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -41,6 +39,7 @@ public interface SyncThirdDataService {
/** /**
* 批量保存商品记录 * 批量保存商品记录
*
* @param goodsListJSON * @param goodsListJSON
* @return * @return
*/ */
@ -48,6 +47,7 @@ public interface SyncThirdDataService {
/** /**
* 批量保存会员记录 * 批量保存会员记录
*
* @param memberList * @param memberList
* @return * @return
*/ */
@ -55,6 +55,7 @@ public interface SyncThirdDataService {
/** /**
* 手动触发同步 * 手动触发同步
*
* @param storeId * @param storeId
* @param syncType * @param syncType
* @return * @return
@ -62,42 +63,43 @@ public interface SyncThirdDataService {
CommonResult syncManual(String storeId, Integer syncType); CommonResult syncManual(String storeId, Integer syncType);
/** /**
*
* @param appKey * @param appKey
* @param sign * @param sign
* @param multipartFile * @param multipartFile
* @return * @return
*/ */
ThirdApiRes fileUpload(String appKey, String sign,String path,String syncType, MultipartFile multipartFile); ThirdApiRes fileUpload(String appKey, String sign, String path, String syncType, MultipartFile multipartFile);
/** /**
*
* @param appKey * @param appKey
* @param sign * @param sign
* @param folders * @param folders
* @return * @return
*/ */
void SyncReadSxFileData(String appKey, String sign,String syncType, List<String> folders); void SyncReadSxFileData(String appKey, String sign, String syncType, List<String> folders);
/** /**
* 下载客户端更新包 * 下载客户端更新包
*
* @param primaryKey * @param primaryKey
* @return * @return
*/ */
ResponseEntity<Resource> downloadToClient(String primaryKey,String clienVersionName); ResponseEntity<Resource> downloadToClient(String primaryKey, String clienVersionName);
/** /**
* 获取客户端数据库配置 * 获取客户端数据库配置
*
* @param appKey * @param appKey
* @param sign * @param sign
* @return * @return
*/ */
ThirdApiRes getStoreDbConfig(String appKey,String sign); ThirdApiRes getStoreDbConfig(String appKey, String sign);
/** /**
* 同步商品数据库存到客户端 * 同步商品数据库存到客户端
*
* @param appKey * @param appKey
* @param sign * @param sign
* @return * @return
@ -105,18 +107,33 @@ public interface SyncThirdDataService {
ThirdApiRes getStoreDataRelease(String appKey, String sign); ThirdApiRes getStoreDataRelease(String appKey, String sign);
/** /**
* 存储扣减商品到redis * 保存一个或多个商品刚刚变化的库存到 redis hash 缓存
*
* @param storeData * @param storeData
*/ */
void saveStoreRealeas(Map storeData); // void saveStoreRelease(Map<String, Integer> storeData);
/**
* Redis 中获取商品有变动的库存数据
*
* @return
*/
Map<String, Integer> getProductStockFromRedis();
/**
* 下单或支付后批量累加减商品库存使用 Redis Hash 的原子自增操作保证并发安全
*
* @param stockDeltaMap key 为商品唯一keyvalue 为库存增降量 例如 {"1234567890123": 100, "1234567890124": 50} 库存数为正负整数单位可能是个数或重量
* 数量为正数时库存数增加数量为负数时库存数减少
*/
void incrProductStockToRedis(Map<String, Integer> stockDeltaMap);
/** /**
*
* @param appKey * @param appKey
* @param sign * @param sign
* @param folders * @param folders
* @return * @return
*/ */
ThirdApiRes fileUploadToOss(String appKey, String sign,String syncType, List<String> folders); ThirdApiRes fileUploadToOss(String appKey, String sign, String syncType, List<String> folders);
} }

View File

@ -9,27 +9,22 @@
package com.suisung.mall.shop.sync.service.impl; package com.suisung.mall.shop.sync.service.impl;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil; import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONArray; import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qcloud.cos.model.COSObjectSummary; import com.qcloud.cos.model.COSObjectSummary;
import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.api.CommonResult;
import com.suisung.mall.common.enums.DicEnum; import com.suisung.mall.common.enums.DicEnum;
import com.suisung.mall.common.modules.base.ShopBaseProductBrand; import com.suisung.mall.common.modules.base.ShopBaseProductBrand;
import com.suisung.mall.common.modules.base.ShopBaseProductCategory; import com.suisung.mall.common.modules.base.ShopBaseProductCategory;
import com.suisung.mall.common.modules.sixun.SxSyncGoods; import com.suisung.mall.common.modules.sixun.SxSyncGoods;
import com.suisung.mall.common.modules.sixun.SxSyncVip; import com.suisung.mall.common.modules.sixun.SxSyncVip;
import com.suisung.mall.common.modules.sync.StoreDbConfig; import com.suisung.mall.common.modules.sync.StoreDbConfig;
@ -38,9 +33,7 @@ import com.suisung.mall.common.modules.sync.SyncConfig;
import com.suisung.mall.common.modules.sync.SyncFileLog; import com.suisung.mall.common.modules.sync.SyncFileLog;
import com.suisung.mall.common.pojo.req.SyncThirdMemberReq; import com.suisung.mall.common.pojo.req.SyncThirdMemberReq;
import com.suisung.mall.common.pojo.res.ThirdApiRes; import com.suisung.mall.common.pojo.res.ThirdApiRes;
import com.suisung.mall.common.utils.I18nUtil; import com.suisung.mall.common.utils.I18nUtil;
import com.suisung.mall.common.utils.StringUtils; import com.suisung.mall.common.utils.StringUtils;
import com.suisung.mall.core.web.service.RedisService; import com.suisung.mall.core.web.service.RedisService;
import com.suisung.mall.shop.base.service.ShopBaseProductCategoryService; import com.suisung.mall.shop.base.service.ShopBaseProductCategoryService;
@ -60,15 +53,15 @@ import com.suisung.mall.shop.sixun.utils.FileUtils;
import com.suisung.mall.shop.sync.Utils.ThreadFileUtils; import com.suisung.mall.shop.sync.Utils.ThreadFileUtils;
import com.suisung.mall.shop.sync.keymanage.RedisKey; import com.suisung.mall.shop.sync.keymanage.RedisKey;
import com.suisung.mall.shop.sync.service.*; import com.suisung.mall.shop.sync.service.*;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -76,6 +69,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -85,17 +79,19 @@ import java.nio.file.Paths;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements SyncThirdDataService { public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements SyncThirdDataService {
private static Logger logger = LoggerFactory.getLogger(SyncThirdDataServiceImpl.class); private static final Logger logger = LoggerFactory.getLogger(SyncThirdDataServiceImpl.class);
private final int limitCnt = 300; private final int limitCnt = 300;
private final AtomicLong threadNum = new AtomicLong(0);
@Value("${client.path}") @Value("${client.path}")
public String clientPath; public String clientPath;
@Autowired @Autowired
@ -104,15 +100,12 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
private SyncConfigService syncConfigService; private SyncConfigService syncConfigService;
@Autowired @Autowired
private SxSyncCategoryService sxSyncCategoryService; private SxSyncCategoryService sxSyncCategoryService;
@Autowired @Autowired
private SxSyncGoodsService sxSyncGoodsService; private SxSyncGoodsService sxSyncGoodsService;
@Autowired @Autowired
private SxSyncVipService sxSyncVipService; private SxSyncVipService sxSyncVipService;
@Autowired @Autowired
private ShopNumberSeqService shopNumberSeqService; private ShopNumberSeqService shopNumberSeqService;
private final AtomicLong threadNum=new AtomicLong(0);
@Autowired @Autowired
private SyncFileLogService syncFileLogService; private SyncFileLogService syncFileLogService;
@Autowired @Autowired
@ -121,6 +114,11 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
@Autowired @Autowired
private RedisService redisService; private RedisService redisService;
@Lazy
@Autowired
private RedisTemplate redisTemplate;
@Autowired @Autowired
private StoreDbConfigService storeDbConfigService; private StoreDbConfigService storeDbConfigService;
@ -135,6 +133,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
@Value("#{accountBaseConfigService.getConfig('tengxun_default_dir')}") @Value("#{accountBaseConfigService.getConfig('tengxun_default_dir')}")
private String TENGXUN_DEFA; private String TENGXUN_DEFA;
/** /**
* 批量保存商品的分类 * 批量保存商品的分类
* *
@ -163,7 +162,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!")); return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!"));
} }
int count=baseSaveOrUpdateShopBaseProductCategoryBatch(list,categoryListJSON,storeId); int count = baseSaveOrUpdateShopBaseProductCategoryBatch(list, categoryListJSON, storeId);
Map<String, Integer> resp = new HashMap<>(); Map<String, Integer> resp = new HashMap<>();
resp.put("count", count); resp.put("count", count);
@ -198,7 +197,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!")); return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!"));
} }
int count=baseSaveOrUpdateShopBaseProductBrandBatch(goodBrandList,storeId,brandListJSON); int count = baseSaveOrUpdateShopBaseProductBrandBatch(goodBrandList, storeId, brandListJSON);
Map<String, Integer> resp = new HashMap<>(); Map<String, Integer> resp = new HashMap<>();
resp.put("count", count); resp.put("count", count);
@ -227,7 +226,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!")); return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!"));
} }
int count=baseSaveOrUpdateGoods(goodsListJSON,storeId); int count = baseSaveOrUpdateGoods(goodsListJSON, storeId);
Map<String, Integer> resp = new HashMap<>(); Map<String, Integer> resp = new HashMap<>();
resp.put("count", count); resp.put("count", count);
@ -257,7 +256,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!")); return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!"));
} }
int count =baseSaveOrUpdateMemberBatch(memberList,storeId); int count = baseSaveOrUpdateMemberBatch(memberList, storeId);
Map<String, Integer> resp = new HashMap<>(); Map<String, Integer> resp = new HashMap<>();
resp.put("count", count); resp.put("count", count);
@ -299,13 +298,13 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
//1-品牌2-分类3-商品4-会员 //1-品牌2-分类3-商品4-会员
switch (syncType) { switch (syncType) {
case 1: case 1:
return syncProductBrand(new DataBaseInfo(),storeId);//todo return syncProductBrand(new DataBaseInfo(), storeId);//todo
case 2: case 2:
return syncProductClazz(new DataBaseInfo(),storeId);//todo 测试 return syncProductClazz(new DataBaseInfo(), storeId);//todo 测试
case 3: case 3:
return syncProduct(new DataBaseInfo(),storeId);//todo 没做完 return syncProduct(new DataBaseInfo(), storeId);//todo 没做完
case 4: case 4:
return syncVip(new DataBaseInfo(),storeId);//todo 测试 return syncVip(new DataBaseInfo(), storeId);//todo 测试
} }
return CommonResult.success(); return CommonResult.success();
} }
@ -316,26 +315,26 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
* *
* @return * @return
*/ */
public CommonResult syncProduct(DataBaseInfo dataBaseInfo,String storeId) { public CommonResult syncProduct(DataBaseInfo dataBaseInfo, String storeId) {
int total= sxSyncGoodsService.getGoodsTotal(dataBaseInfo); int total = sxSyncGoodsService.getGoodsTotal(dataBaseInfo);
// 总页数 // 总页数
int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE); int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE);
int syncCount =0; int syncCount = 0;
for (int i = 1; i < pages; i++) { for (int i = 1; i < pages; i++) {
int count=0; int count = 0;
List<SxSyncGoods> sxSyncGoodsList= sxSyncGoodsService.findGoodsListPage(dataBaseInfo,i,pages); List<SxSyncGoods> sxSyncGoodsList = sxSyncGoodsService.findGoodsListPage(dataBaseInfo, i, pages);
//todo 数据转换 //todo 数据转换
List<SxGoosModel> sxGoosModelList= CvtToGoosModel(sxSyncGoodsList); List<SxGoosModel> sxGoosModelList = CvtToGoosModel(sxSyncGoodsList);
if(CollectionUtil.isEmpty(sxSyncGoodsList)){ if (CollectionUtil.isEmpty(sxSyncGoodsList)) {
continue; continue;
} }
count= baseSaveOrUpdateGoods(JSONUtil.parseArray(sxGoosModelList),storeId); count = baseSaveOrUpdateGoods(JSONUtil.parseArray(sxGoosModelList), storeId);
if(count<=0){ if (count <= 0) {
continue; continue;
} }
syncCount+=count; syncCount += count;
} }
logger.info("同步商品的总数为{},成功数量为{}",total,syncCount); logger.info("同步商品的总数为{},成功数量为{}", total, syncCount);
return CommonResult.success(); return CommonResult.success();
} }
@ -345,29 +344,29 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
* *
* @return * @return
*/ */
public CommonResult syncVip(DataBaseInfo dataBaseInfo,String storeId) { public CommonResult syncVip(DataBaseInfo dataBaseInfo, String storeId) {
// 记录总数 // 记录总数
Integer total = sxSyncVipService.getVipMembersTotal(dataBaseInfo); Integer total = sxSyncVipService.getVipMembersTotal(dataBaseInfo);
// 总页数 // 总页数
int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE); int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE);
List<SyncThirdMemberReq> memberList=new ArrayList<>(); List<SyncThirdMemberReq> memberList = new ArrayList<>();
SyncThirdMemberReq syncThirdMemberReq=null; SyncThirdMemberReq syncThirdMemberReq = null;
int syncCount =0; int syncCount = 0;
for (int i = 1; i < pages; i++) { for (int i = 1; i < pages; i++) {
memberList.clear(); memberList.clear();
int count = 0; int count = 0;
syncThirdMemberReq=new SyncThirdMemberReq(); syncThirdMemberReq = new SyncThirdMemberReq();
List<SxSyncVip> sxSyncVipList= sxSyncVipService.findVipMemberPage(dataBaseInfo,i,SxDataDao.PAGESIZE); List<SxSyncVip> sxSyncVipList = sxSyncVipService.findVipMemberPage(dataBaseInfo, i, SxDataDao.PAGESIZE);
//处理数据转换SxSyncVip>SyncThirdMemberReq //处理数据转换SxSyncVip>SyncThirdMemberReq
memberList=ConverList(sxSyncVipList); memberList = ConverList(sxSyncVipList);
memberList.add(syncThirdMemberReq); memberList.add(syncThirdMemberReq);
count=baseSaveOrUpdateMemberBatch(memberList,storeId); count = baseSaveOrUpdateMemberBatch(memberList, storeId);
if (count <= 0) { if (count <= 0) {
continue; continue;
} }
syncCount+=count; syncCount += count;
} }
logger.info("vip会员总共有{}条数据,同步完成{}条",total,syncCount); logger.info("vip会员总共有{}条数据,同步完成{}条", total, syncCount);
return CommonResult.success(); return CommonResult.success();
} }
@ -376,61 +375,63 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
* *
* @return * @return
*/ */
public CommonResult syncProductBrand(DataBaseInfo dataBaseInfo,String storeId) { public CommonResult syncProductBrand(DataBaseInfo dataBaseInfo, String storeId) {
return null; return null;
} }
/** /**
* 同步商品分类 * 同步商品分类
*
* @return * @return
*/ */
public CommonResult syncProductClazz(DataBaseInfo dataBaseInfo,String storeId) { public CommonResult syncProductClazz(DataBaseInfo dataBaseInfo, String storeId) {
// 记录总数 // 记录总数
Integer total = sxSyncCategoryService.getCategoryTotal(dataBaseInfo); Integer total = sxSyncCategoryService.getCategoryTotal(dataBaseInfo);
// 总页数 // 总页数
int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE); int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE);
int syncCount =0; int syncCount = 0;
for (int i = 1; i <= pages; i++) { for (int i = 1; i <= pages; i++) {
int count = 0; int count = 0;
List<SxCategoryModel> list = sxSyncCategoryService.getCategoryByDataBasePage(dataBaseInfo,i,SxDataDao.PAGESIZE); List<SxCategoryModel> list = sxSyncCategoryService.getCategoryByDataBasePage(dataBaseInfo, i, SxDataDao.PAGESIZE);
if (CollUtil.isEmpty(list)) { if (CollUtil.isEmpty(list)) {
continue; continue;
} }
JSONArray categoryListJSON=JSONUtil.parseArray(list); JSONArray categoryListJSON = JSONUtil.parseArray(list);
List<ShopBaseProductCategory> shopBaseProductCategories = JSONUtil.toList(categoryListJSON, ShopBaseProductCategory.class); List<ShopBaseProductCategory> shopBaseProductCategories = JSONUtil.toList(categoryListJSON, ShopBaseProductCategory.class);
if (shopBaseProductCategories == null) { if (shopBaseProductCategories == null) {
logger.info("转换类型为空,类方法为{}","com.suisung.mall.shop.sync.service.impl.SyncThirdDataServiceImpl.syncProductClazz"); logger.info("转换类型为空,类方法为{}", "com.suisung.mall.shop.sync.service.impl.SyncThirdDataServiceImpl.syncProductClazz");
continue; continue;
} }
count = baseSaveOrUpdateShopBaseProductCategoryBatch(shopBaseProductCategories,categoryListJSON,storeId); count = baseSaveOrUpdateShopBaseProductCategoryBatch(shopBaseProductCategories, categoryListJSON, storeId);
if (count <= 0) { if (count <= 0) {
continue; continue;
} }
syncCount+=count; syncCount += count;
} }
logger.info("商品分类总共有{}条数据,同步完成{}条",total,syncCount); logger.info("商品分类总共有{}条数据,同步完成{}条", total, syncCount);
return CommonResult.success(); return CommonResult.success();
} }
/** /**
* 文件上传 * 文件上传
*
* @param appKey * @param appKey
* @param sign * @param sign
* @param page 分页 * @param page 分页
* @param syncType * @param syncType
* @param multipartFile * @param multipartFile
* @return * @return
*/ */
@Override @Override
public ThirdApiRes fileUpload(String appKey, String sign,String page,String syncType, MultipartFile multipartFile) { public ThirdApiRes fileUpload(String appKey, String sign, String page, String syncType, MultipartFile multipartFile) {
if (StrUtil.isBlank(appKey) || StrUtil.isBlank(sign) ) { if (StrUtil.isBlank(appKey) || StrUtil.isBlank(sign)) {
return new ThirdApiRes().fail(1003, I18nUtil._("缺少必要参数!")); return new ThirdApiRes().fail(1003, I18nUtil._("缺少必要参数!"));
} }
// 验签appid必要参数判断 // 验签appid必要参数判断
SyncApp syncAppO = syncAppService.getOne(new LambdaQueryWrapper<SyncApp>() SyncApp syncAppO = syncAppService.getOne(new LambdaQueryWrapper<SyncApp>()
.select(SyncApp::getApp_key, SyncApp::getApp_secret,SyncApp::getStore_id) .select(SyncApp::getApp_key, SyncApp::getApp_secret, SyncApp::getStore_id)
.eq(SyncApp::getApp_key, appKey) .eq(SyncApp::getApp_key, appKey)
.eq(SyncApp::getApp_secret,sign)); .eq(SyncApp::getApp_secret, sign));
if (syncAppO == null) { if (syncAppO == null) {
return new ThirdApiRes().fail(1001, I18nUtil._("签名有误!")); return new ThirdApiRes().fail(1001, I18nUtil._("签名有误!"));
} }
@ -441,13 +442,13 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
return new ThirdApiRes().fail(1001, I18nUtil._("文件不能为空!")); return new ThirdApiRes().fail(1001, I18nUtil._("文件不能为空!"));
} }
byte[] bytes = multipartFile.getBytes(); byte[] bytes = multipartFile.getBytes();
String folder=new FileUtils().getSyncTypeFlag(syncType,clientPath)+storeId+FileUtils.pathSeparator+page+FileUtils.pathSeparator; String folder = new FileUtils().getSyncTypeFlag(syncType, clientPath) + storeId + FileUtils.pathSeparator + page + FileUtils.pathSeparator;
String filName=multipartFile.getOriginalFilename(); String filName = multipartFile.getOriginalFilename();
String filePath= FileUtils.createFolderAndFileUsingFile(folder,filName); String filePath = FileUtils.createFolderAndFileUsingFile(folder, filName);
Path path = Paths.get(filePath); Path path = Paths.get(filePath);
Files.write(path, bytes); Files.write(path, bytes);
logger.info("path-{},parent-{},filename-{},root-{}",path.toString(),path.getParent(),path.getFileName().toString(),path.getRoot()); logger.info("path-{},parent-{},filename-{},root-{}", path, path.getParent(), path.getFileName().toString(), path.getRoot());
// String filaPath=path.toString(); // String filaPath=path.toString();
// if(filePath.contains(":")){ // if(filePath.contains(":")){
// filePath=filePath.substring(filePath.indexOf(":")+1); // filePath=filePath.substring(filePath.indexOf(":")+1);
// } // }
@ -460,12 +461,13 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
// .toUriString(); // .toUriString();
return new ThirdApiRes().success("文件上传成功"); return new ThirdApiRes().success("文件上传成功");
} catch (IOException e) { } catch (IOException e) {
return new ThirdApiRes().fail(500,"文件上传失败"); return new ThirdApiRes().fail(500, "文件上传失败");
} }
} }
/** /**
* 多线程处理文件 * 多线程处理文件
*
* @param appKey * @param appKey
* @param sign * @param sign
* @param syncType * @param syncType
@ -475,24 +477,24 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
@Async @Async
public void SyncReadSxFileData(String appKey, String sign, String syncType, List<String> folders) { public void SyncReadSxFileData(String appKey, String sign, String syncType, List<String> folders) {
SyncApp syncApp = syncAppService.getOne(new LambdaQueryWrapper<SyncApp>() SyncApp syncApp = syncAppService.getOne(new LambdaQueryWrapper<SyncApp>()
.select(SyncApp::getApp_key, SyncApp::getApp_secret,SyncApp::getStore_id) .select(SyncApp::getApp_key, SyncApp::getApp_secret, SyncApp::getStore_id)
.eq(SyncApp::getApp_key, appKey) .eq(SyncApp::getApp_key, appKey)
.eq(SyncApp::getApp_secret,sign)); .eq(SyncApp::getApp_secret, sign));
String storeId = syncApp.getStore_id(); String storeId = syncApp.getStore_id();
Date tenMinutesAgo = Date.from(Instant.now().minus(Duration.ofMinutes(5)));//校准误差 Date tenMinutesAgo = Date.from(Instant.now().minus(Duration.ofMinutes(5)));//校准误差
Date date= DateUtil.date(tenMinutesAgo); Date date = DateUtil.date(tenMinutesAgo);
if(null==syncApp.getStore_id()|| syncApp.getStore_id().isEmpty()){ if (null == syncApp.getStore_id() || syncApp.getStore_id().isEmpty()) {
logger.info("商品id为空"); logger.info("商品id为空");
return; return;
} }
if(folders==null||folders.isEmpty()){ if (folders == null || folders.isEmpty()) {
logger.info("没有商品数据"); logger.info("没有商品数据");
return; return;
} }
List<String> newFolders=new ArrayList<>(); List<String> newFolders = new ArrayList<>();
folders.forEach(page->{ folders.forEach(page -> {
String newfolder=new FileUtils().getSyncTypeFlag(syncType,clientPath)+storeId+FileUtils.pathSeparator+page+FileUtils.pathSeparator; String newfolder = new FileUtils().getSyncTypeFlag(syncType, clientPath) + storeId + FileUtils.pathSeparator + page + FileUtils.pathSeparator;
newFolders.add(newfolder); newFolders.add(newfolder);
}); });
@ -505,35 +507,35 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
ExecutorService executor = Executors.newFixedThreadPool(6); ExecutorService executor = Executors.newFixedThreadPool(6);
List<Future<?>> futures = new ArrayList<>(); List<Future<?>> futures = new ArrayList<>();
// 提交任务 // 提交任务
AtomicInteger success= new AtomicInteger(); AtomicInteger success = new AtomicInteger();
AtomicInteger fails= new AtomicInteger(); AtomicInteger fails = new AtomicInteger();
List<String> failFolders=new ArrayList<>(); List<String> failFolders = new ArrayList<>();
List<String> failMessage=new ArrayList<>(); List<String> failMessage = new ArrayList<>();
shopBaseProductCategoryService.getCategoryListByStoreId(storeId); shopBaseProductCategoryService.getCategoryListByStoreId(storeId);
for (int i = 0; i < newFolders.size(); i++) { for (int i = 0; i < newFolders.size(); i++) {
final int taskId = i; final int taskId = i;
threadNum.incrementAndGet(); threadNum.incrementAndGet();
futures.add(executor.submit(() -> { futures.add(executor.submit(() -> {
int count = 0;//失败重试机制当失败重试一次再次失败则记录到数据库中 int count = 0;//失败重试机制当失败重试一次再次失败则记录到数据库中
while (true){ while (true) {
count++; count++;
String taskName=newFolders.get(taskId); String taskName = newFolders.get(taskId);
String fileName="good_"+(taskId+1)+".txt"; String fileName = "good_" + (taskId + 1) + ".txt";
JSONArray jsonArray=new ThreadFileUtils().processFolder(taskName,newFolders.get(taskId)); JSONArray jsonArray = new ThreadFileUtils().processFolder(taskName, newFolders.get(taskId));
try { try {
baseSaveOrUpdateGoodsBatch(jsonArray,storeId); baseSaveOrUpdateGoodsBatch(jsonArray, storeId);
success.getAndIncrement(); success.getAndIncrement();
threadNum.decrementAndGet(); threadNum.decrementAndGet();
return "成功" + taskId; return "成功" + taskId;
}catch (Exception e){ } catch (Exception e) {
if(count<2){ if (count < 2) {
//Thread.sleep(100); //Thread.sleep(100);
continue; continue;
} }
fails.getAndIncrement(); fails.getAndIncrement();
failFolders.add(newFolders.get(taskId)+fileName); failFolders.add(newFolders.get(taskId) + fileName);
failMessage.add(taskId+"_"+e.getMessage()); failMessage.add(taskId + "_" + e.getMessage());
return "失败"+newFolders.get(taskId); return "失败" + newFolders.get(taskId);
} }
} }
})); }));
@ -553,13 +555,13 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
shopBaseProductCategoryService.clearCategoryCache(storeId); shopBaseProductCategoryService.clearCategoryCache(storeId);
shopProductSpecItemService.clearExistItem(Integer.valueOf(storeId)); shopProductSpecItemService.clearExistItem(Integer.valueOf(storeId));
baseProductSpecService.clearShopBaseProductSpecMap(Integer.valueOf(storeId)); baseProductSpecService.clearShopBaseProductSpecMap(Integer.valueOf(storeId));
List<SyncFileLog> syncFileLogs=new ArrayList<>(); List<SyncFileLog> syncFileLogs = new ArrayList<>();
for (int i = 0; i < failFolders.size(); i++) { for (int i = 0; i < failFolders.size(); i++) {
String path=failFolders.get(i); String path = failFolders.get(i);
String taskId=failMessage.get(i).split("_")[0]; String taskId = failMessage.get(i).split("_")[0];
SyncFileLog syncFileLog=new SyncFileLog(); SyncFileLog syncFileLog = new SyncFileLog();
syncFileLog.setSyncType(syncType); syncFileLog.setSyncType(syncType);
syncFileLog.setFileName(path.substring(path.lastIndexOf(FileUtils.pathSeparator)+1)); syncFileLog.setFileName(path.substring(path.lastIndexOf(FileUtils.pathSeparator) + 1));
syncFileLog.setSyncStatus(DicEnum.FAILED.getCode()); syncFileLog.setSyncStatus(DicEnum.FAILED.getCode());
syncFileLog.setSyncTaskId(taskId); syncFileLog.setSyncTaskId(taskId);
syncFileLog.setSyncStoreId(storeId); syncFileLog.setSyncStoreId(storeId);
@ -569,56 +571,56 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
syncFileLog.setTargetSystem(DicEnum.SOURCE_SYSTEM_TYPE_SELF.getCode()); syncFileLog.setTargetSystem(DicEnum.SOURCE_SYSTEM_TYPE_SELF.getCode());
syncFileLogs.add(syncFileLog); syncFileLogs.add(syncFileLog);
} }
if(CollUtil.isNotEmpty(syncFileLogs)){ if (CollUtil.isNotEmpty(syncFileLogs)) {
syncFileLogService.saveBatch(syncFileLogs,syncFileLogs.size()); syncFileLogService.saveBatch(syncFileLogs, syncFileLogs.size());
} }
//todo 定时清理文件建议用服务器脚本 //todo 定时清理文件建议用服务器脚本
logger.info("执行成功{}个文件,失败{}个文件",success,fails); logger.info("执行成功{}个文件,失败{}个文件", success, fails);
logger.info("同步商品数据执行结束"); logger.info("同步商品数据执行结束");
//更新当前的获取时间用户客户端获取 //更新当前的获取时间用户客户端获取
try { try {
QueryWrapper<StoreDbConfig> storeDbConfigQueryWrapper = new QueryWrapper<>(); QueryWrapper<StoreDbConfig> storeDbConfigQueryWrapper = new QueryWrapper<>();
storeDbConfigQueryWrapper.eq("store_id", storeId); storeDbConfigQueryWrapper.eq("store_id", storeId);
StoreDbConfig storeDbConfig=storeDbConfigService.getOne(storeDbConfigQueryWrapper); StoreDbConfig storeDbConfig = storeDbConfigService.getOne(storeDbConfigQueryWrapper);
if(ObjectUtil.isNotEmpty(storeDbConfig)){ if (ObjectUtil.isNotEmpty(storeDbConfig)) {
storeDbConfig.setRefreshTime(date); storeDbConfig.setRefreshTime(date);
storeDbConfigService.saveOrUpdate(storeDbConfig); storeDbConfigService.saveOrUpdate(storeDbConfig);
} }
}catch (RuntimeException e){ } catch (RuntimeException e) {
logger.error("同步时间失败"+e.getMessage()); logger.error("同步时间失败" + e.getMessage());
} }
} }
@Override @Override
public ResponseEntity<Resource> downloadToClient(String primaryKey,String clienVersionName) { public ResponseEntity<Resource> downloadToClient(String primaryKey, String clienVersionName) {
logger.info("primaryKey:{}",primaryKey); logger.info("primaryKey:{}", primaryKey);
boolean checked= syncAppService.checkPrimaryKey(primaryKey); boolean checked = syncAppService.checkPrimaryKey(primaryKey);
if(checked){ if (checked) {
String tempFilePath=System.getProperty("user.home"); String tempFilePath = System.getProperty("user.home");
String clientJarPath=""; String clientJarPath = "";
COSObjectSummary cosObjectSummary= ossService.findNewestFile(FileUtils.OSSCLIENTFOLDER); COSObjectSummary cosObjectSummary = ossService.findNewestFile(FileUtils.OSSCLIENTFOLDER);
String jarFileName=cosObjectSummary.getKey().substring(cosObjectSummary.getKey().lastIndexOf("/")+1); String jarFileName = cosObjectSummary.getKey().substring(cosObjectSummary.getKey().lastIndexOf("/") + 1);
if(jarFileName.equals(clienVersionName+".jar")){ if (jarFileName.equals(clienVersionName + ".jar")) {
logger.error("没有版本更新"); logger.error("没有版本更新");
return ResponseEntity.ok() return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM) .contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, .header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=error.txt") "attachment; filename=error.txt")
.header("error","noVersion") .header("error", "noVersion")
.body(new ByteArrayResource(clienVersionName.getBytes(StandardCharsets.UTF_8))); .body(new ByteArrayResource(clienVersionName.getBytes(StandardCharsets.UTF_8)));
}else { } else {
String filePath= cosObjectSummary.getKey(); String filePath = cosObjectSummary.getKey();
clientJarPath= ossService.download(filePath,tempFilePath+FileUtils.pathSeparator+filePath); clientJarPath = ossService.download(filePath, tempFilePath + FileUtils.pathSeparator + filePath);
} }
if(StringUtils.isNotEmpty(clientJarPath)){ if (StringUtils.isNotEmpty(clientJarPath)) {
// 构建文件路径 // 构建文件路径
Path filePath = Paths.get(clientJarPath); Path filePath = Paths.get(clientJarPath);
File file = filePath.toFile(); File file = filePath.toFile();
// 检查文件是否存在 // 检查文件是否存在
if (!file.exists()) { if (!file.exists()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND) return ResponseEntity.status(HttpStatus.NOT_FOUND)
.header("error" ,"noFile") .header("error", "noFile")
.body(new ByteArrayResource(jarFileName.getBytes())); .body(new ByteArrayResource(jarFileName.getBytes()));
} }
// 创建Resource对象 // 创建Resource对象
@ -629,13 +631,13 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
contentType = Files.probeContentType(filePath); contentType = Files.probeContentType(filePath);
} catch (IOException e) { } catch (IOException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND) return ResponseEntity.status(HttpStatus.NOT_FOUND)
.header("error" ,"500") .header("error", "500")
.body(new ByteArrayResource(jarFileName.getBytes())); .body(new ByteArrayResource(jarFileName.getBytes()));
} }
if (contentType == null) { if (contentType == null) {
contentType = "application/octet-stream"; contentType = "application/octet-stream";
} }
// redisService.set(RedisKey.SXCLIENTKEYVERSION,jarFileName); // redisService.set(RedisKey.SXCLIENTKEYVERSION,jarFileName);
// 构建响应 // 构建响应
return ResponseEntity.ok() return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType)) .contentType(MediaType.parseMediaType(contentType))
@ -646,69 +648,120 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
} }
logger.info("!校验失败"); logger.info("!校验失败");
return ResponseEntity.status(HttpStatus.NOT_FOUND) return ResponseEntity.status(HttpStatus.NOT_FOUND)
.header("error" ,"noValid") .header("error", "noValid")
.body(new ByteArrayResource("version".getBytes())); .body(new ByteArrayResource("version".getBytes()));
} }
@Override @Override
public ThirdApiRes getStoreDbConfig(String appKey,String sign) { public ThirdApiRes getStoreDbConfig(String appKey, String sign) {
if (StrUtil.isBlank(appKey) || StrUtil.isBlank(sign) ) { if (StrUtil.isBlank(appKey) || StrUtil.isBlank(sign)) {
return new ThirdApiRes().fail(1003, I18nUtil._("缺少必要参数!")); return new ThirdApiRes().fail(1003, I18nUtil._("缺少必要参数!"));
} }
// 验签appid必要参数判断 // 验签appid必要参数判断
SyncApp syncAppO = syncAppService.getOne(new LambdaQueryWrapper<SyncApp>() SyncApp syncAppO = syncAppService.getOne(new LambdaQueryWrapper<SyncApp>()
.select(SyncApp::getApp_key, SyncApp::getApp_secret,SyncApp::getStore_id) .select(SyncApp::getApp_key, SyncApp::getApp_secret, SyncApp::getStore_id)
.eq(SyncApp::getApp_key, appKey) .eq(SyncApp::getApp_key, appKey)
.eq(SyncApp::getApp_secret,sign)); .eq(SyncApp::getApp_secret, sign));
if (syncAppO == null) { if (syncAppO == null) {
return new ThirdApiRes().fail(1001, I18nUtil._("签名有误!")); return new ThirdApiRes().fail(1001, I18nUtil._("签名有误!"));
} }
String storeId = syncAppO.getStore_id(); String storeId = syncAppO.getStore_id();
QueryWrapper<StoreDbConfig> queryWrapper = new QueryWrapper<>(); QueryWrapper<StoreDbConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("store_id", storeId); queryWrapper.eq("store_id", storeId);
queryWrapper.eq("has_start",DicEnum.YESORNO_1.getCode()); queryWrapper.eq("has_start", DicEnum.YESORNO_1.getCode());
StoreDbConfig storeDbConfig= storeDbConfigService.getOne(queryWrapper); StoreDbConfig storeDbConfig = storeDbConfigService.getOne(queryWrapper);
if (storeDbConfig == null) { if (storeDbConfig == null) {
return new ThirdApiRes().fail(1003, I18nUtil._("服务器配置缺少配置信息!")); return new ThirdApiRes().fail(1003, I18nUtil._("服务器配置缺少配置信息!"));
} }
return new ThirdApiRes().success("成功",storeDbConfig); return new ThirdApiRes().success("成功", storeDbConfig);
} }
@Override @Override
public ThirdApiRes getStoreDataRelease(String appKey, String sign) { public ThirdApiRes getStoreDataRelease(String appKey, String sign) {
if (StrUtil.isBlank(appKey) || StrUtil.isBlank(sign) ) { if (StrUtil.isBlank(appKey) || StrUtil.isBlank(sign)) {
return new ThirdApiRes().fail(1003, I18nUtil._("缺少必要参数!")); return new ThirdApiRes().fail(1003, I18nUtil._("缺少必要参数!"));
} }
// 验签appid必要参数判断 // 验签appid必要参数判断
SyncApp syncAppO = syncAppService.getOne(new LambdaQueryWrapper<SyncApp>() SyncApp syncAppO = syncAppService.getOne(new LambdaQueryWrapper<SyncApp>()
.select(SyncApp::getApp_key, SyncApp::getApp_secret,SyncApp::getStore_id) .select(SyncApp::getApp_key, SyncApp::getApp_secret, SyncApp::getStore_id)
.eq(SyncApp::getApp_key, appKey) .eq(SyncApp::getApp_key, appKey)
.eq(SyncApp::getApp_secret,sign)); .eq(SyncApp::getApp_secret, sign));
if (syncAppO == null) { if (syncAppO == null) {
return new ThirdApiRes().fail(1001, I18nUtil._("签名有误!")); return new ThirdApiRes().fail(1001, I18nUtil._("签名有误!"));
} }
Object obRst= redisService.get(RedisKey.STOREDATARELEASE);//商品库存扣减
Map storeDataResultMap=new HashMap(); // Object obRst = redisService.get(RedisKey.STOREDATARELEASE);//商品库存扣减
if(obRst!=null){ // Map storeDataResultMap = new HashMap();
Map resultMap=(Map)obRst; // if (obRst != null) {
Set<Map.Entry> sme= resultMap.entrySet(); // Map resultMap = (Map) obRst;
storeDataResultMap= sme.stream().filter(m->!(m.getValue().equals((double)0))).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); // Set<Map.Entry> sme = resultMap.entrySet();
// storeDataResultMap = sme.stream().filter(m -> !(m.getValue().equals((double) 0))).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
// }
Map<String, Integer> storeDataResultMap = getProductStockFromRedis();
return new ThirdApiRes().success("success", storeDataResultMap);
}
// @Override
// public void saveStoreRelease(Map storeData) {
// // RMK: 这样写存在严重的线程安全问题可能会导致数据丢失或覆盖
// // 改成 redis 原子级别的 hash 类别存储
// if (CollectionUtil.isEmpty(storeData)) {
// return;
// }
// Object obRst = redisService.get(RedisKey.STOREDATARELEASE);
// if (obRst != null) {
// Map map = (Map) obRst;
// map.putAll(storeData);
// redisService.set(RedisKey.STOREDATARELEASE, map);
// } else {
// redisService.set(RedisKey.STOREDATARELEASE, storeData);
// }
// }
@Override
public Map<String, Integer> getProductStockFromRedis() {
try {
// Redis 获取 hash 结构的所有键值对
Map<Object, Object> redisHash = redisTemplate.opsForHash().entries(RedisKey.STOREDATARELEASE);
if (redisHash == null || redisHash.isEmpty()) {
return Collections.emptyMap();
}
// 转换为 Map<String, Integer>
return redisHash.entrySet().stream()
.collect(Collectors.toMap(
entry -> String.valueOf(entry.getKey()),
entry -> Convert.toInt(entry.getValue(), 0) // 转换失败时默认为 0
));
} catch (Exception e) {
logger.error("从 Redis 获取商品库存失败: {}", e.getMessage(), e);
return Collections.emptyMap();
} }
return new ThirdApiRes().success("success",storeDataResultMap);
} }
@Override @Override
public void saveStoreRealeas(Map storeData) { public void incrProductStockToRedis(Map<String, Integer> stockDeltaMap) {
if(CollectionUtil.isEmpty(storeData)){ // 校验参数避免空指针
if (CollectionUtil.isEmpty(stockDeltaMap)) {
return; return;
} }
Object obRst= redisService.get(RedisKey.STOREDATARELEASE);
if(obRst!=null){ for (Map.Entry<String, Integer> entry : stockDeltaMap.entrySet()) {
Map map=(Map)obRst; String productKey = entry.getKey();
map.putAll(storeData); Integer delta = entry.getValue();
redisService.set(RedisKey.STOREDATARELEASE,map); if (StrUtil.isBlank(productKey) || delta == null) {
}else { continue;
redisService.set(RedisKey.STOREDATARELEASE,storeData); }
try {
// 使用 Redis HINCRBY 保证原子性和高性能
redisTemplate.opsForHash().increment(RedisKey.STOREDATARELEASE, productKey, delta);
} catch (Exception e) {
logger.error("库存累加失败productKey={}, delta={}, error={}", productKey, delta, e.getMessage(), e);
}
} }
} }
@ -717,79 +770,81 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
* 压缩商家数据并上传cos * 压缩商家数据并上传cos
* 保存商店数据 * 保存商店数据
* "E:\\data\\uploaded\\goods\\2025\\6\\6\\1\\2" * "E:\\data\\uploaded\\goods\\2025\\6\\6\\1\\2"
*
* @param path * @param path
*/ */
public void upLoadZipToOss(String path){ public void upLoadZipToOss(String path) {
File file=new File(path); File file = new File(path);
File parentFile=null; File parentFile = null;
if(!file.exists()){//存在则取本地,不存在下载cos的数据 if (!file.exists()) {//存在则取本地,不存在下载cos的数据
logger.info("没有同步数据上传"); logger.info("没有同步数据上传");
return; return;
} }
parentFile=file.getParentFile(); parentFile = file.getParentFile();
String filePath=parentFile.getPath(); String filePath = parentFile.getPath();
filePath=filePath.replaceAll("\\\\","/"); filePath = filePath.replaceAll("\\\\", "/");
String folderName=parentFile.getName(); String folderName = parentFile.getName();
String parentFolderName=parentFile.getParentFile().getPath().replaceAll("\\\\","/"); String parentFolderName = parentFile.getParentFile().getPath().replaceAll("\\\\", "/");
String localPath=parentFolderName+"/"+folderName+".zip"; String localPath = parentFolderName + "/" + folderName + ".zip";
ZipUtil.zip(filePath,localPath,true); ZipUtil.zip(filePath, localPath, true);
if(parentFolderName.contains(":")){ if (parentFolderName.contains(":")) {
parentFolderName=parentFolderName.substring(filePath.indexOf(":")+1); parentFolderName = parentFolderName.substring(filePath.indexOf(":") + 1);
} }
String cosFileName =TENGXUN_DEFA.concat("/").concat("sync").concat(parentFolderName).concat("/").concat(folderName+".zip"); String cosFileName = TENGXUN_DEFA.concat("/").concat("sync").concat(parentFolderName).concat("/").concat(folderName + ".zip");
ossService.uploadObject4OSS(new File(localPath),cosFileName); ossService.uploadObject4OSS(new File(localPath), cosFileName);
} }
/** /**
* 压缩商家数据并上传cos * 压缩商家数据并上传cos
* 保存商店数据 * 保存商店数据
* "E:\\data\\uploaded\\goods\\2025\\6\\6\\1\\2" * "E:\\data\\uploaded\\goods\\2025\\6\\6\\1\\2"
*
* @param path * @param path
*/ */
public void dowloadAndUnZip(String path){ public void dowloadAndUnZip(String path) {
File file=new File(path); File file = new File(path);
File parentFile=null; File parentFile = null;
if(file.exists()){//存在则取本地,不存在下载cos的数据 if (file.exists()) {//存在则取本地,不存在下载cos的数据
logger.info("没有同步数据下载"); logger.info("没有同步数据下载");
}else { } else {
parentFile=file.getParentFile(); parentFile = file.getParentFile();
String ossFilePath=parentFile.getParentFile().getPath(); String ossFilePath = parentFile.getParentFile().getPath();
String fileName=parentFile.getName()+".zip"; String fileName = parentFile.getName() + ".zip";
String parentFolderName=parentFile.getParent().replaceAll("\\\\","/"); String parentFolderName = parentFile.getParent().replaceAll("\\\\", "/");
String localPath=parentFolderName+"/"+fileName; String localPath = parentFolderName + "/" + fileName;
ossFilePath=ossFilePath.replaceAll("\\\\","/")+"/"+fileName; ossFilePath = ossFilePath.replaceAll("\\\\", "/") + "/" + fileName;
if(ossFilePath.contains(":")){ if (ossFilePath.contains(":")) {
ossFilePath=ossFilePath.substring(ossFilePath.indexOf(":")+1); ossFilePath = ossFilePath.substring(ossFilePath.indexOf(":") + 1);
} }
ossFilePath=TENGXUN_DEFA.concat("/").concat("sync").concat(ossFilePath); ossFilePath = TENGXUN_DEFA.concat("/").concat("sync").concat(ossFilePath);
String dowlowFilePath=ossService.download(ossFilePath,localPath); String dowlowFilePath = ossService.download(ossFilePath, localPath);
File localFile=new File(dowlowFilePath); File localFile = new File(dowlowFilePath);
ZipUtil.unzip(dowlowFilePath,localFile.getParent(), CharsetUtil.CHARSET_GBK); ZipUtil.unzip(dowlowFilePath, localFile.getParent(), CharsetUtil.CHARSET_GBK);
} }
} }
@Override @Override
public ThirdApiRes fileUploadToOss(String appKey, String sign, String syncType, List<String> folders) { public ThirdApiRes fileUploadToOss(String appKey, String sign, String syncType, List<String> folders) {
SyncApp syncApp = syncAppService.getOne(new LambdaQueryWrapper<SyncApp>() SyncApp syncApp = syncAppService.getOne(new LambdaQueryWrapper<SyncApp>()
.select(SyncApp::getApp_key, SyncApp::getApp_secret,SyncApp::getStore_id) .select(SyncApp::getApp_key, SyncApp::getApp_secret, SyncApp::getStore_id)
.eq(SyncApp::getApp_key, appKey) .eq(SyncApp::getApp_key, appKey)
.eq(SyncApp::getApp_secret,sign)); .eq(SyncApp::getApp_secret, sign));
String storeId = syncApp.getStore_id(); String storeId = syncApp.getStore_id();
Date tenMinutesAgo = Date.from(Instant.now().minus(Duration.ofMinutes(5)));//校准误差 Date tenMinutesAgo = Date.from(Instant.now().minus(Duration.ofMinutes(5)));//校准误差
Date date= DateUtil.date(tenMinutesAgo); Date date = DateUtil.date(tenMinutesAgo);
if(null==syncApp.getStore_id()|| syncApp.getStore_id().isEmpty()){ if (null == syncApp.getStore_id() || syncApp.getStore_id().isEmpty()) {
logger.info("商品id为空"); logger.info("商品id为空");
return new ThirdApiRes().fail(250,"商品id为空"); return new ThirdApiRes().fail(250, "商品id为空");
} }
if(folders==null||folders.isEmpty()){ if (folders == null || folders.isEmpty()) {
logger.info("没有商品数据"); logger.info("没有商品数据");
return new ThirdApiRes().fail(250,"没有商品数据"); return new ThirdApiRes().fail(250, "没有商品数据");
} }
String newfolder=new FileUtils().getSyncTypeFlag(syncType,clientPath)+storeId+FileUtils.pathSeparator+folders.get(0)+FileUtils.pathSeparator; String newfolder = new FileUtils().getSyncTypeFlag(syncType, clientPath) + storeId + FileUtils.pathSeparator + folders.get(0) + FileUtils.pathSeparator;
upLoadZipToOss(newfolder);//上传文件到cos upLoadZipToOss(newfolder);//上传文件到cos
return new ThirdApiRes().success("上传成功"); return new ThirdApiRes().success("上传成功");
} }