商品同步相关逻辑问题修复

This commit is contained in:
liyj 2025-07-01 14:38:26 +08:00
parent 696027f3e8
commit 96620ba505
11 changed files with 223 additions and 52 deletions

View File

@ -57,6 +57,12 @@
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version> <!-- 使用最新版本 -->
</dependency>
</dependencies>
<!-- 指定仓库为阿里云与阿帕奇 -->

View File

@ -33,7 +33,7 @@ public class DynamicTaskScheduler {
this.sxDataService = sxDataService;
}
@PostConstruct
//@PostConstruct
public void initTasks() {
refreshTasks();
// 每5分钟检查一次数据库更新

View File

@ -12,7 +12,14 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.apache.tomcat.util.codec.binary.Base64;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@Slf4j
public class CommonUtil {
private final static String apiUrl = "http://4ei8850868ux.vicp.fun";
@ -94,5 +101,44 @@ public class CommonUtil {
return StrUtil.toUnderlineCase(jsonObject.getByPath(expression, String.class));
}
/**
* 生成 md 摘要通用签名参考了顺丰同城的做法
* 参考https://openic.sf-express.com/#/quickstart
*
* @param postData
* @param appId
* @param appKey
* @return
*/
public static String generateOpenSign(String postData, String appId, String appKey) {
try {
if (StrUtil.isBlank(postData) || StrUtil.isBlank(appId) || StrUtil.isBlank(appKey)) {
log.error("生成签名时缺少必要参数!");
return Strings.EMPTY;
}
String sb = postData + "&" + appId + "&" + appKey;
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] md5 = md.digest(sb.getBytes(StandardCharsets.UTF_8));
int i;
StringBuffer buf = new StringBuffer();
for (byte b : md5) {
i = b;
if (i < 0) {
i += 256;
}
if (i < 16) {
buf.append("0");
}
buf.append(Integer.toHexString(i));
}
return Base64.encodeBase64String(buf.toString().getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
log.error(e.getMessage());
return Strings.EMPTY;
}
}
}

View File

@ -37,7 +37,7 @@ public class HttpUtils {
public static final String URL_SYNC_GET_DOWNCLIENTJAR="/shop/sync/app/downClientJar";//文件下载
public static final String URL_SYNC_GET_STOREdBCONFIG="/shop/sync/third/getStoreDbConfig";//文件下载
public static final String URL_SYNC_GET_STOREdBCONFIG="/shop/sync/third/getStoreDbConfig";//获取数据库配置
public static final String URL_SYNC_GET_STOR_DATA_RELEASE="/shop/sync/third/syncStoreDataRelease";//库存同步
@ -56,7 +56,6 @@ public class HttpUtils {
// 设置Content-Type为application/x-www-form-urlencoded
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
log.info(modelObject.toString());
HttpEntity<Object> request = new HttpEntity<>(modelObject, headers);
// 发送POST请求

View File

@ -178,6 +178,51 @@ public class BaseDao {
return total;
}
public Integer getBaseGoodsJoinTotal(String ip, String username, String password,Integer portNumber, String dataBaseName,String where){
int total=0;
Connection connection=getConnection(ip,username,password,portNumber,dataBaseName);
try {
String sql="WITH LatestStock AS (" +
" SELECT " +
" tib.item_no, " +
" tib.stock_qty," +
" tib.oper_date," +
" ROW_NUMBER() OVER(PARTITION BY tib.item_no ORDER BY tib.oper_date DESC) AS rn " +
" FROM t_im_branch_stock tib\n" +
") " +
"SELECT " +
" b.*, " +
" ls.stock_qty, " +
" ls.oper_date " +
"FROM ( " +
" SELECT " +
" ROW_NUMBER() OVER(ORDER BY item_clsno) AS rowId, " +
" * " +
" FROM t_bd_item_info " +
") b " +
"LEFT JOIN LatestStock ls ON b.item_no = ls.item_no AND ls.rn = 1 " +
" %s";
sql=String.format(sql,where);
log.info(sql);
PreparedStatement ps= connection.prepareStatement(sql);
ResultSet rs=ps.executeQuery();
while (rs.next()){
total=rs.getInt(1);
}
} catch (SQLException e) {
log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.getBaseTotal",e.getMessage());
throw new RuntimeException(e);
}
finally {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return total;
}
/**
* 带分页数据关联分页查询
@ -211,6 +256,46 @@ public class BaseDao {
return resultDto;
}
public ResultDto baseFindGoodsListJoinPage(String ip, String username, String password,Integer portNumber, String dataBaseName, int pageNo, int pageSize,String where){
Connection connection=getConnection(ip,username,password,portNumber,dataBaseName);
int start=(pageNo-1)*pageSize+1;
int end=pageNo*pageSize;
String sql="WITH LatestStock AS (" +
" SELECT " +
" tib.item_no, " +
" tib.stock_qty," +
" tib.oper_date," +
" ROW_NUMBER() OVER(PARTITION BY tib.item_no ORDER BY tib.oper_date DESC) AS rn " +
" FROM t_im_branch_stock tib " +
") " +
"SELECT " +
" b.*, " +
" ls.stock_qty, " +
" ls.oper_date " +
"FROM ( " +
" SELECT " +
" ROW_NUMBER() OVER(ORDER BY item_clsno) AS rowId ," +
" * " +
" FROM t_bd_item_info\n" +
") b " +
"LEFT JOIN LatestStock ls ON b.item_no = ls.item_no AND ls.rn = 1 " +
" %s b.rowId BETWEEN %s AND %s ";
sql=String.format(sql,where,start,end);
log.info(sql);
ResultDto resultDto=new ResultDto();
ResultSet rs=null;
try {
PreparedStatement ps= connection.prepareStatement(sql);
rs = ps.executeQuery();
} catch (SQLException e) {
log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.baseFindListJoinPage",e.getMessage());
throw new RuntimeException(e);
}
resultDto.setResultSet(rs);
resultDto.setConnection(connection);
return resultDto;
}
/**
*
* @param ip

View File

@ -36,7 +36,14 @@ public class SxDataDao extends BaseDao{
private final static String T_BD_BASECODE_TYPE="t_bd_basecode_type";//品牌表
private final static String TYPE_NO="type_no";//品牌排序字段
private final static String T_IM_BRANCH_STOCK="t_im_branch_stock";//库存表
//private final static String T_IM_BRANCH_STOCK="t_im_branch_stock";//库存表
private final static String T_IM_BRANCH_STOCK="(" +
"select * from( " +
" select ROW_NUMBER() OVER( " +
" partition BY tib.item_no order by tib.oper_date desc) as rn, " +
" tib.* " +
" from t_im_branch_stock tib)tib where tib.rn=1) ";
private final static String T_RM_SPEC_PRICE="t_rm_spec_price";//活动表
@ -147,13 +154,7 @@ public class SxDataDao extends BaseDao{
* @return
*/
public int getTBditemInfoJoninTotal(DataBaseInfo dataBaseInfo){
return getBaseJoinTotal(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName()
, T_BD_ITEM_INFO
,T_IM_BRANCH_STOCK
,"item_no"
,"item_no"
,ITEM_CLSNO
,"t.stock_qty,t.oper_date"
return getBaseGoodsJoinTotal(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName()
,dataBaseInfo.getWhere()==null?DEFALTWHERE:dataBaseInfo.getWhere());
}
@ -175,13 +176,7 @@ public class SxDataDao extends BaseDao{
* @param pageSize
*/
public List<SxSyncGoods> findBditemInfoListPage(DataBaseInfo dataBaseInfo,int pageNo,int pageSize){
ResultDto resultDto=baseFindListJoinPage(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName()
, T_BD_ITEM_INFO
,T_IM_BRANCH_STOCK
,"item_no"
,"item_no"
,ITEM_CLSNO
,"t.stock_qty,t.oper_date"
ResultDto resultDto=baseFindGoodsListJoinPage(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName()
,pageNo,pageSize,dataBaseInfo.getWhere()==null?DEFALTWHERE:dataBaseInfo.getWhere());
ResultSet rs= resultDto.getResultSet();
List<SxSyncGoods> sxSyncGoodses=new ArrayList<>();
@ -204,23 +199,26 @@ public class SxDataDao extends BaseDao{
sxSyncGoods.setGross_margin(new BigDecimal("0"));//毛利率
}
sxSyncGoods.setItem_no(rs.getString("item_no"));//货号
sxSyncGoods.setItem_subname(rs.getString("item_subname"));//商品名称
sxSyncGoods.setItem_subname(rs.getString("item_name"));//商品名称
sxSyncGoods.setItem_subno(rs.getString("item_subno"));//商品条码
sxSyncGoods.setBig_cls_name("9999");//商品大类 todo 如何关联
sxSyncGoods.setSmall_cls_name(rs.getString("item_clsno").trim());//商品小类 todo 如何关联
sxSyncGoods.setItem_size(rs.getString("item_size"));//规格
sxSyncGoods.setUnit_no(rs.getString("unit_no"));//单位 todo
sxSyncGoods.setStock(rs.getBigDecimal("stock_qty"));//库存数量 todo item_stock
sxSyncGoods.setUnit_no(rs.getString("unit_no"));//单位
if(null==rs.getBigDecimal("stock_qty")){
sxSyncGoods.setStock(BigDecimal.ZERO);
}else {
sxSyncGoods.setStock(rs.getBigDecimal("stock_qty"));//库存数量
}
sxSyncGoods.setPrice(rs.getBigDecimal("price"));//进货价
sxSyncGoods.setSale_price(rs.getBigDecimal("sale_price"));//零售价
sxSyncGoods.setVip_price(rs.getBigDecimal("vip_price"));//会员价
sxSyncGoods.setVip_acc_flag(rs.getBigDecimal("vip_acc_flag"));//允许积分
sxSyncGoods.setVip_acc_num(rs.getBigDecimal("vip_acc_num"));//积分值
sxSyncGoods.setSale_flag(rs.getInt("main_Sale_flag"));//商品状态 todo 是main_Sale_flag?
//sxSyncGoods.setSale_flag(rs.getInt("main_Sale_flag"));//商品状态 todo 是main_Sale_flag?
sxSyncGoods.setItem_rem(rs.getString("item_rem"));//助记码
sxSyncGoods.setBuild_date(rs.getString("build_date"));//生产日期 todo
sxSyncGoods.setValid_days(getStopDate(rs));//保质期 todo stop_date-build_date
@ -460,16 +458,24 @@ public class SxDataDao extends BaseDao{
dataBaseInfo.getPassword(), dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName());
try {
conn.setAutoCommit(false); // 关闭自动提交开启事务
String sql = "update t_im_branch_stock set stock_qty= stock_qty+?,oper_date=? where item_no=?";
//String sql = "update t_im_branch_stock set stock_qty= stock_qty+(?),oper_date=? where item_no=?";
String sql = "WITH TopStock AS ( " +
" SELECT TOP(1) * " +
" FROM t_im_branch_stock " +
" WHERE item_no = ? " +
" ORDER BY oper_date DESC " +
") " +
" UPDATE TopStock " +
" SET stock_qty = stock_qty+(?),oper_date=?;";
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
try (PreparedStatement ps = conn.prepareStatement(sql)) {
int batchSize = 1000; // 每批处理1000条
int count = 0;
Set<Map.Entry> sme=map.entrySet();
for (Map.Entry entry : sme) {
ps.setDouble(1, (double) entry.getValue());
ps.setTimestamp(2, timestamp);
ps.setString(3, (String) entry.getKey());
ps.setString(1, (String) entry.getKey());
ps.setDouble(2, (double) entry.getValue());
ps.setTimestamp(3, timestamp);
ps.addBatch(); // 添加至批处理
count++;
if (count % batchSize == 0) {

View File

@ -8,7 +8,12 @@ public class CommentModel {
@ApiModelProperty("店铺的key")
private String appKey;
@ApiModelProperty("店铺的密钥")
private String appId;
@ApiModelProperty("报文签名")
private String sign;
@ApiModelProperty("店铺的密钥")
private String signe;
@ApiModelProperty("店铺的id")
private String storeId;

View File

@ -11,5 +11,6 @@ public class UploadModel extends CommentModel{
private String page;
@ApiModelProperty("同步类型1商品,2分类,3品牌,4会员")
private String syncType;
@ApiModelProperty("报文签名")
private String sign;
}

View File

@ -185,7 +185,7 @@ public abstract class SxDataAbstService {
// if(type.equals("2")){
// finalSxGoosModel.setPrice(finalSxGoosModel.getPrice().multiply(m.getDiscount()));
// }
finalSxGoosModel.setIsSpecial("1");
// finalSxGoosModel.setIsSpecial("1");
}
});
sxGoosModelList.add(sxGoosModel);

View File

@ -43,10 +43,10 @@ public class WebClientService {
public String uploudSxData(String filePath, CommentModel commentModel,String page,String syncType){
UploadModel uploadModel=new UploadModel();
uploadModel.setAppKey(commentModel.getAppKey());
uploadModel.setAppId(commentModel.getAppId());
uploadModel.setSign(commentModel.getSign());
uploadModel.setPage(page);
uploadModel.setSyncType(syncType);
//"C:\\Users\\Administrator\\uploaded\\2025\\3\\25\\goods_1.txt"
return this.uploadFile(filePath, remoteIp+HttpUtils.URL_UPLOUP,uploadModel);
}

View File

@ -7,6 +7,7 @@ import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.small.client.Cache.CommonCache;
import com.small.client.Utils.*;
import com.small.client.dao.SxDataDao;
@ -89,14 +90,17 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
String jsonString="";
ObjectMapper objectMapper = new ObjectMapper();
try {
jsonString = objectMapper.writeValueAsString(sxCategoryModelList);
Gson gson=new Gson();
jsonString=gson.toJson(sxCategoryModelList);
jsonArray = JSONUtil.parseArray(jsonString);
} catch (JsonProcessingException e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
String sign=CommonUtil.generateOpenSign(jsonString,commentModel.getAppId(),commentModel.getAppKey());
log.info("sign:{}",sign);
String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_CATEGORY
+"?appKey="+commentModel.getAppKey()
+"&sign="+commentModel.getSign(),jsonArray);//todo 后期改为文件传输
+"&sign="+sign,jsonArray);//todo 后期改为文件传输
if (!HttpUtils.SUCCESSCODE.equals(code)) {
continue;
}
@ -147,16 +151,19 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
if(brandModels!=null&&brandModels.size()>0){
String jsonString ="";
JSONArray jsonArray =new JSONArray();
ObjectMapper objectMapper = new ObjectMapper();
// ObjectMapper objectMapper = new ObjectMapper();
try {
jsonString = objectMapper.writeValueAsString(brandModels);
Gson gson=new Gson();
jsonString=gson.toJson(brandModels);
jsonArray = JSONUtil.parseArray(jsonString);
} catch (JsonProcessingException e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
String sign=CommonUtil.generateOpenSign(jsonArray.toString(),commentModel.getAppId(),commentModel.getAppKey());
log.info("sign={}",sign);
String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_BRAND
+"?appKey="+commentModel.getAppKey()
+"&sign="+commentModel.getSign(),jsonArray);//todo 后期改为文件传输
+"&sign="+sign,jsonArray);//todo 后期改为文件传输
if(code!=null){
log.info("品牌总共有{}条数据,同步完成{}条",brandModels.size(),brandModels.size());
}
@ -201,14 +208,16 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
String sign=CommonUtil.generateOpenSign(jsonString,commentModel.getAppId(),commentModel.getAppKey());
String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_MEMBER
+"?appKey="+commentModel.getAppKey()
+"&sign="+commentModel.getSign(),memberList);//todo 后期改为文件传输
+"&sign="+sign,memberList);//todo 后期改为文件传输
if (!HttpUtils.SUCCESSCODE.equals(code)) {
continue;
}
syncCount+=memberList.size();
}
log.info("vip会员总共有{}条数据,同步完成{}条",total,syncCount);
}
@ -222,6 +231,7 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
String parentId= sxSyncCategories.stream().filter(m->m.getItem_clsname().equals(parentName)).
map(SxSyncCategory::getItem_clsno).collect(Collectors.joining());
getBdBrandCacheList(dataBaseInfo);
getCategoryCacheList(sxSyncCategories);
String childrens=commonCache.get(CommonCache.CACHE_CATEGROY+parentId);
if(childrens==null){
log.info(JSONUtil.toJsonStr(buildTree(sxSyncCategories,parentId)));
@ -300,7 +310,7 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
where+=" or b.build_date>'"+commentModel.getSyncTime()+"' ";
}
if(StringUtils.isNotEmpty(dataBaseInfo.getOperDate())){
where+=" and t.oper_date>'"+dataBaseInfo.getOperDate()+"' ";
where+=" and ls.oper_date>'"+dataBaseInfo.getOperDate()+"' ";
}
dataBaseInfo.setWhere(where);
// 记录总数
@ -313,6 +323,8 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
log.info("暂无商品同步");
return;
}
where+=" and";
dataBaseInfo.setWhere(where);
// 总页数
int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE);
@ -344,23 +356,20 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
//通知服务器上传cos
HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_GOODS_NOTICE_UPLOAD_TO_OSS
+"?appKey="+commentModel.getAppKey()
+"&sign="+commentModel.getSign()
+"&sign="+commentModel.getAppId()
+"&syncType="+DicEnum.MUAL_1.getCode()
+"&refreshDate="+refreshDate,
+"&refreshDateStr="+refreshDate,
JSONUtil.parseArray(folders));
//folders.add(String.valueOf(4));
//folders.add(String.valueOf(5));
log.info("商品分类总共有{}条数据,同步完成{}条",total,syncCount);
String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_GOODS_READ
+"?appKey="+commentModel.getAppKey()
+"&sign="+commentModel.getSign()
+"&sign="+commentModel.getAppId()
+"&syncType="+DicEnum.MUAL_1.getCode(),
JSONUtil.parseArray(folders));
if (HttpUtils.SUCCESSCODE.equals(code)) {
log.info("思迅商品同步完成,通知服务器处理数据相应成功");
//记录同步时间
createDateFile();
// createDateFile();
}
}
@ -377,6 +386,8 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
String fileName=fileUtils.getFileName(syncType,page,FileUtils.txtEnd);
String filePath=file.getAbsolutePath();
fileUtils.writeFile(filePath,fileName,content);
String sign=CommonUtil.generateOpenSign(content,commentModel.getAppId(),commentModel.getAppKey());
commentModel.setSign(sign);
return webClientService.uploudSxData(filePath+FileUtils.pathSeparator+fileName,commentModel,page.toString(),syncType);
}
@ -467,7 +478,19 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
}
commonCache.setBrandCahce("brandCache",specPriceDtos);
}
}
}
/**
* 获取商品分类据并加入到缓存
* @param sxSyncCategories
* @return
*/
public void getCategoryCacheList(List<SxSyncCategory> sxSyncCategories){
if(CollectionUtil.isNotEmpty(sxSyncCategories)){
for (SxSyncCategory sxSyncCategory : sxSyncCategories) {
commonCache.put(sxSyncCategory.getItem_clsno(),sxSyncCategory.getItem_clsname());
}
}
}
@ -481,7 +504,7 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
String encryptedData = getPrimaryKey();
Map<String, String> result= CryptoUtils.decryptAndUnpack(encryptedData);
CommentModel commentModel=new CommentModel();
commentModel.setSign(result.get("sign"));
commentModel.setAppId(result.get("sign"));
commentModel.setAppKey(result.get("appKey"));
commentModel.setStoreId(result.get("storeId"));
//获取上次同步的最大时间
@ -643,10 +666,11 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
public DataBaseInfo getDataBaseInfo(CommentModel commentModel) {
JSONObject jsonObject=new JSONObject();
jsonObject.putOnce("appKey",commentModel.getAppKey());
jsonObject.putOnce("sign",commentModel.getSign());
jsonObject.putOnce("sign",commentModel.getAppId());
StoreDbConfig storeDbConfig= HttpUtils.postDataGetConfig(restTemplate,remoteIp+HttpUtils.URL_SYNC_GET_STOREdBCONFIG
+"?appKey="+commentModel.getAppKey()
+"&sign="+commentModel.getSign(), jsonObject);
+"&sign="+commentModel.getAppId(), jsonObject);
DataBaseInfo dataBaseInfo=new DataBaseInfo();
if(null!=storeDbConfig){
dataBaseInfo.setIp(storeDbConfig.getDbIp());
@ -671,8 +695,7 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
}
JSONObject jsonObject= restTemplate.getForObject(remoteIp+HttpUtils.URL_SYNC_GET_STOR_DATA_RELEASE
+"?appKey="+commentModel.getAppKey()
+"&sign="+commentModel.getSign(),JSONObject.class);
+"&sign="+commentModel.getAppId(),JSONObject.class);
if(null!=jsonObject.get("result")){
Map map=(Map)jsonObject.get("result");
sxDataDao.updateStoreData(dataBaseInfo,map);