From 79775dde6355f48f4bfba134a196b28bbb10df69 Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 4 Apr 2025 20:27:55 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=89=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/van/business/util/JDUtil.java | 596 +++++++----------- src/main/resources/application-dev.yml | 4 +- 2 files changed, 241 insertions(+), 359 deletions(-) diff --git a/src/main/java/cn/van/business/util/JDUtil.java b/src/main/java/cn/van/business/util/JDUtil.java index 38b00d8..fb549d8 100644 --- a/src/main/java/cn/van/business/util/JDUtil.java +++ b/src/main/java/cn/van/business/util/JDUtil.java @@ -2,9 +2,7 @@ package cn.van.business.util; import cn.van.business.model.jd.OrderRow; -import cn.van.business.model.jd.ProductOrder; import cn.van.business.repository.OrderRowRepository; -import cn.van.business.repository.ProductOrderRepository; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.fasterxml.jackson.databind.ObjectMapper; @@ -34,6 +32,7 @@ import org.springframework.stereotype.Component; import java.io.BufferedInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.net.URL; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; @@ -41,18 +40,15 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.*; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import static cn.van.business.util.JDUtil.UserInteractionState.GiftMoneyStep.*; -import static cn.van.business.util.JDUtil.UserInteractionState.ProcessState.DISINFECTANT_CABINET; -import static cn.van.business.util.JDUtil.UserInteractionState.ProcessState.INIT; -import static cn.van.business.util.JDUtil.UserInteractionState.ProductOrderStep.*; +import static cn.van.business.util.JDUtil.UserInteractionState.ProcessState.*; import static cn.van.business.util.WXUtil.super_admins; -import java.net.URL; /** * @author Leo @@ -84,21 +80,19 @@ public class JDUtil { private static final long TIMEOUT_MINUTES = 1; private final StringRedisTemplate redisTemplate; private final OrderRowRepository orderRowRepository; - private final ProductOrderRepository productOrderRepository; private final WXUtil wxUtil; private final OrderUtil orderUtil; // 添加ObjectMapper来序列化和反序列化UserInteractionState private final ObjectMapper objectMapper = new ObjectMapper(); - + private final ConcurrentHashMap userInteractionStates = new ConcurrentHashMap<>(); private HashMap cacheMap = new HashMap<>(); // 构造函数中注入StringRedisTemplate @Autowired - public JDUtil(StringRedisTemplate redisTemplate, ProductOrderRepository productOrderRepository, OrderRowRepository orderRowRepository, WXUtil wxUtil, OrderUtil orderUtil) { + public JDUtil(StringRedisTemplate redisTemplate, OrderRowRepository orderRowRepository, WXUtil wxUtil, OrderUtil orderUtil) { this.redisTemplate = redisTemplate; this.orderRowRepository = orderRowRepository; - this.productOrderRepository = productOrderRepository; this.wxUtil = wxUtil; this.orderUtil = orderUtil; } @@ -533,14 +527,14 @@ public class JDUtil { * 接口描述:通过商品链接、领券链接、活动链接获取普通推广链接或优惠券二合一推广链接 * jd.union.open.promotion.bysubunionid.get */ - String transfer(String url,String giftCouponKey) { + String transfer(String url, String giftCouponKey) { JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, LPF_APP_KEY_WZ, LPF_SECRET_KEY_WZ); - UnionOpenPromotionBysubunionidGetRequest request=new UnionOpenPromotionBysubunionidGetRequest(); - PromotionCodeReq promotionCodeReq=new PromotionCodeReq(); + UnionOpenPromotionBysubunionidGetRequest request = new UnionOpenPromotionBysubunionidGetRequest(); + PromotionCodeReq promotionCodeReq = new PromotionCodeReq(); promotionCodeReq.setSceneId(1); promotionCodeReq.setMaterialId(url); - if (giftCouponKey!=null){ + if (giftCouponKey != null) { promotionCodeReq.setGiftCouponKey(giftCouponKey); } @@ -623,274 +617,232 @@ public class JDUtil { // return response; //} - private void handleUserInteraction(String fromWxid, String message) { - String key = INTERACTION_STATE_PREFIX + fromWxid; - String stateJson = redisTemplate.opsForValue().get(key); - UserInteractionState state; +private void handleUserInteraction(String fromWxid, String message) { + String key = INTERACTION_STATE_PREFIX + fromWxid; + UserInteractionState state = userInteractionStates.get(key); - if (stateJson == null) { + if (state == null) { + state = new UserInteractionState(); + logger.debug("New interaction state created for user: {}", fromWxid); + } else { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime lastInteractionTime = LocalDateTime.parse(state.getLastInteractionTime(), DATE_TIME_FORMATTER); + if (ChronoUnit.MINUTES.between(lastInteractionTime, now) > TIMEOUT_MINUTES) { + userInteractionStates.remove(key); + logger.debug("Deleted timeout state for user: {}", fromWxid); state = new UserInteractionState(); - logger.debug("New interaction state created for user: {}", fromWxid); - } else { - try { - state = objectMapper.readValue(stateJson, UserInteractionState.class); - // 检查是否超时 - LocalDateTime now = LocalDateTime.now(); - LocalDateTime lastInteractionTime = LocalDateTime.parse(state.getLastInteractionTime(), DATE_TIME_FORMATTER); - if (ChronoUnit.MINUTES.between(lastInteractionTime, now) > TIMEOUT_MINUTES) { - redisTemplate.delete(key); - logger.debug("Deleted timeout state for user: {}", fromWxid); - state = new UserInteractionState(); - } - } catch (Exception e) { - logger.error("Error parsing interaction state: {}", e.getMessage()); - state = new UserInteractionState(); - } } + } - state.updateLastInteractionTime(); + state.updateLastInteractionTime(); + userInteractionStates.put(key, state); // 确保状态保存 - switch (state.getCurrentState()) { - case INIT: - if ("转链".equals(message)) { - state.setCurrentState(UserInteractionState.ProcessState.PRODUCT_PROMOTION); - state.setCurrentField("content"); - wxUtil.sendTextMessage(fromWxid, "请输入推广文案(包含商品链接):", 1, fromWxid); - logger.info("进入转链流程 - 文案输入步骤"); - } else if ("礼金".equals(message)) { - state.setCurrentState(UserInteractionState.ProcessState.PRODUCT_PROMOTION); - state.setCurrentField("content"); - wxUtil.sendTextMessage(fromWxid, "请输入推广文案(包含商品链接):", 1, fromWxid); - logger.info("进入转链流程 - 文案输入步骤"); + switch (state.getCurrentState()) { + case INIT: + if ("转链".equals(message)) { + state.setCurrentState(UserInteractionState.ProcessState.PRODUCT_PROMOTION); + state.setCurrentField("content"); + wxUtil.sendTextMessage(fromWxid, "请输入推广文案(包含商品链接):", 1, fromWxid); + logger.info("进入转链流程 - 文案输入步骤"); + } + break; - } else if ("登记".equals(message)) { - state.setCurrentState(DISINFECTANT_CABINET); - state.setCurrentField("orderId"); - wxUtil.sendTextMessage(fromWxid, "请输入订单号:", 1, fromWxid); - logger.debug("User {} entered DISINFECTANT_CABINET state", fromWxid); + case PRODUCT_PROMOTION: + if ("content".equals(state.getCurrentField())) { + cacheMap.remove("text" + fromWxid); + + // 第一次输入文案,直接生成内容 + HashMap> messagesAll = generatePromotionContent(message); + List messages = messagesAll.get("text");// 不需要图片和SKU名称 + for (String s : messages) { + wxUtil.sendTextMessage(fromWxid, s, 1, fromWxid); } - break; - - case PRODUCT_PROMOTION: - if ("content".equals(state.getCurrentField())) { - cacheMap.remove("text"+fromWxid); - - // 第一次输入文案,直接生成内容 - HashMap> messagesAll = generatePromotionContent(message); - List messages = messagesAll.get("text") ;// 不需要图片和SKU名称 - for (String s : messages) { - wxUtil.sendTextMessage(fromWxid, s, 1, fromWxid); - } - try { - List images = messagesAll.get("images"); - if (images != null){ - for (String s : images) { - if (s!=null){ - wxUtil.sendImageMessage(fromWxid, s); - } + try { + List images = messagesAll.get("images"); + if (images != null) { + for (String s : images) { + if (s != null) { + wxUtil.sendImageMessage(fromWxid, s); } } - } catch (Exception e) { - logger.error("Error generating promotion content: {}", e.getMessage()); } - cacheMap.put("text"+fromWxid,messagesAll.get("data").get(0)); - // 询问是否需要继续转链 - wxUtil.sendTextMessage(fromWxid, "是否需要开通礼金?\n回复 1 - 是\n回复 2 - 否", 1, fromWxid); - logger.info("转链流程 - 等待用户确认是否需要开通礼金"); - state.setCurrentField("confirm"); - } else if ("confirm".equals(state.getCurrentField())) { - logger.info("转链流程 - 用户确认是否需要开通礼金:{}", message); - if ("1".equals(message)) { - state.setCurrentState(UserInteractionState.ProcessState.PRODUCT_PROMOTION); - state.setCurrentField("content"); - wxUtil.sendTextMessage(fromWxid, "请输入推广文案(包含商品链接):", 1, fromWxid); - logger.info("进入转链流程 - 文案输入步骤"); - } else if ("2".equals(message)) { - wxUtil.sendTextMessage(fromWxid, "开通礼金流程已取消", 1, fromWxid); - cacheMap.remove("text"+fromWxid); + } catch (Exception e) { + logger.error("Error generating promotion content: {}", e.getMessage()); + } + cacheMap.put("text" + fromWxid, messagesAll.get("data").get(0)); + // 直接进入确认开通礼金的步骤 + state.setCurrentField("confirm"); + wxUtil.sendTextMessage(fromWxid, "是否需要开通礼金?\n回复 1 - 是\n回复 2 - 否", 1, fromWxid); + logger.info("转链流程 - 等待用户确认是否需要开通礼金"); + } else if ("confirm".equals(state.getCurrentField())) { + logger.info("转链流程 - 用户确认是否需要开通礼金:{}", message); + if ("1".equals(message)) { + // ▼▼▼ 关键修改:明确设置初始步骤 ▼▼▼ + state.setCurrentState(UserInteractionState.ProcessState.GIFT_MONEY_FLOW); + state.setCurrentStep(UserInteractionState.GiftMoneyStep.STEP_AMOUNT); + state.getCollectedFields().clear(); + userInteractionStates.put(key, state); // 立即持久化状态 + wxUtil.sendTextMessage(fromWxid, "请输入开通金额(元):", 1, fromWxid); + } else if ("2".equals(message)) { + wxUtil.sendTextMessage(fromWxid, "开通礼金流程已取消", 1, fromWxid); + cacheMap.remove("text" + fromWxid); + } else { + wxUtil.sendTextMessage(fromWxid, "无效的选择,请重新输入:\n回复 1 - 是\n回复 2 - 否", 1, fromWxid); + } + state.reset(); // 重置状态 + userInteractionStates.put(key, state); // 在外部保存 + } + break; + + case GIFT_MONEY_FLOW: + createPromotionWithGift(fromWxid, message); + break; + + default: + wxUtil.sendTextMessage(fromWxid, "无效的状态,请重新开始对话", 1, fromWxid); + state.setCurrentState(INIT); + logger.debug("User {} reset to INIT state due to invalid state", fromWxid); + break; + } + + userInteractionStates.put(key, state); + logger.debug("Updated interaction state for user {}: {}", fromWxid, JSON.toJSONString(state)); +} +private void createPromotionWithGift(String fromWxid, String message) { + String key = INTERACTION_STATE_PREFIX + fromWxid; + UserInteractionState state = userInteractionStates.get(key); + +// 修改点3:在createPromotionWithGift方法开始处增加状态校验 + if (state == null || state.getCurrentStep() == null) { + logger.warn("非法状态访问: {}", fromWxid); + wxUtil.sendTextMessage(fromWxid, "⚠️ 会话超时,请重新开始流程", 1, fromWxid); + return; + } else { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime lastInteractionTime = LocalDateTime.parse(state.getLastInteractionTime(), DATE_TIME_FORMATTER); + if (ChronoUnit.MINUTES.between(lastInteractionTime, now) > TIMEOUT_MINUTES) { + userInteractionStates.remove(key); + logger.debug("Deleted timeout state for user: {}", fromWxid); + state = new UserInteractionState(); + } + } + + state.updateLastInteractionTime(); + userInteractionStates.put(key, state); // 确保状态保存 + + try { + switch (state.getCurrentStep()) { + case STEP_CONFIRM_GIFT: + if ("1".equals(message)) { + state.setCurrentStep(STEP_AMOUNT); + wxUtil.sendTextMessage(fromWxid, "请输入开通金额(元):", 1, fromWxid); + } else if ("2".equals(message)) { + // 不开通礼金,直接生成转链 + String cachedData = cacheMap.get("text" + fromWxid); + if (cachedData != null) { + JSONObject jsonObject = JSONObject.parseObject(cachedData); + String skuId = jsonObject.getString("materialUrl"); + String transferUrl = transfer(skuId, null); + wxUtil.sendTextMessage(fromWxid, "转链后的链接:\n" + transferUrl, 1, fromWxid); } else { - wxUtil.sendTextMessage(fromWxid, "无效的选择,请重新输入:\n回复 1 - 是\n回复 2 - 否", 1, fromWxid); + wxUtil.sendTextMessage(fromWxid, "未找到缓存的商品链接,请重新开始流程", 1, fromWxid); } - state.reset(); // 重置状态 + state.reset(); + userInteractionStates.put(key, state); // 在外部保存 + } else { + wxUtil.sendTextMessage(fromWxid, "无效的选择,请重新输入:\n回复 1 - 是\n回复 2 - 否", 1, fromWxid); } break; - - case GIFT_MONEY_FLOW: - handleGiftMoneyFlow(fromWxid, message, state); + case STEP_AMOUNT: + logger.debug("用户 {} 输入金额:{}", fromWxid, message); + if (message == null || message.trim().isEmpty()) { + wxUtil.sendTextMessage(fromWxid, "金额不能为空,请输入数字(如:100.00)", 1, fromWxid); + return; + } + if (!isValidAmount(message)) { + wxUtil.sendTextMessage(fromWxid, "金额格式错误,请输入数字(如:100.00)", 1, fromWxid); + return; + } + double amount = Double.parseDouble(message); + String formattedAmount = String.format("%.2f", amount); + state.getCollectedFields().put("amount", formattedAmount); + state.setCurrentStep(STEP_QUANTITY); + userInteractionStates.put(key, state); // ▼▼▼ 保存点2 ▼▼▼ + wxUtil.sendTextMessage(fromWxid, "请输入数量(1-100):", 1, fromWxid); break; - case PRODUCT_ORDER_REGISTRATION: - handleProductOrderRegistration(fromWxid, message, state); + case STEP_QUANTITY: + logger.debug("用户 {} 输入数量:{}", fromWxid, message); // 新增 + if (!isValidQuantity(message)) { + wxUtil.sendTextMessage(fromWxid, "数量格式错误,请输入整数", 1, fromWxid); + return; + } + + int quantity = Integer.parseInt(message); + if (quantity < 1 || quantity > 100) { + wxUtil.sendTextMessage(fromWxid, "数量需在1-100之间", 1, fromWxid); + return; + } + + // 从缓存中获取 amount + String amountStr = state.getCollectedFields().get("amount"); + if (amountStr == null || amountStr.trim().isEmpty()) { + wxUtil.sendTextMessage(fromWxid, "未找到金额,请重新开始流程", 1, fromWxid); + state.reset(); + userInteractionStates.put(key, state); // 在外部保存 + return; + } + amount = Double.parseDouble(amountStr); + + logger.debug("礼金参数准备完成:金额={}元,数量={}", amount, quantity); // 新增 + + String cachedData = cacheMap.get("text" + fromWxid); + if (cachedData != null) { + JSONObject jsonObject = JSONObject.parseObject(cachedData); + String skuId = jsonObject.getString("materialUrl"); + String giftKey = createGiftCoupon(skuId, amount, quantity, "pop"); + if (giftKey == null) { + logger.error("用户 {} 礼金创建失败:SKU={}, 金额={}元,数量={}", fromWxid, skuId, amount, quantity); // 新增 + wxUtil.sendTextMessage(fromWxid, "❌ 礼金创建失败,请检查商品是否符合要求", 1, fromWxid); + state.reset(); + userInteractionStates.put(key, state); // 在外部保存 + + return; + } + + logger.info("用户 {} 礼金创建成功:批次ID={}, 参数:SKU={}, 金额={}元,数量={}", fromWxid, giftKey, skuId, amount, quantity); // 新增关键成功日志 + state.getCollectedFields().put("giftKey", giftKey); + + // 生成转链 + String transferUrl = transfer(skuId, giftKey); + wxUtil.sendTextMessage(fromWxid, "附带礼金的链接:\n" + transferUrl, 1, fromWxid); + state.reset(); + userInteractionStates.put(key, state); // 在外部保存 + } else { + wxUtil.sendTextMessage(fromWxid, "未找到缓存的商品链接,请重新开始流程", 1, fromWxid); + state.reset(); + userInteractionStates.put(key, state); // 在外部保存 + } break; default: - wxUtil.sendTextMessage(fromWxid, "无效的状态,请重新开始对话", 1, fromWxid); - state.setCurrentState(INIT); - logger.debug("User {} reset to INIT state due to invalid state", fromWxid); + state.setCurrentStep(STEP_CONFIRM_GIFT); + wxUtil.sendTextMessage(fromWxid, "是否需要开通礼金?\n回复 1 - 是\n回复 2 - 否", 1, fromWxid); break; } - - try { - redisTemplate.opsForValue().set(key, objectMapper.writeValueAsString(state), TIMEOUT_MINUTES, TimeUnit.MINUTES); - logger.debug("Saved interaction state for user {}: {}", fromWxid, state); - } catch (Exception e) { - logger.error("Error saving interaction state: {}", e.getMessage()); - } + } catch (Exception e) { + logger.error("转链和礼金流程异常,用户 {} 当前步骤:{}", fromWxid, state.getCurrentStep(), e); + wxUtil.sendTextMessage(fromWxid, "❌ 系统异常,请稍后重试", 1, fromWxid); + state.reset(); + userInteractionStates.put(key, state); // 在外部保存 } - - // 新增方法:处理文案中的链接并进行转链 - private String transferContentWithLinks(String message) { - List urls = extractUJDUrls(message); - if (urls.isEmpty()) { - return "文案中未找到有效的商品链接,请检查格式是否正确。\n" + message; - } - - StringBuilder enrichedContent = new StringBuilder(); - for (String url : urls) { - String transferUrl = transfer(url,null); - if (transferUrl != null) { - enrichedContent.append("原链接:").append(url).append("\n") - .append("转链后链接:").append(transferUrl).append("\n"); - } else { - enrichedContent.append("链接转链失败:").append(url).append("\n"); - } - } - return enrichedContent.toString(); - } - - // 新增礼金流程处理方法 - private void handleGiftMoneyFlow(String fromWxid, String message, UserInteractionState state) { - if (cacheMap.get("text"+fromWxid) != null){ - message = cacheMap.get("text"+fromWxid); - } - if (state.getCurrentStep() == null) { - state.setCurrentStep(STEP_PRODUCT_LINK); - wxUtil.sendTextMessage(fromWxid, "流程异常,已重置。请输入商品链接:", 1, fromWxid); - return; - } - - if ("礼金".equals(message)) { - state.reset(); - logger.debug("用户 {} 重置礼金流程", fromWxid); // 新增 - wxUtil.sendTextMessage(fromWxid, "流程已重置,请重新开始", 1, fromWxid); - return; - } - - try { - String skuId = ""; - double amount = 0.0; - String owner = "pop"; - switch (state.getCurrentStep()) { - case STEP_PRODUCT_LINK: - if (cacheMap.get("text"+fromWxid) != null) { - String data = cacheMap.get("text" + fromWxid); - JSONObject jsonObject = JSONObject.parseObject(data); - skuId = jsonObject.getString("materialUrl"); - String owner1 = jsonObject.getString("owner"); - if (owner1 != null && owner1.equals("g")){ - owner = "g"; - } - - }else { - skuId = parseSkuFromUrl(message); - } - logger.debug("用户 {} 输入商品链接:{}, 解析出SKU: {}", fromWxid, message, skuId); // 新增 - if (skuId == null) { - wxUtil.sendTextMessage(fromWxid, "商品链接格式错误,请重新输入", 1, fromWxid); - return; - } - //BigfieldQueryResult queryResult = queryProductInfo(skuId); - //logger.debug("商品ID {} 查询结果:{}", skuId, queryResult); // 新增 - //if (queryResult == null) { - // wxUtil.sendTextMessage(fromWxid, "⚠️ 商品信息查询失败,可能链接无效", 1, fromWxid); - // return; - //} - - //BigFieldGoodsResp productInfo = queryResult.getData()[0]; - //owner = productInfo.getOwner(); - //state.getCollectedFields().put("skuId", skuId); - //state.getCollectedFields().put("productInfo", productInfo.getBaseBigFieldInfo().getWdis()); - //state.getCollectedFields().put("owner", owner); - // - //logger.debug("商品信息已收集:SKU={}, Owner={}, WDIS={}", skuId, owner, productInfo.getBaseBigFieldInfo().getWdis()); // 新增 - //String productInfo = skuId; - state.getCollectedFields().put("skuId", skuId); - state.setCurrentStep(STEP_AMOUNT); - String prompt = "请输入开通金额(元):"; - wxUtil.sendTextMessage(fromWxid, prompt, 1, fromWxid); - break; - - case STEP_AMOUNT: - logger.debug("用户 {} 输入金额:{}", fromWxid, message); - // 强制检查空值 - if (message == null || message.trim().isEmpty()) { - wxUtil.sendTextMessage(fromWxid, "金额不能为空,请输入数字(如:100.00)", 1, fromWxid); - return; - } - // 校验格式 - if (!isValidAmount(message)) { - wxUtil.sendTextMessage(fromWxid, "金额格式错误,请输入数字(如:100.00)", 1, fromWxid); - return; - } - // 转换并保留两位小数 - amount = Double.parseDouble(message); - String formattedAmount = String.format("%.2f", amount); - state.getCollectedFields().put("amount", formattedAmount); - state.setCurrentStep(STEP_QUANTITY); - wxUtil.sendTextMessage(fromWxid, "请输入数量(1-100):", 1, fromWxid); - break; - - case STEP_QUANTITY: - logger.debug("用户 {} 输入数量:{}", fromWxid, message); // 新增 - if (!isValidQuantity(message)) { - wxUtil.sendTextMessage(fromWxid, "数量格式错误,请输入整数", 1, fromWxid); - return; - } - - int quantity = Integer.parseInt(message); - if (quantity < 1 || quantity > 100) { - wxUtil.sendTextMessage(fromWxid, "数量需在1-100之间", 1, fromWxid); - return; - } - - logger.debug("礼金参数准备完成:SKU={},金额={}元,数量={},Owner={}", state.getCollectedFields().get("skuId"), amount, quantity, state.getCollectedFields().get("owner")); // 新增 - - String giftKey = createGiftCoupon(state.getCollectedFields().get("skuId"), - Double.parseDouble(state.getCollectedFields().get("amount")), - quantity, - owner); - if (giftKey == null) { - logger.error("用户 {} 礼金创建失败:SKU={}, 金额={}, 数量={}, Owner={}", fromWxid, skuId, amount, quantity, owner); // 新增 - wxUtil.sendTextMessage(fromWxid, "❌ 礼金创建失败,请检查商品是否符合要求", 1, fromWxid); - state.reset(); - return; - } - - logger.info("用户 {} 礼金创建成功:批次ID={}, 参数:SKU={}, 金额={}元,数量={}, Owner={}", fromWxid, giftKey, skuId, amount, quantity, owner); // 新增关键成功日志 - state.getCollectedFields().put("giftKey", giftKey); - - // 需要调用转链,传入礼金批次ID,返回转链和文案 - if (cacheMap.get("text"+fromWxid) != null) { - String data = cacheMap.get("text" + fromWxid); - JSONObject jsonObject = JSONObject.parseObject(data); - skuId = jsonObject.getString("materialUrl"); - String transfer = transfer(skuId, giftKey); - wxUtil.sendTextMessage(fromWxid, "附带礼金的链接:\n" + transfer, 1, fromWxid); +} - } - break; - } - } catch (Exception e) { - logger.error("礼金流程异常,用户 {} 当前步骤:{}", fromWxid, state.getCurrentStep(), e); // 新增 - wxUtil.sendTextMessage(fromWxid, "❌ 系统异常,请稍后重试", 1, fromWxid); - state.reset(); - } - } /** * 生成转链和文案的方法 @@ -898,11 +850,11 @@ public class JDUtil { * @param message 文案内容,包含商品链接 * @return 处理后的文案,附带商品信息 */ - public HashMap> generatePromotionContent(String message) { + public HashMap> generatePromotionContent(String message) { HashMap> finallMessage = new HashMap<>(); List textList = new ArrayList<>(); List imagesList = new ArrayList<>(); - List dataList = new ArrayList<>() ; + List dataList = new ArrayList<>(); // 提取文案中的所有 u.jd.com 链接 List urls = extractUJDUrls(message); @@ -1197,7 +1149,7 @@ public class JDUtil { String replaceAll = itemMap.get("skuName").replaceAll("国家", "").replaceAll("补贴", ""); itemMap.put("spuid", String.valueOf(productInfo.getData()[0].getSpuid())); //for (HashMap.Entry entry : itemMap.entrySet()) { - //couponInfo.append(" ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + //couponInfo.append(" ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); //} couponInfo.append(" ").append("店铺:\n").append(itemMap.get("shopName")).append("\n") .append("标题:\n").append(replaceAll).append("\n") @@ -1230,7 +1182,7 @@ public class JDUtil { StringBuilder wenan = new StringBuilder().append("文案生成: \n"); // 完成转链后替换链接为u.jd.com链接,文案不修改就返回 for (HashMap stringStringHashMap : resultList) { - String transferUrl = transfer(stringStringHashMap.get("materialUrl"),null); + String transferUrl = transfer(stringStringHashMap.get("materialUrl"), null); wenan = new StringBuilder(message.replace(stringStringHashMap.get("url"), transferUrl)); } @@ -1257,6 +1209,7 @@ public class JDUtil { return null; } } + /** * 提取文案中的所有 u.jd.com 链接 * @@ -1469,100 +1422,23 @@ public class JDUtil { private boolean isValidQuantity(String input) { - return input.matches("^\\d+$"); - } - - - private void handleProductOrderRegistration(String fromWxid, String message, UserInteractionState state) { - switch (state.getCurrentProductOrderStep()) { - case STEP_ORDER_ID: - if (!message.matches("^\\d{10,20}$")) { - wxUtil.sendTextMessage(fromWxid, "⚠️ 订单号格式错误(需10-20位数字)", 1, fromWxid); - return; - } - state.getCollectedFields().put("orderId", message); - state.setCurrentProductOrderStep(STEP_PRODUCT_INFO); - wxUtil.sendTextMessage(fromWxid, "请输入商品信息(格式:商品名称-类型编号)\n类型对照:1-家电 2-数码 3-服饰\n示例:格力空调-1", 1, fromWxid); - break; - - case STEP_PRODUCT_INFO: - String[] productInfo = message.split("-"); - if (productInfo.length != 2 || !productInfo[1].matches("[1-3]")) { - wxUtil.sendTextMessage(fromWxid, "❌ 格式错误或类型编号无效", 1, fromWxid); - return; - } - state.getCollectedFields().put("skuName", productInfo[0]); - state.getCollectedFields().put("skuType", productInfo[1]); - - state.setCurrentProductOrderStep(UserInteractionState.ProductOrderStep.STEP_RECIPIENT_INFO); - wxUtil.sendTextMessage(fromWxid, "请输入收件信息(格式:姓名-电话-地址)\n示例:张三-13812345678-北京市朝阳区", 1, fromWxid); - break; - - case STEP_RECIPIENT_INFO: - String[] recipientInfo = message.split("-"); - if (recipientInfo.length < 3) { - wxUtil.sendTextMessage(fromWxid, "❌ 格式错误,请按示例格式输入", 1, fromWxid); - return; - } - state.getCollectedFields().put("recipientName", recipientInfo[0]); - state.getCollectedFields().put("recipientPhone", recipientInfo[1]); - state.getCollectedFields().put("recipientAddress", String.join("-", Arrays.copyOfRange(recipientInfo, 2, recipientInfo.length))); - - // 生成确认信息 - String confirmMsg = buildConfirmMessage(state); - wxUtil.sendTextMessage(fromWxid, confirmMsg, 1, fromWxid); - state.setCurrentProductOrderStep(STEP_REVIEW_CONFIRM); - break; - - case STEP_REVIEW_CONFIRM: - if ("确认".equals(message)) { - boolean success = saveFullProductOrder(state, fromWxid); - wxUtil.sendTextMessage(fromWxid, success ? "✅ 订单登记成功!" : "❌ 保存失败,请联系管理员", 1, fromWxid); - state.reset(); - } else { - state.setCurrentProductOrderStep(STEP_ORDER_ID); - wxUtil.sendTextMessage(fromWxid, "请重新输入订单号:", 1, fromWxid); - } - break; + if (input == null || input.trim().isEmpty()) { + logger.error("数量为空或无效: {}", input); + return false; } - } - - private String buildConfirmMessage(UserInteractionState state) { - return "📋 请确认登记信息:\n" + "────────────────\n" + "▪ 订单号:" + state.getCollectedFields().get("orderId") + "\n" + "▪ 商品名称:" + state.getCollectedFields().get("skuName") + "\n" + "▪ 商品类型:" + getTypeDesc(state.getCollectedFields().get("skuType")) + "\n" + "▪ 收件人:" + state.getCollectedFields().get("recipientName") + "\n" + "▪ 联系方式:" + state.getCollectedFields().get("recipientPhone") + "\n" + "▪ 收货地址:" + state.getCollectedFields().get("recipientAddress") + "\n" + "────────────────\n" + "回复【确认】提交,其他内容重新开始"; - } - - private boolean saveFullProductOrder(UserInteractionState state, String fromWxid) { try { - ProductOrder order = new ProductOrder(); - order.setOrderId(state.getCollectedFields().get("orderId")); - order.setSkuName(state.getCollectedFields().get("skuName")); - order.setSkuType(Integer.valueOf(state.getCollectedFields().get("skuType"))); - order.setRecipientName(state.getCollectedFields().get("recipientName")); - order.setRecipientPhone(state.getCollectedFields().get("recipientPhone")); - order.setRecipientAddress(state.getCollectedFields().get("recipientAddress")); - order.setOrderTime(new Date()); // 设置下单时间为当前时间 - order.setWhoOrder(state.getCollectedFields().get("recipientName")); // 关联管理员信息 - order.setIsReviewed(false); // 默认是否晒图登记 - order.setIsCashbackReceived(false); // 默认是否返现到账 - order.setFromWxid(fromWxid); // 设置当前交互的wxid - - productOrderRepository.save(order); - logger.info("订单登记成功:{}", order); + int quantity = Integer.parseInt(input); + if (quantity < 1 || quantity > 100) { + logger.error("数量超出范围: {}", quantity); + return false; + } return true; - } catch (Exception e) { - logger.error("订单保存异常:{}", e.getMessage()); + } catch (NumberFormatException e) { + logger.error("数量格式错误: {}", input, e); return false; } } - private String getTypeDesc(String skuType) { - return switch (skuType) { - case "1" -> "家电"; - case "2" -> "数码"; - case "3" -> "服饰"; - default -> "未知类型"; - }; - } // 定义一个内部类来存储用户交互状态 @Getter @@ -1573,9 +1449,9 @@ public class JDUtil { private ProcessState currentState; private Map collectedFields; // 用于存储收集到的字段值 private String currentField; // 当前正在询问的字段 - private ProductOrderStep currentProductOrderStep; public UserInteractionState() { + this.currentStep = GiftMoneyStep.STEP_CONFIRM_GIFT; // 初始 this.lastInteractionTime = LocalDateTime.now().format(DATE_TIME_FORMATTER); this.currentState = INIT; this.collectedFields = new HashMap<>(); @@ -1590,25 +1466,31 @@ public class JDUtil { public void reset() { this.currentState = INIT; this.collectedFields.clear(); - this.currentStep = STEP_PRODUCT_LINK; // 明确重置步骤 - this.currentProductOrderStep = null; + this.currentStep = STEP_CONFIRM_GIFT ; // 明确重置步骤 updateLastInteractionTime(); } // 推荐使用枚举管理状态 public enum ProcessState { - INIT, GIFT_MONEY_FLOW, DISINFECTANT_CABINET, PRODUCT_ORDER_REGISTRATION, PRODUCT_PROMOTION + INIT, GIFT_MONEY_FLOW, PRODUCT_PROMOTION } - public enum GiftMoneyStep { - STEP_PRODUCT_LINK, STEP_AMOUNT, STEP_QUANTITY - } +public enum GiftMoneyStep { + STEP_CONFIRM_GIFT(0), // 显式指定序号 + STEP_AMOUNT(1), + STEP_QUANTITY(2); + private final int code; + + GiftMoneyStep(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} - // 在UserInteractionState类中新增步骤枚举 - public enum ProductOrderStep { - STEP_ORDER_ID, STEP_PRODUCT_INFO, STEP_RECIPIENT_INFO, STEP_REVIEW_CONFIRM, STEP_CASHBACK_TRACK - } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 5faf7cd..c40697f 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -61,5 +61,5 @@ rocketmq: consume-thread-max: 64 # 消费线程池最大线程数 consume-message-batch-max-size: 64 # 批量消费最大消息数 isRunning: - wx: false - jd: false + wx: true + jd: true