diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/JDOrderListController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/JDOrderListController.java index a839ce9..dbb9c7d 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/JDOrderListController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/JDOrderListController.java @@ -32,6 +32,7 @@ import com.ruoyi.jarvis.domain.dto.JDOrderSimpleDTO; import com.ruoyi.jarvis.domain.dto.QuickRecordModelOption; import com.ruoyi.jarvis.domain.dto.QuickRecordModelShopOption; import com.ruoyi.jarvis.util.QuickRecordModelShopOptionUtil; +import com.ruoyi.jarvis.service.IJDOrderModelShopService; import com.ruoyi.jarvis.service.IJDOrderProfitService; import com.ruoyi.system.service.ISysConfigService; import com.ruoyi.jarvis.service.IJDOrderService; @@ -58,6 +59,7 @@ public class JDOrderListController extends BaseController private final ObjectMapper objectMapper; private final ISysConfigService sysConfigService; + private final IJDOrderModelShopService jdOrderModelShopService; public JDOrderListController(IJDOrderService jdOrderService, IJDOrderProfitService jdOrderProfitService, IOrderRowsService orderRowsService, @@ -65,7 +67,8 @@ public class JDOrderListController extends BaseController GroupRebateExcelImportService groupRebateExcelImportService, IGroupRebateExcelUploadService groupRebateExcelUploadService, ObjectMapper objectMapper, - ISysConfigService sysConfigService) { + ISysConfigService sysConfigService, + IJDOrderModelShopService jdOrderModelShopService) { this.jdOrderService = jdOrderService; this.jdOrderProfitService = jdOrderProfitService; this.orderRowsService = orderRowsService; @@ -74,6 +77,7 @@ public class JDOrderListController extends BaseController this.groupRebateExcelUploadService = groupRebateExcelUploadService; this.objectMapper = objectMapper; this.sysConfigService = sysConfigService; + this.jdOrderModelShopService = jdOrderModelShopService; } /** @@ -196,6 +200,16 @@ public class JDOrderListController extends BaseController return AjaxResult.success(options); } + /** + * 一键迁移:将 model_number 末尾匹配的店铺前缀写入 model_shop,并从 model_number 截掉。 + * 需先配置 quickRecord.modelShopOptions。 + */ + @Log(title = "JD订单型号店铺拆分迁移", businessType = BusinessType.UPDATE) + @PostMapping("/migrateModelShop") + public AjaxResult migrateModelShop() { + return AjaxResult.success(jdOrderService.migrateModelShopSplit()); + } + /** * 导入跟团返现类 Excel:按「单号/订单号」匹配系统订单,将「是否返现」「总共返现」等写入后返备注(可多次导入累加);文件落盘并记上传记录。 */ @@ -331,6 +345,7 @@ public class JDOrderListController extends BaseController public AjaxResult edit(@RequestBody JsonNode root) throws JsonProcessingException { JDOrder jdOrder = objectMapper.treeToValue(root, JDOrder.class); applyExtraCostFromPayload(root, jdOrder); + jdOrderModelShopService.normalizeOrder(jdOrder); jdOrderProfitService.recalculate(jdOrder); jdOrder.getParams().put("applyProfitFields", Boolean.TRUE); int rows = jdOrderService.updateJDOrder(jdOrder); @@ -642,8 +657,8 @@ public class JDOrderListController extends BaseController String duoduoOrderNo = o.getThirdPartyOrderNo() != null && !o.getThirdPartyOrderNo().trim().isEmpty() ? o.getThirdPartyOrderNo() : (o.getRemark() != null ? o.getRemark() : ""); - // 型号 - String modelNumber = o.getModelNumber() != null ? o.getModelNumber() : ""; + // 型号(对外完整型号 = 本体 + 店铺前缀) + String modelNumber = QuickRecordModelShopOptionUtil.fullModelNumber(o); // 数量(固定为1) String quantity = "1"; diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/JDOrder.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/JDOrder.java index 2175bb0..c34a832 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/JDOrder.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/JDOrder.java @@ -27,10 +27,14 @@ public class JDOrder extends BaseEntity { @Excel(name = "分销标记") private String distributionMark; - /** 型号 */ + /** 型号(本体,不含店铺后缀) */ @Excel(name = "型号") private String modelNumber; + /** 型号店铺短前缀(如海尔厨房;完整型号 = modelNumber + modelShop) */ + @Excel(name = "型号店铺") + private String modelShop; + /** 列表筛选:型号不含这些子串(逗号/空格分隔,对应 SQL 多条 NOT LIKE),不入库 */ @Transient private String modelNumberExclude; diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/dto/QuickRecordModelOption.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/dto/QuickRecordModelOption.java index 09823d4..2a2a73b 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/dto/QuickRecordModelOption.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/dto/QuickRecordModelOption.java @@ -10,6 +10,9 @@ public class QuickRecordModelOption { private String modelNumber; + /** 型号店铺短前缀 */ + private String modelShop; + /** 最近一次订单的下单付款金额 */ private Double lastPaymentAmount; diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/JDOrderMapper.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/JDOrderMapper.java index 8c7dff8..3a7cce2 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/JDOrderMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/JDOrderMapper.java @@ -67,6 +67,13 @@ public interface JDOrderMapper { * 每个型号取其主键最大的一条订单的付款 / 后返(用于快捷录单下拉回填) */ List selectQuickRecordModelOptions(); + + /** 待拆分型号店铺的历史订单(全表扫描,由业务层按配置过滤) */ + List selectOrdersPendingModelShopMigration(); + + int updateModelShopFields(@org.apache.ibatis.annotations.Param("id") Long id, + @org.apache.ibatis.annotations.Param("modelNumber") String modelNumber, + @org.apache.ibatis.annotations.Param("modelShop") String modelShop); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderModelShopService.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderModelShopService.java new file mode 100644 index 0000000..e75adf4 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderModelShopService.java @@ -0,0 +1,25 @@ +package com.ruoyi.jarvis.service; + +import java.util.Map; + +import com.ruoyi.jarvis.domain.JDOrder; +import com.ruoyi.jarvis.domain.dto.QuickRecordModelShopOption; + +import java.util.List; + +/** + * 型号与店铺前缀拆分、归一化及历史数据迁移。 + */ +public interface IJDOrderModelShopService { + + List loadShopOptions(); + + void normalizeOrder(JDOrder order); + + String fullModel(JDOrder order); + + String lookupModelBase(String rawModel); + + /** 一键迁移:将 model_number 末尾匹配的店铺前缀写入 model_shop 并从 model_number 截掉 */ + Map migrateExistingOrders(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderService.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderService.java index d8c93c1..a75f11f 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderService.java @@ -52,6 +52,9 @@ public interface IJDOrderService { /** 快捷录单:型号及最近一次单的付款 / 后返 */ List selectQuickRecordModelOptions(); + + /** 一键迁移:拆分 model_number 末尾店铺前缀到 model_shop */ + java.util.Map migrateModelShopSplit(); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/InstructionServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/InstructionServiceImpl.java index 8340437..b37c738 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/InstructionServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/InstructionServiceImpl.java @@ -7,6 +7,7 @@ import com.ruoyi.jarvis.domain.WeComShareLinkLogisticsJob; import com.ruoyi.jarvis.service.IInstructionService; import com.ruoyi.jarvis.service.IOrderRowsService; import com.ruoyi.jarvis.service.IJDOrderService; +import com.ruoyi.jarvis.service.IJDOrderModelShopService; import com.ruoyi.jarvis.service.IProductJdConfigService; import com.ruoyi.jarvis.service.IPhoneReplaceConfigService; import com.ruoyi.jarvis.service.SuperAdminService; @@ -42,6 +43,8 @@ public class InstructionServiceImpl implements IInstructionService { @Resource private IJDOrderService jdOrderService; @Resource + private IJDOrderModelShopService jdOrderModelShopService; + @Resource private SuperAdminService superAdminService; @Resource private StringRedisTemplate stringRedisTemplate; @@ -358,13 +361,13 @@ public class InstructionServiceImpl implements IInstructionService { // 统一截取分销标记 list.forEach(order -> order.setDistributionMark(truncateDistributionMark(order.getDistributionMark()))); String low = kw.toLowerCase(Locale.ROOT); - List matched = list.stream().filter(o -> contains(o.getRemark(), low) || contains(o.getOrderId(), low) || contains(o.getModelNumber(), low) || contains(o.getAddress(), low) || contains(o.getBuyer(), low)).limit(50).collect(Collectors.toList()); + List matched = list.stream().filter(o -> contains(o.getRemark(), low) || contains(o.getOrderId(), low) || contains(o.getModelNumber(), low) || contains(o.getModelShop(), low) || contains(orderFullModel(o), low) || contains(o.getAddress(), low) || contains(o.getBuyer(), low)).limit(50).collect(Collectors.toList()); if (matched.isEmpty()) return Collections.singletonList("未找到匹配的订单"); StringBuilder sb = new StringBuilder(); int i = 0; for (JDOrder o : matched) { i++; - sb.append(i).append(", 单:").append(nvl(o.getRemark())).append("\n分销标记:").append(nvl(o.getDistributionMark())).append("\n型号:").append(nvl(o.getModelNumber())).append("\n链接:").append(nvl(o.getLink())).append("\n下单付款:").append(nvl(o.getPaymentAmount())).append("\n后返金额:").append(nvl(o.getRebateAmount())).append("\n地址:").append(nvl(o.getAddress())).append("\n物流链接:").append(nvl(o.getLogisticsLink())).append("\n订单号:").append(nvl(o.getOrderId())).append("\n下单人:").append(nvl(o.getBuyer())).append("\n下单时间:").append(nvl(o.getOrderTime())).append("\n备注:").append(nvl(o.getStatus())).append("\n━━━━━━━━━━━━\n"); + sb.append(i).append(", 单:").append(nvl(o.getRemark())).append("\n分销标记:").append(nvl(o.getDistributionMark())).append("\n型号:").append(nvl(orderFullModel(o))).append("\n链接:").append(nvl(o.getLink())).append("\n下单付款:").append(nvl(o.getPaymentAmount())).append("\n后返金额:").append(nvl(o.getRebateAmount())).append("\n地址:").append(nvl(o.getAddress())).append("\n物流链接:").append(nvl(o.getLogisticsLink())).append("\n订单号:").append(nvl(o.getOrderId())).append("\n下单人:").append(nvl(o.getBuyer())).append("\n下单时间:").append(nvl(o.getOrderTime())).append("\n备注:").append(nvl(o.getStatus())).append("\n━━━━━━━━━━━━\n"); } return Collections.singletonList(sb.toString()); } @@ -388,7 +391,7 @@ public class InstructionServiceImpl implements IInstructionService { List sorted = filtered.stream().sorted(Comparator.comparing(JDOrder::getRemark, Comparator.nullsFirst(String::compareTo))).collect(Collectors.toList()); StringBuilder sb = new StringBuilder(); for (JDOrder o : sorted) { - sb.append(nvl(o.getRemark())).append('\t').append(nvl(o.getOrderId())).append('\t').append(fmt(o.getOrderTime()).split(" ")[0]).append('\t').append(nvl(o.getModelNumber())).append('\t').append(nvl(o.getAddress())).append('\t').append(nvl(o.getLogisticsLink())).append('\t').append('\t').append(nvl(o.getBuyer())).append('\t').append(nvl(o.getPaymentAmount())).append('\t').append(nvl(o.getRebateAmount())).append('\t').append(mapDistribution(o.getDistributionMark())).append('\t').append(nvl(o.getStatus())).append("\n"); + sb.append(nvl(o.getRemark())).append('\t').append(nvl(o.getOrderId())).append('\t').append(fmt(o.getOrderTime()).split(" ")[0]).append('\t').append(nvl(orderFullModel(o))).append('\t').append(nvl(o.getAddress())).append('\t').append(nvl(o.getLogisticsLink())).append('\t').append('\t').append(nvl(o.getBuyer())).append('\t').append(nvl(o.getPaymentAmount())).append('\t').append(nvl(o.getRebateAmount())).append('\t').append(mapDistribution(o.getDistributionMark())).append('\t').append(nvl(o.getStatus())).append("\n"); } return Collections.singletonList(sb.toString()); } @@ -422,7 +425,7 @@ public class InstructionServiceImpl implements IInstructionService { String dm = e.getKey() != null ? e.getKey() : "未提供"; List orders = e.getValue(); - Map byModel = orders.stream().collect(Collectors.groupingBy(JDOrder::getModelNumber, Collectors.counting())); + Map byModel = orders.stream().collect(Collectors.groupingBy(this::orderFullModel, Collectors.counting())); int totalCount = 0; StringBuilder summary = new StringBuilder(); @@ -445,7 +448,7 @@ public class InstructionServiceImpl implements IInstructionService { // 找到该型号价格最高的订单 for (JDOrder order : orders) { - if (model.equals(order.getModelNumber())) { + if (model.equals(orderFullModel(order))) { JDOrder currentMax = groupMaxPriceOrders.get(model); if (currentMax == null || (order.getPaymentAmount() != null && @@ -467,7 +470,7 @@ public class InstructionServiceImpl implements IInstructionService { .append("单:").append(o.getRemark() != null ? o.getRemark() : "未提供").append("\n") .append("备注:").append(o.getStatus() != null ? o.getStatus() : " ").append("\n") .append("第三方单号:").append(o.getThirdPartyOrderNo() != null && !o.getThirdPartyOrderNo().isEmpty() ? o.getThirdPartyOrderNo() : "无").append("\n") - .append("型号:").append(o.getModelNumber() != null ? o.getModelNumber() : "未提供").append("\n") + .append("型号:").append(o.getModelNumber() != null || o.getModelShop() != null ? orderFullModel(o) : "未提供").append("\n") .append("地址:").append(o.getAddress() != null ? o.getAddress() : "未提供").append("\n") .append("物流链接:\n").append(o.getLogisticsLink() != null ? o.getLogisticsLink() : "无"); } @@ -594,7 +597,7 @@ public class InstructionServiceImpl implements IInstructionService { List orders = e.getValue(); Map byModel = orders.stream() - .collect(Collectors.groupingBy(JDOrder::getModelNumber, Collectors.counting())); + .collect(Collectors.groupingBy(this::orderFullModel, Collectors.counting())); int totalCount = 0; StringBuilder summary = new StringBuilder(); @@ -616,7 +619,7 @@ public class InstructionServiceImpl implements IInstructionService { // 找到该型号价格最高的订单 for (JDOrder order : orders) { - if (model.equals(order.getModelNumber())) { + if (model.equals(orderFullModel(order))) { JDOrder currentMax = buyerMaxPriceOrders.get(model); if (currentMax == null || (order.getPaymentAmount() != null && @@ -641,7 +644,7 @@ public class InstructionServiceImpl implements IInstructionService { .append("单:").append(o.getRemark() != null ? o.getRemark() : "未提供").append("\n") .append("备注:").append(o.getStatus() != null ? o.getStatus() : " ").append("\n") .append("第三方单号:").append(o.getThirdPartyOrderNo() != null && !o.getThirdPartyOrderNo().isEmpty() ? o.getThirdPartyOrderNo() : "无").append("\n") - .append("型号:").append(o.getModelNumber() != null ? o.getModelNumber() : "未提供").append("\n") + .append("型号:").append(o.getModelNumber() != null || o.getModelShop() != null ? orderFullModel(o) : "未提供").append("\n") .append("地址:").append(o.getAddress() != null ? o.getAddress() : "未提供").append("\n") .append("物流链接:\n").append(o.getLogisticsLink() != null ? o.getLogisticsLink() : "无"); } @@ -874,7 +877,7 @@ public class InstructionServiceImpl implements IInstructionService { } } - String jf = productJdConfigService.getJdUrlByProductModel(modelNumber); + String jf = productJdConfigService.getJdUrlByProductModel(jdOrderModelShopService.lookupModelBase(modelNumber)); StringBuilder order = new StringBuilder(); order.append("生"+phone).append("\n").append(fenxiaoInfo).append("\n").append(modelNumber).append("\n").append(jf).append("\n").append(quantityStr).append("\n").append(address); @@ -928,10 +931,11 @@ public class InstructionServiceImpl implements IInstructionService { String rawModelToken = extractLastNonChineseToken(addressLine); String modelNumber = sanitizeModel(rawModelToken); + String lookupModel = jdOrderModelShopService.lookupModelBase(modelNumber); String cleanedAddress = addressLine.replaceAll("\\[.*?]", "").replace(rawModelToken, "").replaceAll("\\s+", " ").trim(); String fullAddress = cleanedAddress + " 安装派送联系" + phone + (suffix.isEmpty() ? "" : "转" + suffix); - String jfLink = productJdConfigService.getJdUrlByProductModel(modelNumber); + String jfLink = productJdConfigService.getJdUrlByProductModel(lookupModel); StringBuilder sheng = new StringBuilder(); sheng.append("生\n").append(distributionMark).append("\n").append(modelNumber).append("\n").append(jfLink != null ? jfLink : "").append("\n") @@ -956,7 +960,7 @@ public class InstructionServiceImpl implements IInstructionService { String distributionMark = split[1]; String link = sanitizeLink(split[3]); if ((link == null || link.isEmpty()) && shouldAutoFillLink(distributionMark)) { - String fetched = productJdConfigService.getJdUrlByProductModel(split[2]); + String fetched = productJdConfigService.getJdUrlByProductModel(jdOrderModelShopService.lookupModelBase(split[2])); if (fetched != null && !fetched.isEmpty()) { link = fetched; } @@ -1148,7 +1152,7 @@ public class InstructionServiceImpl implements IInstructionService { } // 获取转链链接 - String jf = productJdConfigService.getJdUrlByProductModel(modelNumber); + String jf = productJdConfigService.getJdUrlByProductModel(jdOrderModelShopService.lookupModelBase(modelNumber)); // 构建"生"指令格式 StringBuilder sheng = new StringBuilder(); @@ -1453,7 +1457,7 @@ public class InstructionServiceImpl implements IInstructionService { if (order == null) { return null; } - String model = order.getModelNumber(); + String model = jdOrderModelShopService.fullModel(order); String address = order.getAddress(); String logistics = extractOriginalLogisticsLinkNew(originalInput); if (logistics == null && order.getLogisticsLink() != null) { @@ -1515,6 +1519,7 @@ public class InstructionServiceImpl implements IInstructionService { String originalInput = input.trim().replace("元", ""); // 与 JDUtil.parseOrderFromText 一致的模板字段 JDOrder order = parseOrderFromText(originalInput); + jdOrderModelShopService.normalizeOrder(order); // 字段校验并返回缺失项(根据新模板格式,单号存储在remark字段中) StringBuilder missing = new StringBuilder(); @@ -1658,7 +1663,7 @@ public class InstructionServiceImpl implements IInstructionService { sb.append("第三方单号:").append(order.getThirdPartyOrderNo().trim()).append("\n"); } sb.append("分销标记:").append(nvl(order.getDistributionMark())).append("\n"); - sb.append("型号:").append(nvl(order.getModelNumber())).append("\n"); + sb.append("型号:").append(nvl(jdOrderModelShopService.fullModel(order))).append("\n"); sb.append("下单人:").append(nvl(order.getBuyer())).append("\n"); sb.append("付款:").append(order.getPaymentAmount() != null ? order.getPaymentAmount().toString() : "").append("\n"); sb.append("后返:").append(order.getRebateAmount() != null ? order.getRebateAmount().toString() : "").append("\n"); @@ -1692,6 +1697,11 @@ public class InstructionServiceImpl implements IInstructionService { return first; } + /** 对外展示 / 录单文案用的完整型号(本体 + 店铺前缀) */ + private String orderFullModel(JDOrder order) { + return order == null ? "" : jdOrderModelShopService.fullModel(order); + } + private boolean isEmpty(String s) { return s == null || s.isEmpty(); } @@ -1730,7 +1740,7 @@ public class InstructionServiceImpl implements IInstructionService { sb.append("下单地址(注意带分机):\n"); sb.append(order.getAddress() != null ? order.getAddress() : "").append("\n"); sb.append("—————————\n"); - sb.append("型号:").append(order.getModelNumber() != null ? order.getModelNumber() : "").append("\n"); + sb.append("型号:").append(orderFullModel(order)).append("\n"); sb.append("\n"); sb.append("下单人(需填):\n"); sb.append(order.getBuyer() != null ? order.getBuyer() : "").append("\n"); @@ -1772,7 +1782,7 @@ public class InstructionServiceImpl implements IInstructionService { sb.append("分销标记:").append(order.getDistributionMark() != null ? order.getDistributionMark() : "").append("\n"); sb.append("第三方单号:").append(order.getThirdPartyOrderNo() != null ? order.getThirdPartyOrderNo() : "").append("\n"); sb.append("型号:\n"); - sb.append(order.getModelNumber() != null ? order.getModelNumber() : "").append("\n"); + sb.append(orderFullModel(order)).append("\n"); sb.append("链接:\n"); sb.append(order.getLink() != null ? order.getLink() : "").append("\n"); sb.append("下单付款:\n"); diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderModelShopServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderModelShopServiceImpl.java new file mode 100644 index 0000000..ec207e8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderModelShopServiceImpl.java @@ -0,0 +1,79 @@ +package com.ruoyi.jarvis.service.impl; + +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.jarvis.domain.JDOrder; +import com.ruoyi.jarvis.domain.dto.QuickRecordModelShopOption; +import com.ruoyi.jarvis.mapper.JDOrderMapper; +import com.ruoyi.jarvis.service.IJDOrderModelShopService; +import com.ruoyi.jarvis.util.QuickRecordModelShopOptionUtil; +import com.ruoyi.system.service.ISysConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class JDOrderModelShopServiceImpl implements IJDOrderModelShopService { + + @Autowired + private ISysConfigService sysConfigService; + + @Autowired + private JDOrderMapper jdOrderMapper; + + @Override + public List loadShopOptions() { + String raw = sysConfigService.selectConfigByKey(QuickRecordModelShopOptionUtil.CONFIG_KEY); + return QuickRecordModelShopOptionUtil.parseOptions(raw); + } + + @Override + public void normalizeOrder(JDOrder order) { + QuickRecordModelShopOptionUtil.normalizeOrderModelShop(order, loadShopOptions()); + } + + @Override + public String fullModel(JDOrder order) { + return QuickRecordModelShopOptionUtil.fullModelNumber(order); + } + + @Override + public String lookupModelBase(String rawModel) { + return QuickRecordModelShopOptionUtil.lookupModelBase(rawModel, loadShopOptions()); + } + + @Override + public Map migrateExistingOrders() { + List options = loadShopOptions(); + Map result = new HashMap<>(); + if (options.isEmpty()) { + result.put("updated", 0); + result.put("skipped", 0); + result.put("message", "未配置店铺选项(quickRecord.modelShopOptions),请先配置后再迁移"); + return result; + } + List candidates = jdOrderMapper.selectOrdersPendingModelShopMigration(); + int updated = 0; + int skipped = 0; + for (JDOrder row : candidates) { + String beforeBase = StringUtils.trim(row.getModelNumber()); + String beforeShop = StringUtils.trim(row.getModelShop()); + normalizeOrder(row); + String afterBase = StringUtils.trim(row.getModelNumber()); + String afterShop = StringUtils.trim(row.getModelShop()); + if (beforeBase.equals(afterBase) && beforeShop.equals(afterShop)) { + skipped++; + continue; + } + jdOrderMapper.updateModelShopFields(row.getId(), row.getModelNumber(), row.getModelShop()); + updated++; + } + result.put("updated", updated); + result.put("skipped", skipped); + result.put("total", candidates.size()); + result.put("message", String.format("迁移完成:更新 %d 条,无变化 %d 条(共扫描 %d 条)", updated, skipped, candidates.size())); + return result; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderServiceImpl.java index 2b9fadc..b62c451 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderServiceImpl.java @@ -3,6 +3,7 @@ package com.ruoyi.jarvis.service.impl; import com.ruoyi.jarvis.domain.JDOrder; import com.ruoyi.jarvis.domain.dto.QuickRecordModelOption; import com.ruoyi.jarvis.mapper.JDOrderMapper; +import com.ruoyi.jarvis.service.IJDOrderModelShopService; import com.ruoyi.jarvis.service.IJDOrderProfitService; import com.ruoyi.jarvis.service.IJDOrderService; import org.springframework.beans.factory.annotation.Autowired; @@ -19,6 +20,9 @@ public class JDOrderServiceImpl implements IJDOrderService { @Autowired private IJDOrderProfitService jdOrderProfitService; + @Autowired + private IJDOrderModelShopService jdOrderModelShopService; + @Override public List selectJDOrderList(JDOrder jdOrder) { return jdOrderMapper.selectJDOrderList(jdOrder); @@ -36,12 +40,14 @@ public class JDOrderServiceImpl implements IJDOrderService { @Override public int insertJDOrder(JDOrder jdOrder) { + jdOrderModelShopService.normalizeOrder(jdOrder); jdOrderProfitService.recalculate(jdOrder); return jdOrderMapper.insertJDOrder(jdOrder); } @Override public int updateJDOrder(JDOrder jdOrder) { + jdOrderModelShopService.normalizeOrder(jdOrder); return jdOrderMapper.updateJDOrder(jdOrder); } @@ -87,6 +93,11 @@ public class JDOrderServiceImpl implements IJDOrderService { public List selectQuickRecordModelOptions() { return jdOrderMapper.selectQuickRecordModelOptions(); } + + @Override + public java.util.Map migrateModelShopSplit() { + return jdOrderModelShopService.migrateExistingOrders(); + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/util/QuickRecordModelShopOptionUtil.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/util/QuickRecordModelShopOptionUtil.java index 84d3a34..9929554 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/util/QuickRecordModelShopOptionUtil.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/util/QuickRecordModelShopOptionUtil.java @@ -2,6 +2,7 @@ package com.ruoyi.jarvis.util; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.jarvis.domain.dto.QuickRecordModelShopOption; +import com.ruoyi.jarvis.domain.JDOrder; import java.util.ArrayList; import java.util.Collections; @@ -88,4 +89,58 @@ public final class QuickRecordModelShopOptionUtil { } return new String[] { full, "" }; } + + public static String fullModelNumber(String base, String shop) { + String b = StringUtils.trim(base); + String s = StringUtils.trim(shop); + if (StringUtils.isEmpty(b)) { + return s; + } + if (StringUtils.isEmpty(s)) { + return b; + } + if (b.endsWith(s)) { + return b; + } + return b + s; + } + + public static String fullModelNumber(JDOrder order) { + if (order == null) { + return ""; + } + return fullModelNumber(order.getModelNumber(), order.getModelShop()); + } + + /** + * 落库前归一化:model_number 仅存本体,model_shop 存店铺前缀。 + * 若 model_shop 已有值则仅纠正 model_number 仍带后缀的情况。 + */ + public static void normalizeOrderModelShop(JDOrder order, List options) { + if (order == null) { + return; + } + String base = StringUtils.trim(order.getModelNumber()); + String shop = StringUtils.trim(order.getModelShop()); + if (StringUtils.isNotEmpty(shop)) { + if (StringUtils.isNotEmpty(base) && base.endsWith(shop) && base.length() > shop.length()) { + order.setModelNumber(base.substring(0, base.length() - shop.length())); + } + order.setModelShop(shop); + return; + } + if (StringUtils.isEmpty(base)) { + order.setModelShop(""); + return; + } + String[] split = splitModelSuffix(base, options); + order.setModelNumber(split[0]); + order.setModelShop(split[1]); + } + + /** 京粉/型号配置 lookup 用本体型号 */ + public static String lookupModelBase(String rawModel, List options) { + String[] split = splitModelSuffix(StringUtils.trim(rawModel), options); + return split[0]; + } } diff --git a/ruoyi-system/src/main/resources/mapper/jarvis/JDOrderMapper.xml b/ruoyi-system/src/main/resources/mapper/jarvis/JDOrderMapper.xml index 8edfc05..812adc6 100644 --- a/ruoyi-system/src/main/resources/mapper/jarvis/JDOrderMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/jarvis/JDOrderMapper.xml @@ -7,6 +7,7 @@ + @@ -46,7 +47,7 @@ - select id, remark, distribution_mark, model_number, link, payment_amount, rebate_amount, + select id, remark, distribution_mark, model_number, model_shop, link, payment_amount, rebate_amount, address, logistics_link, order_id, buyer, order_time, create_time, update_time, status, is_count_enabled, third_party_order_no, jingfen_actual_price, is_refunded, refund_date, is_refund_received, refund_received_date, is_rebate_received, rebate_received_date, is_price_protected, price_protected_date, is_invoice_opened, invoice_opened_date, is_review_posted, review_posted_date, @@ -67,7 +68,13 @@ ) and distribution_mark = #{distributionMark} - and model_number like concat('%', #{modelNumber}, '%') + + and ( + model_number like concat('%', #{modelNumber}, '%') + or concat(IFNULL(model_number, ''), IFNULL(model_shop, '')) like concat('%', #{modelNumber}, '%') + ) + + and model_shop = #{modelShop} and (model_number is null or model_number not like concat('%', #{ex}, '%')) @@ -120,7 +127,13 @@ ) and distribution_mark = #{distributionMark} - and model_number like concat('%', #{modelNumber}, '%') + + and ( + model_number like concat('%', #{modelNumber}, '%') + or concat(IFNULL(model_number, ''), IFNULL(model_shop, '')) like concat('%', #{modelNumber}, '%') + ) + + and model_shop = #{modelShop} and (model_number is null or model_number not like concat('%', #{ex}, '%')) @@ -177,7 +190,7 @@ insert into jd_order ( - remark, distribution_mark, model_number, link, + remark, distribution_mark, model_number, model_shop, link, payment_amount, rebate_amount, address, logistics_link, tencent_doc_pushed, tencent_doc_push_time, order_id, buyer, order_time, create_time, update_time, status, is_count_enabled, third_party_order_no, jingfen_actual_price, @@ -185,7 +198,7 @@ is_price_protected, price_protected_date, is_invoice_opened, invoice_opened_date, is_review_posted, review_posted_date, selling_price_type, selling_price, profit, selling_price_manual, profit_manual, extra_cost ) values ( - #{remark}, #{distributionMark}, #{modelNumber}, #{link}, + #{remark}, #{distributionMark}, #{modelNumber}, #{modelShop}, #{link}, #{paymentAmount}, #{rebateAmount}, #{address}, #{logisticsLink}, 0, null, #{orderId}, #{buyer}, #{orderTime}, now(), now(), #{status}, #{isCountEnabled}, #{thirdPartyOrderNo}, #{jingfenActualPrice}, @@ -201,6 +214,7 @@ remark = #{remark}, distribution_mark = #{distributionMark}, model_number = #{modelNumber}, + model_shop = #{modelShop}, link = #{link}, payment_amount = #{paymentAmount}, rebate_amount = #{rebateAmount}, @@ -305,24 +319,45 @@ + + + + + update jd_order + set model_number = #{modelNumber}, + model_shop = #{modelShop}, + update_time = now() + where id = #{id} + + diff --git a/sql/jd_order_model_shop.sql b/sql/jd_order_model_shop.sql new file mode 100644 index 0000000..3dec5f1 --- /dev/null +++ b/sql/jd_order_model_shop.sql @@ -0,0 +1,2 @@ +-- 京东订单:型号店铺短前缀(与 model_number 拆分存储,便于筛选;完整对外型号 = model_number + model_shop) +ALTER TABLE jd_order ADD COLUMN model_shop VARCHAR(64) DEFAULT NULL COMMENT '型号店铺短前缀(如海尔厨房)';