diff --git a/pom.xml b/pom.xml
index 153629dc1..d82487393 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,6 +47,30 @@
3.1.2
+
+
+ local
+
+ true
+
+
+ local
+
+
+
+ dev
+
+ dev
+
+
+
+ prod
+
+ prod
+
+
+
+
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/migartion/GtController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/migartion/GtController.java
index 13ca75fd5..7f4d77595 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/migartion/GtController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/migartion/GtController.java
@@ -1,21 +1,23 @@
package com.ruoyi.web.controller.xkt.migartion;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
-import com.ruoyi.web.controller.xkt.migartion.vo.gt.GtAttrVO;
-import com.ruoyi.web.controller.xkt.migartion.vo.gt.GtCateVO;
-import com.ruoyi.web.controller.xkt.migartion.vo.gt.GtProdSkuVO;
-import com.ruoyi.web.controller.xkt.migartion.vo.gt.GtProdVO;
+import com.ruoyi.web.controller.xkt.migartion.vo.gt.*;
import com.ruoyi.xkt.mapper.SysProductCategoryMapper;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -36,6 +38,7 @@ public class GtController extends BaseController {
final RedisCache redisCache;
final SysProductCategoryMapper prodCateMapper;
+ final ObjectMapper objectMapper;
/**
* step1
@@ -122,11 +125,54 @@ public class GtController extends BaseController {
return R.ok();
}
+
/**
* step4
*/
@PreAuthorize("@ss.hasAnyRoles('admin,general_admin')")
- @PostMapping("/attr/cache/{user_id}/{product_id}")
+ @PostMapping("/attr/cache/{userId}")
+ public R createAttrCache(@PathVariable Integer userId, @Validated @RequestBody GtAttrQueryVO attrQueryVO) {
+ // 获取GT所有在售商品
+ List gtOnSaleList = ObjectUtils.defaultIfNull(redisCache
+ .getCacheObject(CacheConstants.MIGRATION_GT_SALE_BASIC_KEY + userId), new ArrayList<>());
+ if (CollectionUtils.isEmpty(gtOnSaleList)) {
+ throw new ServiceException("商品列表为空", HttpStatus.ERROR);
+ }
+ if (attrQueryVO.getUrlPrefix().trim().contains("?")) {
+ throw new ServiceException("urlPrefix不能包含?");
+ }
+ if (attrQueryVO.getRefererPrefix().trim().contains("?")) {
+ throw new ServiceException("refererPrefix不能包含?");
+ }
+ List gtProdIdList = gtOnSaleList.stream().map(GtProdSkuVO::getProduct_id).distinct().collect(Collectors.toList());
+ Map gtRelateMap = gtOnSaleList.stream().collect(Collectors.toMap(GtProdSkuVO::getProduct_id, GtProdSkuVO::getArticle_number, (v1, v2) -> v2));
+ Random random = new Random();
+ List errArtNoList = new ArrayList<>();
+ final Integer total = gtProdIdList.size();
+ Map> multiKeyMap = new HashMap<>();
+ for (int i = 0; i < gtProdIdList.size(); i++) {
+ Map attrMap = new HashMap<>();
+ try {
+ // 添加随机延迟(6-20秒)
+ if (i > 0) {
+ Thread.sleep((random.nextInt(15) + 6) * 1000L);
+ }
+ sendRequest(attrQueryVO, gtProdIdList.get(i), attrMap, errArtNoList, i);
+ } catch (Exception e) {
+ System.out.println("请求失败: " + e.getMessage());
+ }
+ System.out.println("总共: " + total + " ,目前正在执行: ==> " + gtProdIdList.get(i) + " : " + (i + 1));
+ multiKeyMap.put(CacheConstants.MIGRATION_GT_SALE_ATTR_KEY + userId + "_" + gtProdIdList.get(i), attrMap);
+ }
+ redisCache.setCacheMapBatch(multiKeyMap);
+ return CollectionUtils.isNotEmpty(errArtNoList) ? R.fail(errArtNoList.stream().map(gtRelateMap::get).collect(Collectors.toList())) : R.ok();
+ }
+
+ /**
+ * step4.5 补偿
+ */
+ @PreAuthorize("@ss.hasAnyRoles('admin,general_admin')")
+ @PostMapping("/compensation/attr/cache/{user_id}/{product_id}")
public R createAttrCache(@PathVariable(value = "user_id") Integer user_id, @PathVariable("product_id") Integer product_id,
@Validated @RequestBody GtAttrVO attrVO) {
// 判断缓存中是否有该product_id
@@ -150,6 +196,8 @@ public class GtController extends BaseController {
return R.ok();
}
+
+
/**
* step5
*/
@@ -167,7 +215,6 @@ public class GtController extends BaseController {
return R.ok();
}
-
/**
* Unicode解码方法
*
@@ -207,5 +254,46 @@ public class GtController extends BaseController {
return result.toString();
}
+ private void sendRequest(GtAttrQueryVO attrQueryVO, Integer gtProdId, Map attrMap, List errArtNoList, int index) {
+ // 创建HttpClient(自动管理连接池)
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpGet request = new HttpGet(attrQueryVO.getUrlPrefix() + "?product_id=" + gtProdId);
+ request.setHeader("Accept-Encoding", attrQueryVO.getAccept().trim());
+ request.setHeader("Accept", attrQueryVO.getAccept().trim());
+ request.setHeader("Accept-Language", attrQueryVO.getAcceptLanguage().trim());
+ request.setHeader("Connection", attrQueryVO.getConnection().trim());
+ request.setHeader("Cookie", attrQueryVO.getCookie().trim());
+ request.setHeader("Host", attrQueryVO.getHost().trim());
+ request.setHeader("Referer", attrQueryVO.getRefererPrefix().trim() + "?product_id=" + gtProdId);
+ request.setHeader("X-Requested-With", attrQueryVO.getRequestedWith().trim());
+ request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36");
+ // 执行请求
+ String response = client.execute(request, httpResponse -> EntityUtils.toString(httpResponse.getEntity()));
+ try {
+ // 将响应内容转换为GtAttrVO对象
+ GtAttrVO gtAttrVO = objectMapper.readValue(response, GtAttrVO.class);
+ gtAttrVO.getData().forEach((itemId, attr) -> {
+ // 不处理 multi=1 的属性
+ if (attr.getMulti() == 1) {
+ return;
+ }
+ // 有值
+ if (attr.getHas_value() == 1) {
+ attr.getAttr().stream().filter(x -> x.getChoosed() == 1).forEach(x -> attrMap.put(x.getProps_name(), x.getPropsvalue_name()));
+ }
+ });
+ } catch (Exception e) {
+ System.err.println("当前执行: ==> " + gtProdId + " : " + index +" 未获取到!");
+ errArtNoList.add(gtProdId);
+ System.err.println("JSON转换失败: " + e.getMessage());
+ e.printStackTrace();
+ }
+ } catch (Exception e) {
+ System.err.println("当前执行: ==> " + gtProdId + " : " + "失败!");
+ errArtNoList.add(gtProdId);
+ System.out.println("请求异常: " + e.getMessage());
+ }
+ }
+
}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/migartion/vo/gt/GtAttrQueryVO.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/migartion/vo/gt/GtAttrQueryVO.java
new file mode 100644
index 000000000..b0efb9376
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/migartion/vo/gt/GtAttrQueryVO.java
@@ -0,0 +1,27 @@
+package com.ruoyi.web.controller.xkt.migartion.vo.gt;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author liangyq
+ * @date 2025-05-11 23:46
+ */
+@Data
+public class GtAttrQueryVO {
+
+ @NotBlank(message = "urlPrefix不能为空!")
+ private String urlPrefix;
+ private String accept;
+ private String acceptEncoding;
+ private String acceptLanguage;
+ private String connection;
+ private String cookie;
+ private String host;
+ private String refererPrefix;
+ private String requestedWith;
+
+}
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 43e4ffde2..2e8af2ea8 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -2,3 +2,4 @@ spring:
profiles:
active: local
# active: prod
+# active: @spring.profiles.active@
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
index b513bed2e..02346a10b 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
@@ -1,6 +1,8 @@
package com.ruoyi.common.core.redis;
+import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
@@ -194,6 +196,26 @@ public class RedisCache
}
}
+ /**
+ * 批量缓存多个 Map 到 Redis(使用管道)
+ *
+ * @param keyValueMap 键值对,key 为 Redis 键,value 为对应的 Map 数据
+ */
+ public void setCacheMapBatch(final Map> keyValueMap) {
+ redisTemplate.executePipelined(new SessionCallback