1
This commit is contained in:
@@ -103,7 +103,7 @@ public class ErpProductController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从闲鱼ERP拉取商品列表并保存
|
* 从闲鱼ERP拉取商品列表并保存(单页,保留用于兼容)
|
||||||
*/
|
*/
|
||||||
@PreAuthorize("@ss.hasPermi('jarvis:erpProduct:pull')")
|
@PreAuthorize("@ss.hasPermi('jarvis:erpProduct:pull')")
|
||||||
@Log(title = "拉取闲鱼商品", businessType = BusinessType.INSERT)
|
@Log(title = "拉取闲鱼商品", businessType = BusinessType.INSERT)
|
||||||
@@ -124,7 +124,7 @@ public class ErpProductController extends BaseController
|
|||||||
if (productStatus != null) {
|
if (productStatus != null) {
|
||||||
message += "(筛选条件:状态=" + statusText + ")";
|
message += "(筛选条件:状态=" + statusText + ")";
|
||||||
}
|
}
|
||||||
message += "。建议:1.尝试不选择状态拉取全部商品;2.尝试其他状态(下架、已售等)";
|
message += "。建议:使用全量同步功能自动遍历所有页码";
|
||||||
return success(message);
|
return success(message);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -132,6 +132,24 @@ public class ErpProductController extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全量同步商品(自动遍历所有页码,同步更新和删除)
|
||||||
|
*/
|
||||||
|
@PreAuthorize("@ss.hasPermi('jarvis:erpProduct:pull')")
|
||||||
|
@Log(title = "全量同步闲鱼商品", businessType = BusinessType.UPDATE)
|
||||||
|
@PostMapping("/syncAll")
|
||||||
|
public AjaxResult syncAllProducts(
|
||||||
|
@RequestParam(required = false) String appid,
|
||||||
|
@RequestParam(required = false) Integer productStatus)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
IErpProductService.SyncResult result = erpProductService.syncAllProducts(appid, productStatus);
|
||||||
|
return success(result.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return error("全量同步失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取状态文本(用于提示信息)
|
* 获取状态文本(用于提示信息)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -69,5 +69,39 @@ public interface IErpProductService
|
|||||||
* @return 拉取结果
|
* @return 拉取结果
|
||||||
*/
|
*/
|
||||||
public int pullAndSaveProductList(String appid, Integer pageNo, Integer pageSize, Integer productStatus);
|
public int pullAndSaveProductList(String appid, Integer pageNo, Integer pageSize, Integer productStatus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全量同步商品列表(自动遍历所有页码,同步更新和删除)
|
||||||
|
*
|
||||||
|
* @param appid ERP应用ID
|
||||||
|
* @param productStatus 商品状态(null表示全部状态)
|
||||||
|
* @return 同步结果
|
||||||
|
*/
|
||||||
|
public SyncResult syncAllProducts(String appid, Integer productStatus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步结果
|
||||||
|
*/
|
||||||
|
public static class SyncResult {
|
||||||
|
private int totalPulled; // 拉取总数
|
||||||
|
private int added; // 新增数量
|
||||||
|
private int updated; // 更新数量
|
||||||
|
private int deleted; // 删除数量
|
||||||
|
private int failed; // 失败数量
|
||||||
|
private String message; // 结果消息
|
||||||
|
|
||||||
|
public int getTotalPulled() { return totalPulled; }
|
||||||
|
public void setTotalPulled(int totalPulled) { this.totalPulled = totalPulled; }
|
||||||
|
public int getAdded() { return added; }
|
||||||
|
public void setAdded(int added) { this.added = added; }
|
||||||
|
public int getUpdated() { return updated; }
|
||||||
|
public void setUpdated(int updated) { this.updated = updated; }
|
||||||
|
public int getDeleted() { return deleted; }
|
||||||
|
public void setDeleted(int deleted) { this.deleted = deleted; }
|
||||||
|
public int getFailed() { return failed; }
|
||||||
|
public void setFailed(int failed) { this.failed = failed; }
|
||||||
|
public String getMessage() { return message; }
|
||||||
|
public void setMessage(String message) { this.message = message; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,14 @@ import com.ruoyi.jarvis.domain.ErpProduct;
|
|||||||
import com.ruoyi.jarvis.service.IErpProductService;
|
import com.ruoyi.jarvis.service.IErpProductService;
|
||||||
import com.ruoyi.erp.request.ERPAccount;
|
import com.ruoyi.erp.request.ERPAccount;
|
||||||
import com.ruoyi.erp.request.ProductListQueryRequest;
|
import com.ruoyi.erp.request.ProductListQueryRequest;
|
||||||
|
import com.ruoyi.erp.request.ProductDeleteRequest;
|
||||||
import com.alibaba.fastjson2.JSONArray;
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 闲鱼商品Service业务层处理
|
* 闲鱼商品Service业务层处理
|
||||||
@@ -313,6 +317,164 @@ public class ErpProductServiceImpl implements IErpProductService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全量同步商品列表(自动遍历所有页码,同步更新和删除)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IErpProductService.SyncResult syncAllProducts(String appid, Integer productStatus) {
|
||||||
|
IErpProductService.SyncResult result = new IErpProductService.SyncResult();
|
||||||
|
Set<Long> remoteProductIds = new HashSet<>(); // 远程商品ID集合
|
||||||
|
int pageNo = 1;
|
||||||
|
int pageSize = 50; // 每页50条,尽量少请求次数
|
||||||
|
int totalPulled = 0;
|
||||||
|
int added = 0;
|
||||||
|
int updated = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
ERPAccount account = resolveAccount(appid);
|
||||||
|
|
||||||
|
// 第一步:遍历所有页码,拉取并保存所有商品
|
||||||
|
log.info("开始全量同步商品,账号:{}", appid);
|
||||||
|
while (true) {
|
||||||
|
ProductListQueryRequest request = new ProductListQueryRequest(account);
|
||||||
|
request.setPage(pageNo, pageSize);
|
||||||
|
|
||||||
|
if (productStatus != null) {
|
||||||
|
Integer apiStatus = convertProductStatus(productStatus);
|
||||||
|
if (apiStatus != null) {
|
||||||
|
request.setProductStatus(apiStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String responseBody = request.getResponseBody();
|
||||||
|
JSONObject response = JSONObject.parseObject(responseBody);
|
||||||
|
|
||||||
|
if (response == null || response.getInteger("code") == null || response.getInteger("code") != 0) {
|
||||||
|
String errorMsg = response != null ? response.getString("msg") : "未知错误";
|
||||||
|
log.error("拉取商品列表失败(页码:{}): {}", pageNo, errorMsg);
|
||||||
|
result.setFailed(result.getFailed() + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject data = response.getJSONObject("data");
|
||||||
|
if (data == null) {
|
||||||
|
log.warn("拉取商品列表返回数据为空(页码:{})", pageNo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray productList = data.getJSONArray("list");
|
||||||
|
|
||||||
|
if (productList == null || productList.isEmpty()) {
|
||||||
|
log.info("第 {} 页数据为空,同步完成", pageNo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理当前页商品
|
||||||
|
for (int i = 0; i < productList.size(); i++) {
|
||||||
|
JSONObject productJson = productList.getJSONObject(i);
|
||||||
|
ErpProduct erpProduct = parseProductJson(productJson, appid);
|
||||||
|
if (erpProduct != null && erpProduct.getProductId() != null) {
|
||||||
|
remoteProductIds.add(erpProduct.getProductId());
|
||||||
|
|
||||||
|
// 保存或更新商品
|
||||||
|
ErpProduct existing = erpProductMapper.selectErpProductByProductIdAndAppid(
|
||||||
|
erpProduct.getProductId(), erpProduct.getAppid());
|
||||||
|
if (existing != null) {
|
||||||
|
erpProduct.setId(existing.getId());
|
||||||
|
erpProductMapper.updateErpProduct(erpProduct);
|
||||||
|
updated++;
|
||||||
|
} else {
|
||||||
|
erpProductMapper.insertErpProduct(erpProduct);
|
||||||
|
added++;
|
||||||
|
}
|
||||||
|
totalPulled++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("已同步第 {} 页,共 {} 条商品", pageNo, productList.size());
|
||||||
|
|
||||||
|
// 判断是否还有下一页
|
||||||
|
if (productList.size() < pageSize) {
|
||||||
|
log.info("已拉取完所有页码,共 {} 页", pageNo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageNo++;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.setTotalPulled(totalPulled);
|
||||||
|
result.setAdded(added);
|
||||||
|
result.setUpdated(updated);
|
||||||
|
|
||||||
|
// 第二步:对比本地和远程,删除本地有但远程没有的商品
|
||||||
|
log.info("开始同步删除,远程商品数:{}", remoteProductIds.size());
|
||||||
|
|
||||||
|
// 查询本地该账号下的所有商品
|
||||||
|
ErpProduct queryParam = new ErpProduct();
|
||||||
|
queryParam.setAppid(appid);
|
||||||
|
List<ErpProduct> localProducts = erpProductMapper.selectErpProductList(queryParam);
|
||||||
|
|
||||||
|
// 找出需要删除的商品(本地有但远程没有的)
|
||||||
|
List<ErpProduct> toDelete = localProducts.stream()
|
||||||
|
.filter(p -> !remoteProductIds.contains(p.getProductId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (!toDelete.isEmpty()) {
|
||||||
|
log.info("发现 {} 个本地商品在远程已不存在,开始删除", toDelete.size());
|
||||||
|
|
||||||
|
for (ErpProduct product : toDelete) {
|
||||||
|
try {
|
||||||
|
// 先调用远程删除接口
|
||||||
|
ProductDeleteRequest deleteRequest = new ProductDeleteRequest(account);
|
||||||
|
deleteRequest.setProductId(product.getProductId());
|
||||||
|
String deleteResponse = deleteRequest.getResponseBody();
|
||||||
|
JSONObject deleteResult = JSONObject.parseObject(deleteResponse);
|
||||||
|
|
||||||
|
if (deleteResult != null && deleteResult.getInteger("code") != null &&
|
||||||
|
deleteResult.getInteger("code") == 0) {
|
||||||
|
// 远程删除成功,删除本地记录
|
||||||
|
erpProductMapper.deleteErpProductById(product.getId());
|
||||||
|
result.setDeleted(result.getDeleted() + 1);
|
||||||
|
log.debug("成功删除商品:{}", product.getProductId());
|
||||||
|
} else {
|
||||||
|
// 远程删除失败,记录日志但不删除本地(可能是远程已经删除了)
|
||||||
|
String errorMsg = deleteResult != null ? deleteResult.getString("msg") : "未知错误";
|
||||||
|
log.warn("远程删除商品失败(可能已不存在):{},错误:{}", product.getProductId(), errorMsg);
|
||||||
|
// 如果远程返回商品不存在的错误,也删除本地记录
|
||||||
|
if (errorMsg != null && (errorMsg.contains("不存在") || errorMsg.contains("not found"))) {
|
||||||
|
erpProductMapper.deleteErpProductById(product.getId());
|
||||||
|
result.setDeleted(result.getDeleted() + 1);
|
||||||
|
} else {
|
||||||
|
result.setFailed(result.getFailed() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除商品异常:{}", product.getProductId(), e);
|
||||||
|
result.setFailed(result.getFailed() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建结果消息
|
||||||
|
StringBuilder msg = new StringBuilder();
|
||||||
|
msg.append(String.format("同步完成!拉取:%d个,新增:%d个,更新:%d个,删除:%d个",
|
||||||
|
totalPulled, added, updated, result.getDeleted()));
|
||||||
|
if (result.getFailed() > 0) {
|
||||||
|
msg.append(String.format(",失败:%d个", result.getFailed()));
|
||||||
|
}
|
||||||
|
result.setMessage(msg.toString());
|
||||||
|
|
||||||
|
log.info(result.getMessage());
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("全量同步商品异常", e);
|
||||||
|
result.setMessage("同步失败: " + e.getMessage());
|
||||||
|
result.setFailed(result.getFailed() + 1);
|
||||||
|
throw new RuntimeException("全量同步商品失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析ERP账号
|
* 解析ERP账号
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user