浏览代码

医健宝代码提交

yjwang 4 天之前
父节点
当前提交
7ef8c90100
共有 35 个文件被更改,包括 1797 次插入130 次删除
  1. 35 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreAfterSalesScrmController.java
  2. 47 18
      fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java
  3. 36 36
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  4. 46 0
      fs-service/src/main/java/com/fs/erp/dto/LogisticsCompanyQueryRequestDTO.java
  5. 50 0
      fs-service/src/main/java/com/fs/erp/dto/LogisticsCompanyQueryResponseDTO.java
  6. 98 0
      fs-service/src/main/java/com/fs/erp/dto/OutboundOrderQueryRequestDTO.java
  7. 146 0
      fs-service/src/main/java/com/fs/erp/dto/OutboundOrderQueryResponseDTO.java
  8. 115 0
      fs-service/src/main/java/com/fs/erp/dto/SkuSnQueryRequestDTO.java
  9. 195 0
      fs-service/src/main/java/com/fs/erp/dto/SkuSnQueryResponseDTO.java
  10. 21 0
      fs-service/src/main/java/com/fs/erp/http/JstErpHttpService.java
  11. 51 0
      fs-service/src/main/java/com/fs/erp/http/JstErpHttpServiceImpl.java
  12. 26 0
      fs-service/src/main/java/com/fs/erp/service/IErpOrderService.java
  13. 47 9
      fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java
  14. 10 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreAfterSalesItemScrmMapper.java
  15. 7 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreAfterSalesScrmMapper.java
  16. 2 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java
  17. 2 1
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java
  18. 23 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreAfterSalesParam.java
  19. 2 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreAfterSalesItemScrmService.java
  20. 5 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreAfterSalesScrmService.java
  21. 5 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java
  22. 5 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesItemScrmServiceImpl.java
  23. 203 30
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java
  24. 313 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  25. 5 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsMyStoreOrderListQueryVO.java
  26. 4 4
      fs-service/src/main/resources/application-config-druid-yjb.yml
  27. 12 12
      fs-service/src/main/resources/application-dev-yjb.yml
  28. 1 0
      fs-service/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml
  29. 4 0
      fs-service/src/main/resources/mapper/hisStore/FsStoreAfterSalesItemScrmMapper.xml
  30. 3 0
      fs-service/src/main/resources/mapper/hisStore/FsStoreAfterSalesScrmMapper.xml
  31. 6 0
      fs-service/src/main/resources/mapper/hisStore/FsStoreOrderItemScrmMapper.xml
  32. 81 1
      fs-store/src/main/java/com/fs/hisStore/controller/store/FsStoreAfterSalesScrmController.java
  33. 149 0
      fs-user-app/src/main/java/com/fs/app/controller/store/LogisticsCompanyScrmController.java
  34. 24 6
      fs-user-app/src/main/java/com/fs/app/controller/store/StoreAfterSalesScrmController.java
  35. 18 12
      fs-user-app/src/main/java/com/fs/app/controller/store/StoreOrderScrmController.java

+ 35 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreAfterSalesScrmController.java

@@ -2,6 +2,7 @@ package com.fs.hisStore.controller;
 
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import cn.hutool.core.util.StrUtil;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -14,7 +15,10 @@ import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.domain.FsUser;
+import com.fs.his.dto.ExpressInfoDTO;
+import com.fs.his.service.IFsExpressService;
 import com.fs.his.service.IFsUserService;
+import com.fs.hisStore.enums.ShipperCodeEnum;
 import com.fs.hisStore.domain.*;
 import com.fs.hisStore.mapper.FsStoreOrderItemScrmMapper;
 import com.fs.hisStore.mapper.FsStoreVerifyCodeScrmMapper;
@@ -67,6 +71,9 @@ public class FsStoreAfterSalesScrmController extends BaseController
     @Autowired
     private FsStoreOrderItemScrmMapper fsStoreOrderItemMapper;
 
+    @Autowired
+    private IFsExpressService expressService;
+
     /**
      * 查询售后记录列表
      */
@@ -105,6 +112,34 @@ public class FsStoreAfterSalesScrmController extends BaseController
         ExcelUtil<FsStoreAfterSalesVO> util = new ExcelUtil<FsStoreAfterSalesVO>(FsStoreAfterSalesVO.class);
         return util.exportExcel(list, "storeAfterSales");
     }
+    /**
+     * 查询用户寄回快递轨迹(售后)
+     */
+    @PreAuthorize("@ss.hasPermi('store:storeAfterSales:query')")
+    @GetMapping(value = "/getExpress/{id}")
+    public R getExpress(@PathVariable("id") Long id) {
+        FsStoreAfterSalesScrm afterSales = fsStoreAfterSalesService.selectFsStoreAfterSalesById(id);
+        ExpressInfoDTO expressInfoDTO = null;
+        if (afterSales != null
+                && StrUtil.isNotBlank(afterSales.getDeliverySn())
+                && StrUtil.isNotBlank(afterSales.getShipperCode())) {
+            String lastFourNumber = "";
+            // 顺丰需要寄件人手机后 4 位;寄回时商家手机号即寄件人志向的收件人手机
+            if (ShipperCodeEnum.SF.getValue().equals(afterSales.getShipperCode())) {
+                String phone = afterSales.getPhoneNumber();
+                if (StrUtil.isNotBlank(phone) && phone.length() >= 4) {
+                    lastFourNumber = StrUtil.sub(phone, phone.length() - 4, phone.length());
+                }
+            }
+            expressInfoDTO = expressService.getExpressInfo(
+                    afterSales.getOrderCode(),
+                    afterSales.getShipperCode(),
+                    afterSales.getDeliverySn(),
+                    lastFourNumber);
+        }
+        return R.ok().put("data", expressInfoDTO);
+    }
+
     /**
      * 获取售后记录详细信息
      */

+ 47 - 18
fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java

@@ -43,13 +43,13 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
 import java.math.BigDecimal;
 import java.text.ParseException;
 import java.time.LocalTime;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
@@ -205,29 +205,58 @@ public class MallStoreTask
     public void deliveryOp()
     {
         List<FsStoreOrderScrm> list = fsStoreOrderMapper.selectUpdateExpress();
-        for (FsStoreOrderScrm order : list){
-            ErpOrderQueryRequert request = new ErpOrderQueryRequert();
-            request.setCode(order.getExtendOrderId());
-            IErpOrderService erpOrderService = getErpOrderService();
-            ErpOrderQueryResponse response = erpOrderService.getScrmOrder(request);
-            if (erpOrderService != dfOrderService) {
-                if(response.getOrders()!=null && !response.getOrders().isEmpty()){
-                    for(ErpOrderQuery orderQuery : response.getOrders()){
-                        if(orderQuery.getDeliverys()!=null&&orderQuery.getDeliverys().size()>0){
-                            for(ErpDeliverys delivery:orderQuery.getDeliverys()){
-                                if(delivery.getDelivery()&&StringUtils.isNotEmpty(delivery.getMail_no())){
-                                    //更新商订单状态 删除REDIS
-                                    orderService.deliveryOrder(order.getOrderCode(),delivery.getMail_no(),delivery.getExpress_code(),delivery.getExpress_name());
-                                    redisCache.deleteObject(DELIVERY+":"+order.getExtendOrderId());
-                                }
-                            }
+        if (list == null || list.isEmpty()) {
+            return;
+        }
+
+        IErpOrderService erpOrderService = getErpOrderService();
+        if (erpOrderService == null) {
+            return;
+        }
+
+        // 代服走单独逻辑
+        if (erpOrderService == dfOrderService) {
+            for (FsStoreOrderScrm order : list) {
+                ErpOrderQueryRequert request = new ErpOrderQueryRequert();
+                request.setCode(order.getExtendOrderId());
+                erpOrderService.getScrmOrder(request);
+            }
+            return;
+        }
+
+        // 其他ERP走批量查询
+        List<String> codes = new ArrayList<>();
+        Map<String, FsStoreOrderScrm> orderMap = new HashMap<>();
+        for (FsStoreOrderScrm order : list) {
+            codes.add(order.getExtendOrderId());
+            orderMap.put(order.getOrderCode(), order);
+        }
+
+        ErpOrderQueryResponse response = erpOrderService.getScrmOrderBatch(codes);
+        if (response == null || response.getOrders() == null || response.getOrders().isEmpty()) {
+            return;
+        }
+
+        List<FsStoreOrderScrm> deliveredOrders = new ArrayList<>();
+        for (ErpOrderQuery orderQuery : response.getOrders()) {
+            if (orderQuery.getDeliverys() != null && !orderQuery.getDeliverys().isEmpty()) {
+                for (ErpDeliverys delivery : orderQuery.getDeliverys()) {
+                    if (delivery.getDelivery() && StringUtils.isNotEmpty(delivery.getMail_no())) {
+                        FsStoreOrderScrm matchedOrder = orderMap.get(orderQuery.getCode());
+                        if (matchedOrder != null) {
+                            orderService.deliveryOrder(matchedOrder.getOrderCode(), delivery.getMail_no(), delivery.getExpress_code(), delivery.getExpress_name());
+                            redisCache.deleteObject(DELIVERY + ":" + matchedOrder.getExtendOrderId());
+                            deliveredOrders.add(matchedOrder);
                         }
                     }
-
                 }
             }
         }
 
+        // 批量同步聚水潭溯源码和批次号
+        if (!deliveredOrders.isEmpty()) {
+            orderService.syncSkuSnFromJstBatch(deliveredOrders);
+        }
     }
 
 

+ 36 - 36
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -445,47 +445,47 @@ public class CompanyServiceImpl implements ICompanyService
     @Override
     @Transactional
     public void addCompanyTuiMoney(FsStoreOrder order) {
-        if(order.getCompanyId()!=null&&order.getCompanyId()>0){
-            Company company=companyMapper.selectCompanyByIdForUpdate(order.getCompanyId());
-            if(company!=null){
-                String json =configService.selectConfigByKey("his.store");
-                StoreConfig config= JSONUtil.toBean(json,StoreConfig.class);
-                //支付金额-(订单金额*rate%)
-                Double rate=config.getTuiMoneyRate()/100d;
-                BigDecimal tuiMoney=order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
-                logger.info("写入公司推广佣金:"+tuiMoney);
-                company.setTuiMoney(company.getTuiMoney().add(tuiMoney));
-                companyMapper.updateCompany(company);
-                FsStoreOrder storeOrderMap=new FsStoreOrder();
-                storeOrderMap.setOrderId(order.getOrderId());
-                storeOrderMap.setTuiMoney(tuiMoney);
-                storeOrderMapper.updateFsStoreOrder(storeOrderMap);
-            }
-        }
+//        if(order.getCompanyId()!=null&&order.getCompanyId()>0){
+//            Company company=companyMapper.selectCompanyByIdForUpdate(order.getCompanyId());
+//            if(company!=null){
+//                String json =configService.selectConfigByKey("his.store");
+//                StoreConfig config= JSONUtil.toBean(json,StoreConfig.class);
+//                //支付金额-(订单金额*rate%)
+//                Double rate=config.getTuiMoneyRate()/100d;
+//                BigDecimal tuiMoney=order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
+//                logger.info("写入公司推广佣金:"+tuiMoney);
+//                company.setTuiMoney(company.getTuiMoney().add(tuiMoney));
+//                companyMapper.updateCompany(company);
+//                FsStoreOrder storeOrderMap=new FsStoreOrder();
+//                storeOrderMap.setOrderId(order.getOrderId());
+//                storeOrderMap.setTuiMoney(tuiMoney);
+//                storeOrderMapper.updateFsStoreOrder(storeOrderMap);
+//            }
+//        }
     }
 
     @Override
     @Transactional
     public void addCompanyTuiMoney(FsStoreOrderScrm order) {
-        if(order.getCompanyId()!=null&&order.getCompanyId()>0){
-            Company company=companyMapper.selectCompanyByIdForUpdate(order.getCompanyId());
-            if(company!=null){
-                String json =configService.selectConfigByKey("his.store");
-                StoreConfig config= JSONUtil.toBean(json,StoreConfig.class);
-                //支付金额-(订单金额*rate%)
-                if (config.getTuiMoneyRate()!=null){
-                    Double rate=config.getTuiMoneyRate()/100d;
-                    BigDecimal tuiMoney=order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
-                    logger.info("写入公司推广佣金:"+tuiMoney);
-                    company.setTuiMoney(company.getTuiMoney().add(tuiMoney));
-                    companyMapper.updateCompany(company);
-                    FsStoreOrderScrm storeOrderMap=new FsStoreOrderScrm();
-                    storeOrderMap.setId(order.getId());
-                    storeOrderMap.setTuiMoney(tuiMoney);
-                    storeOrderScrmMapper.updateFsStoreOrder(storeOrderMap);
-                }
-            }
-        }
+//        if(order.getCompanyId()!=null&&order.getCompanyId()>0){
+//            Company company=companyMapper.selectCompanyByIdForUpdate(order.getCompanyId());
+//            if(company!=null){
+//                String json =configService.selectConfigByKey("his.store");
+//                StoreConfig config= JSONUtil.toBean(json,StoreConfig.class);
+//                //支付金额-(订单金额*rate%)
+//                if (config.getTuiMoneyRate()!=null){
+//                    Double rate=config.getTuiMoneyRate()/100d;
+//                    BigDecimal tuiMoney=order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
+//                    logger.info("写入公司推广佣金:"+tuiMoney);
+//                    company.setTuiMoney(company.getTuiMoney().add(tuiMoney));
+//                    companyMapper.updateCompany(company);
+//                    FsStoreOrderScrm storeOrderMap=new FsStoreOrderScrm();
+//                    storeOrderMap.setId(order.getId());
+//                    storeOrderMap.setTuiMoney(tuiMoney);
+//                    storeOrderScrmMapper.updateFsStoreOrder(storeOrderMap);
+//                }
+//            }
+//        }
     }
 
     @Override

+ 46 - 0
fs-service/src/main/java/com/fs/erp/dto/LogisticsCompanyQueryRequestDTO.java

@@ -0,0 +1,46 @@
+package com.fs.erp.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 聚水潭物流(快递)公司查询请求DTO
+ * 接口: /open/logisticscompany/query
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class LogisticsCompanyQueryRequestDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 第几页,默认第一页开始
+     */
+    private Integer pageIndex;
+
+    /**
+     * 每页多少条,默认30条,最大50条
+     */
+    private Integer pageSize;
+
+    /**
+     * 修改起始时间
+     */
+    private String modifiedBegin;
+
+    /**
+     * 修改结束时间
+     */
+    private String modifiedEnd;
+
+    /**
+     * 公司编号,该值存在时查询对应公司或分仓下的启用的物流公司信息,否则查询系统内所有的物流公司信息
+     */
+    private Integer wmsCoId;
+}

+ 50 - 0
fs-service/src/main/java/com/fs/erp/dto/LogisticsCompanyQueryResponseDTO.java

@@ -0,0 +1,50 @@
+package com.fs.erp.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 聚水潭物流(快递)公司查询响应DTO
+ * 接口: /open/logisticscompany/query
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class LogisticsCompanyQueryResponseDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private Integer code;
+    private String msg;
+    private DataWrapper data;
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class DataWrapper implements Serializable {
+        private static final long serialVersionUID = 1L;
+        private Integer pageIndex;
+        private Integer pageSize;
+        private Integer dataCount;
+        private Integer pageCount;
+        private Boolean hasNext;
+        private List<LogisticsCompany> datas;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class LogisticsCompany implements Serializable {
+        private static final long serialVersionUID = 1L;
+        /** 快递公司编码(ERP唯一) */
+        private String lcId;
+        /** 快递公司名称 */
+        private String lcName;
+        /** 修改时间 */
+        private String modified;
+    }
+}

+ 98 - 0
fs-service/src/main/java/com/fs/erp/dto/OutboundOrderQueryRequestDTO.java

@@ -0,0 +1,98 @@
+package com.fs.erp.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 聚水潭销售出库查询请求DTO
+ * 接口: /open/orders/out/simple/query
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class OutboundOrderQueryRequestDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 店铺编码
+     */
+    private Integer shopId;
+
+    /**
+     * 是否线下店铺
+     */
+    private Boolean isOfflineShop;
+
+    /**
+     * 单据状态
+     * WaitConfirm=待出库;Confirmed=已出库;Delete=作废;Cancelled=取消;OuterConfirming=外部发货中;Archive=已结算
+     */
+    private String status;
+
+    /**
+     * 起始时间(和结束时间必须同时存在,时间间隔不能超过七天)
+     */
+    private String modifiedBegin;
+
+    /**
+     * 结束时间
+     */
+    private String modifiedEnd;
+
+    /**
+     * 线上单号列表(最大50)
+     */
+    private List<String> soIds;
+
+    /**
+     * 第几页,从第一页开始,默认1
+     */
+    private Integer pageIndex;
+
+    /**
+     * 每页多少条,默认30,最大50
+     */
+    private Integer pageSize;
+
+    /**
+     * 出库仓编号
+     */
+    private Integer wmsCoId;
+
+    /**
+     * 内部单号列表(最大50条)
+     */
+    private List<String> oIds;
+
+    /**
+     * 物流单号列表(不超过20条)
+     */
+    private List<String> lIds;
+
+    /**
+     * 出库单号列表(最大50)
+     */
+    private List<String> ioIds;
+
+    /**
+     * 时间戳(sql server行版本号,防止分页漏单)
+     */
+    private Integer startTs;
+
+    /**
+     * 是否查询总条数(默认true,使用start_ts查询建议传false)
+     */
+    private Boolean isGetTotal;
+
+    /**
+     * 时间类型 0:修改时间modified,2:出库时间io_date;默认0
+     */
+    private Integer dateType;
+}

+ 146 - 0
fs-service/src/main/java/com/fs/erp/dto/OutboundOrderQueryResponseDTO.java

@@ -0,0 +1,146 @@
+package com.fs.erp.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 聚水潭销售出库查询响应DTO
+ * 接口: /open/orders/out/simple/query
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class OutboundOrderQueryResponseDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private Integer code;
+    private String msg;
+    private DataWrapper data;
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class DataWrapper {
+        private Integer pageSize;
+        private Integer pageIndex;
+        private Integer dataCount;
+        private Integer pageCount;
+        private Boolean hasNext;
+        private List<OutboundOrder> datas;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class OutboundOrder {
+        /** 公司编号 */
+        private Long coId;
+        /** 店铺编码 */
+        private Long shopId;
+        /** 出库单号 */
+        private Long ioId;
+        /** 内部单号 */
+        private Long oId;
+        /** 线上单号 */
+        private String soId;
+        /** 创建时间 */
+        private String created;
+        /** 修改时间 */
+        private String modified;
+        /** 状态 */
+        private String status;
+        /** 物流单号 */
+        private String lId;
+        /** 出库时间 */
+        private String ioDate;
+        /** 快递公司编码 */
+        private String lcId;
+        /** 物流公司名称 */
+        private String logisticsCompany;
+        /** 订单类型 */
+        private String orderType;
+        /** 商品集合 */
+        private List<OutboundItem> items;
+        /** 生产批次集合 */
+        private List<BatchInfo> batchs;
+        /** 唯一码集合 */
+        private List<SnInfo> sns;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class OutboundItem {
+        /** 子单号 */
+        private Long ioiId;
+        /** 商品编码 */
+        private String skuId;
+        /** 数量 */
+        private Integer qty;
+        /** 商品名称 */
+        private String name;
+        /** 颜色规格 */
+        private String propertiesValue;
+        /** 单价 */
+        private BigDecimal salePrice;
+        /** 金额 */
+        private BigDecimal saleAmount;
+        /** 批次号 */
+        private String batchId;
+        /** 批次日期 */
+        private String productDate;
+        /** 有效期至 */
+        private String expirationDate;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class BatchInfo {
+        /** 批次号 */
+        private String batchNo;
+        /** 出库单商品明细单号 */
+        private Long ioiId;
+        /** 商品编码 */
+        private String skuId;
+        /** 数量 */
+        private Integer qty;
+        /** 批次日期 */
+        private String productDate;
+        /** 供应商编号 */
+        private Integer supplierId;
+        /** 供应商名称 */
+        private String supplierName;
+        /** 有效期至 */
+        private String expirationDate;
+        /** 状态 */
+        private String status;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class SnInfo {
+        /** 商品编码 */
+        private String skuId;
+        /** 唯一码 */
+        private String sn;
+        /** 生产批次号(仅开启唯一码生产批次溯源流程下有效) */
+        private String batchId;
+        /** 生产日期 */
+        private String producedDate;
+        /** 有效期至 */
+        private String expirationDate;
+        /** 是否箱唯一码 */
+        private Boolean isPackSn;
+        /** 箱码 */
+        private String packSn;
+    }
+}

+ 115 - 0
fs-service/src/main/java/com/fs/erp/dto/SkuSnQueryRequestDTO.java

@@ -0,0 +1,115 @@
+package com.fs.erp.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 聚水潭唯一码/溯源码查询请求DTO
+ * 接口: /open/jushuitan/skusn/query
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class SkuSnQueryRequestDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 唯一码列表
+     */
+    private List<String> skuSns;
+
+    /**
+     * 出库单Id列表
+     */
+    private List<Long> ioIds;
+
+    /**
+     * 批次Id列表
+     */
+    private List<String> waveIds;
+
+    /**
+     * 箱号列表
+     */
+    private List<String> packIds;
+
+    /**
+     * 仓位列表
+     */
+    private List<String> bins;
+
+    /**
+     * 状态列表
+     * Pack:入库;Out:出库;Pick:拣货;Checkout:验货出库;Seed:播种;
+     * PickReturn:拣货归还;Pick1:一单一件拣货;Pick2:一单多件拣货;
+     * PaperPick:纸质拣货;WaitConfirm:待出库;WaitPack:待入库;
+     */
+    private List<String> status;
+
+    /**
+     * 排除状态
+     */
+    private List<String> exceptStatuses;
+
+    /**
+     * 是否排除没有生产批次的记录
+     */
+    private Boolean isExceptNoBatch;
+
+    /**
+     * 查询属性(返回数据属性)
+     */
+    private List<String> flds;
+
+    /**
+     * 第几页
+     */
+    private Integer pageIndex;
+
+    /**
+     * 每页多少条
+     */
+    private Integer pageSize;
+
+    /**
+     * 修改时间起始值
+     */
+    private String modifiedBegin;
+
+    /**
+     * 修改时间截止值
+     */
+    private String modifiedEnd;
+
+    /**
+     * 时间类型;默认0:修改时间
+     */
+    private Integer dateTime;
+
+    /**
+     * 唯一码类型 1:唯一码/序列号, 3:生产批次, 4:快销
+     */
+    private List<Integer> types;
+
+    /**
+     * 商品编码列表
+     */
+    private List<String> skuIds;
+
+    /**
+     * 内部单号列表(聚水潭oId)
+     */
+    private List<Long> oIds;
+
+    /**
+     * 线上单号列表
+     */
+    private List<String> soIds;
+}

+ 195 - 0
fs-service/src/main/java/com/fs/erp/dto/SkuSnQueryResponseDTO.java

@@ -0,0 +1,195 @@
+package com.fs.erp.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 聚水潭唯一码/溯源码查询响应DTO
+ * 接口: /open/jushuitan/skusn/query
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class SkuSnQueryResponseDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 响应码
+     */
+    private String code;
+
+    /**
+     * 是否成功
+     */
+    private String issuccess;
+
+    /**
+     * 响应信息
+     */
+    private String msg;
+
+    /**
+     * 数据列表
+     */
+    private List<SkuSnItem> data;
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class SkuSnItem implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * 唯一码/溯源码
+         */
+        private String skuSn;
+
+        /**
+         * 公司编号
+         */
+        private Integer coId;
+
+        /**
+         * 商品编码
+         */
+        private String skuId;
+
+        /**
+         * 状态
+         */
+        private String status;
+
+        /**
+         * 批次Id
+         */
+        private String waveId;
+
+        /**
+         * 出库单Id
+         */
+        private String ioId;
+
+        /**
+         * 仓位
+         */
+        private String bin;
+
+        /**
+         * 修改时间
+         */
+        private String modified;
+
+        /**
+         * 箱号
+         */
+        private String packId;
+
+        /**
+         * 款式编码
+         */
+        private String iId;
+
+        /**
+         * 生产批次号
+         */
+        private String lcId;
+
+        /**
+         * 支付时间
+         */
+        private String payDate;
+
+        /**
+         * 商品信息
+         */
+        private String skuInfo;
+
+        /**
+         * 供应商
+         */
+        private String supplierName;
+
+        /**
+         * 打印日期
+         */
+        private String printDate;
+
+        /**
+         * 创建时间
+         */
+        private String created;
+
+        /**
+         * 创建人
+         */
+        private String creator;
+
+        /**
+         * 分仓编号
+         */
+        private String wmsCoId;
+
+        /**
+         * 店铺编号
+         */
+        private String shopId;
+
+        /**
+         * 备注
+         */
+        private String remark;
+
+        /**
+         * 总数量
+         */
+        private String totalQty;
+
+        /**
+         * 批次生产效期
+         */
+        private String ioDate;
+
+        /**
+         * 内部单号
+         */
+        private String oId;
+
+        /**
+         * 数量
+         */
+        private String qty;
+
+        /**
+         * 类型(序列号:SerialNumber 生产批批次:ProducedBatch ,快销采购:p2d)
+         */
+        private String type;
+
+        /**
+         * 生产批次号(唯一码页签)
+         */
+        private String batchId;
+
+        /**
+         * 生产日期
+         */
+        private String producedDate;
+
+        /**
+         * 批次有效期
+         */
+        private String expirationDate;
+
+        /**
+         * 时间戳
+         */
+        private String ts;
+    }
+}

+ 21 - 0
fs-service/src/main/java/com/fs/erp/http/JstErpHttpService.java

@@ -77,4 +77,25 @@ public interface JstErpHttpService {
      */
     CommonResponse<AssetProcessResultDTO> aftersaleConfirm(AfterSaleConfirmRequestDTO dto);
 
+    /**
+     * 唯一码/溯源码跟踪信息查询
+     * @param dto 请求参数
+     * @return SkuSnQueryResponseDTO
+     */
+    SkuSnQueryResponseDTO querySkuSn(SkuSnQueryRequestDTO dto);
+
+    /**
+     * 销售出库查询(获取批次号和唯一码)
+     * @param dto 请求参数
+     * @return OutboundOrderQueryResponseDTO
+     */
+    OutboundOrderQueryResponseDTO queryOutboundOrder(OutboundOrderQueryRequestDTO dto);
+
+    /**
+     * 物流(快递)公司查询
+     * @param dto 请求参数
+     * @return LogisticsCompanyQueryResponseDTO
+     */
+    LogisticsCompanyQueryResponseDTO queryLogisticsCompany(LogisticsCompanyQueryRequestDTO dto);
+
 }

+ 51 - 0
fs-service/src/main/java/com/fs/erp/http/JstErpHttpServiceImpl.java

@@ -147,6 +147,57 @@ public class JstErpHttpServiceImpl implements JstErpHttpService {
         return parseCommonResponse(response);
     }
 
+    @Override
+    public SkuSnQueryResponseDTO querySkuSn(SkuSnQueryRequestDTO dto) {
+        String url = BASE_URL + "open/jushuitan/skusn/query";
+        log.info("查询唯一码/溯源码 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
+
+        HttpResponse response = executeJsonPost(url, dto);
+        String body = response.body();
+        log.debug("溯源码查询响应体: {}", body);
+        SkuSnQueryResponseDTO result = JSON.parseObject(body, SkuSnQueryResponseDTO.class);
+        if (result == null || !"0".equals(result.getCode())) {
+            String errMsg = result != null ? result.getMsg() : "请求失败";
+            log.error("溯源码查询失败: {}", errMsg);
+            throw new RuntimeException("查询溯源码失败!原因:" + errMsg);
+        }
+        return result;
+    }
+
+    @Override
+    public OutboundOrderQueryResponseDTO queryOutboundOrder(OutboundOrderQueryRequestDTO dto) {
+        String url = BASE_URL + "open/orders/out/simple/query";
+        log.info("销售出库查询 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
+
+        HttpResponse response = executeJsonPost(url, dto);
+        String body = response.body();
+        log.debug("销售出库查询响应体: {}", body);
+        OutboundOrderQueryResponseDTO result = JSON.parseObject(body, OutboundOrderQueryResponseDTO.class);
+        if (result == null || result.getCode() == null || result.getCode() != 0) {
+            String errMsg = result != null ? result.getMsg() : "请求失败";
+            log.error("销售出库查询失败: {}", errMsg);
+            throw new RuntimeException("销售出库查询失败!原因:" + errMsg);
+        }
+        return result;
+    }
+
+    @Override
+    public LogisticsCompanyQueryResponseDTO queryLogisticsCompany(LogisticsCompanyQueryRequestDTO dto) {
+        String url = BASE_URL + "open/logisticscompany/query";
+        log.info("物流公司查询 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
+
+        HttpResponse response = executeJsonPost(url, dto);
+        String body = response.body();
+        log.debug("物流公司查询响应体: {}", body);
+        LogisticsCompanyQueryResponseDTO result = JSON.parseObject(body, LogisticsCompanyQueryResponseDTO.class);
+        if (result == null || result.getCode() == null || result.getCode() != 0) {
+            String errMsg = result != null ? result.getMsg() : "请求失败";
+            log.error("物流公司查询失败: {}", errMsg);
+            throw new RuntimeException("物流公司查询失败!原因:" + errMsg);
+        }
+        return result;
+    }
+
     /**
      * 执行JSON格式的POST请求
      *

+ 26 - 0
fs-service/src/main/java/com/fs/erp/service/IErpOrderService.java

@@ -1,11 +1,15 @@
 package com.fs.erp.service;
 
 import com.fs.erp.domain.ErpOrder;
+import com.fs.erp.domain.ErpOrderQuery;
 import com.fs.erp.domain.ErpRefundOrder;
 import com.fs.erp.dto.*;
 import com.fs.his.domain.FsStoreOrder;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public interface IErpOrderService
 {
 
@@ -27,5 +31,27 @@ public interface IErpOrderService
     void getOrderDeliveryStatus(FsStoreOrder order);
 
     void getOrderScrmDeliveryStatus(FsStoreOrderScrm order);
+
+    /**
+     * 批量查询ERP订单
+     */
+    default ErpOrderQueryResponse getScrmOrderBatch(List<String> codes) {
+        ErpOrderQueryResponse combinedResponse = new ErpOrderQueryResponse();
+        List<ErpOrderQuery> allOrders = new ArrayList<>();
+        for (String code : codes) {
+            ErpOrderQueryRequert request = new ErpOrderQueryRequert();
+            request.setCode(code);
+            try {
+                ErpOrderQueryResponse response = getScrmOrder(request);
+                if (response != null && response.getOrders() != null) {
+                    allOrders.addAll(response.getOrders());
+                }
+            } catch (Exception e) {
+                // 单条失败不影响整体
+            }
+        }
+        combinedResponse.setOrders(allOrders);
+        return combinedResponse;
+    }
 }
 

+ 47 - 9
fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java

@@ -395,17 +395,8 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         // 1. 构建查询请求DTO
         OrderQueryRequestDTO requestDTO = new OrderQueryRequestDTO();
         requestDTO.setOIds(Collections.singletonList(Long.valueOf(param.getCode())));
-
-
-        // 2. 调用ERP服务查询订单
         OrderQueryResponseDTO query = jstErpHttpService.query(requestDTO);
-
-        // 3. 构建响应对象
         ErpOrderQueryResponse response = new ErpOrderQueryResponse();
-
-        // 4. 设置基本响应信息
-
-        // 5. 转换订单数据
         if (query.getOrders() != null && !query.getOrders().isEmpty()) {
             List<ErpOrderQuery> erpOrders = query.getOrders().stream()
                     .map(this::convertToErpOrderQueryScrm)
@@ -418,6 +409,53 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
 
         return response;
     }
+
+    /**
+     * 批量查询聚水潭订单(按20个一组分批查询,避免API请求频繁)
+     */
+    @Override
+    public ErpOrderQueryResponse getScrmOrderBatch(List<String> codes) {
+        ErpOrderQueryResponse combinedResponse = new ErpOrderQueryResponse();
+        List<ErpOrderQuery> allOrders = new ArrayList<>();
+
+        // 按20个一组分批查询(聚水潭API限制)
+        int batchSize = 20;
+        for (int i = 0; i < codes.size(); i += batchSize) {
+            List<String> batch = codes.subList(i, Math.min(i + batchSize, codes.size()));
+            List<Long> oIds = batch.stream().map(Long::valueOf).collect(Collectors.toList());
+
+            OrderQueryRequestDTO requestDTO = new OrderQueryRequestDTO();
+            requestDTO.setOIds(oIds);
+
+            try {
+                OrderQueryResponseDTO query = jstErpHttpService.query(requestDTO);
+                if (query != null && query.getOrders() != null && !query.getOrders().isEmpty()) {
+                    for (OrderQueryResponseDTO.Order order : query.getOrders()) {
+                        try {
+                            ErpOrderQuery erpOrder = convertToErpOrderQueryScrm(order);
+                            allOrders.add(erpOrder);
+                        } catch (Exception e) {
+                            log.error("转换订单数据失败, soId: {}", order.getSoId(), e);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                log.error("批量查询聚水潭订单失败, oIds: {}", batch, e);
+            }
+
+            // 每批次之间稍微停顿,避免触发频率限制
+            if (i + batchSize < codes.size()) {
+                try {
+                    Thread.sleep(200);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+
+        combinedResponse.setOrders(allOrders);
+        return combinedResponse;
+    }
     /**
      * 将OrderQueryResponseDTO.Order转换为ErpOrderQuery
      *

+ 10 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreAfterSalesItemScrmMapper.java

@@ -2,6 +2,7 @@ package com.fs.hisStore.mapper;
 
 import java.util.List;
 import com.fs.hisStore.domain.FsStoreAfterSalesItemScrm;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 售后子Mapper接口
@@ -58,4 +59,13 @@ public interface FsStoreAfterSalesItemScrmMapper
      * @return 结果
      */
     public int deleteFsStoreAfterSalesItemByIds(Long[] ids);
+
+
+    /**
+     * 删除售后子
+     *
+     * @param id 售后ID
+     * @return 结果
+     */
+    public int deleteFsStoreAfterSalesItemByAfterSalesId(@Param("id") Long id);
 }

+ 7 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreAfterSalesScrmMapper.java

@@ -294,4 +294,11 @@ public interface FsStoreAfterSalesScrmMapper
 
     @Select(" SELECT id FROM fs_store_order_scrm WHERE order_code =#{id}")
     Long selectFsOrderIdByCode(Long id);
+
+    /**
+     * 获取售后类型
+     * @param orderCode 订单编码
+     * @return 售后类型
+     * **/
+    Integer getAfterSalesType(@Param("orderCode") String orderCode);
 }

+ 2 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java

@@ -269,6 +269,8 @@ public interface FsStoreOrderItemScrmMapper
 
     List<FsStoreOrderItemScrm> selectFsStoreOrderItemByOrderCode(String orderCode);
 
+    List<FsStoreOrderItemScrm> selectFsStoreOrderItemByOrderCodes(@Param("orderCodes") List<String> orderCodes);
+
     void batchUpdateVerifyCodeByOrderCode(List<FsStoreOrderItemScrm> batchList);
 
     /**

+ 2 - 1
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java

@@ -472,7 +472,8 @@ public interface FsStoreOrderScrmMapper extends BaseMapper<FsStoreOrderScrm>
     List<FsPromotionOrderVO> selectFsPromotionOrderListVO(@Param("maps")FsStoreOrderParam param);
 
     @Select({"<script> " +
-            "select o.id,o.order_code,o.item_json,o.pay_price,o.status,o.is_package,o.package_json,o.delivery_id,o.finish_time,o.prescribe_id,o.is_prescribe,hps.audit_status AS prescriptionAuditStatus,ss.store_name  from fs_store_order_scrm o  LEFT JOIN fs_store_hospital580_prescription_scrm hps ON hps.store_order_id = o.id  LEFT JOIN fs_store_scrm ss ON ss.store_id = o.store_id\n" +
+            "select o.id,o.order_code,o.item_json,o.pay_price,o.status,o.is_package,o.package_json,o.delivery_id,o.finish_time,o.prescribe_id,o.is_prescribe,hps.audit_status AS prescriptionAuditStatus,ss.store_name,ass.`status` AS after_sales_type   from fs_store_order_scrm o  LEFT JOIN fs_store_hospital580_prescription_scrm hps ON hps.store_order_id = o.id  LEFT JOIN fs_store_scrm ss ON ss.store_id = o.store_id\n" +
+            "LEFT JOIN fs_store_after_sales_scrm ass ON o.order_code = ass.order_code\n" +
             "where o.is_del=0 and o.is_sys_del=0 " +
             "<if test = 'maps.status != null and maps.status != \"\"     '> " +
             "and o.status =#{maps.status} " +

+ 23 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreAfterSalesParam.java

@@ -1,5 +1,6 @@
 package com.fs.hisStore.param;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fs.hisStore.param.FsStoreAfterSalesProductParam;
 import lombok.Data;
 
@@ -47,4 +48,26 @@ public class FsStoreAfterSalesParam implements Serializable
      */
     @NotBlank
     private List<FsStoreAfterSalesProductParam> productList;
+
+    /** 商家收货人(可选,与物流信息一起提交时使用) */
+    private String consignee;
+
+    /** 商家手机号(可选) */
+    @JsonProperty("phone_number")
+    private String phoneNumber;
+
+    /** 商家地址(可选) */
+    private String address;
+
+    /** 物流公司编码 lcId(可选) */
+    @JsonProperty("shipper_code")
+    private String shipperCode;
+
+    /** 物流单号(可选) */
+    @JsonProperty("delivery_sn")
+    private String deliverySn;
+
+    /** 物流名称 lcName(可选) */
+    @JsonProperty("delivery_name")
+    private String deliveryName;
 }

+ 2 - 0
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreAfterSalesItemScrmService.java

@@ -58,4 +58,6 @@ public interface IFsStoreAfterSalesItemScrmService
      * @return 结果
      */
     public int deleteFsStoreAfterSalesItemById(Long id);
+
+    public int deleteFsStoreAfterSalesItemByAfterSalesId(Long id);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreAfterSalesScrmService.java

@@ -117,4 +117,9 @@ public interface IFsStoreAfterSalesScrmService
     int noAuditing(FsStoreAfterSalesScrm fsStoreAfterSales);
 
     int storeRefundMoney(FsStoreAfterSalesScrm fsStoreAfterSales);
+
+    /**
+     * 获取订单售后类型
+     * **/
+    Integer getAfterSalesType(String orderCode);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java

@@ -113,6 +113,11 @@ public interface IFsStoreOrderScrmService
 
     void deliveryOrder(String orderCode,String deliveryId,String deliverCode,String deliverName);
 
+    /**
+     * 批量同步聚水潭溯源码和批次号
+     */
+    void syncSkuSnFromJstBatch(List<FsStoreOrderScrm> orders);
+
     void updateDeliveryOrder(Long id,String deliveryId,String deliverCode,String deliverName);
 
     List<FsMyStoreOrderListQueryVO> selectFsMyStoreOrderListVO(FsMyStoreOrderQueryParam param);

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesItemScrmServiceImpl.java

@@ -117,4 +117,9 @@ public class FsStoreAfterSalesItemScrmServiceImpl implements IFsStoreAfterSalesI
     {
         return fsStoreAfterSalesItemMapper.deleteFsStoreAfterSalesItemById(id);
     }
+
+    @Override
+    public int deleteFsStoreAfterSalesItemByAfterSalesId(Long id) {
+        return fsStoreAfterSalesItemMapper.deleteFsStoreAfterSalesItemByAfterSalesId(id);
+    }
 }

+ 203 - 30
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java

@@ -24,11 +24,16 @@ import com.fs.erp.constant.AfterSalesOrderStatusEnum;
 import com.fs.erp.domain.FsJstAftersalePush;
 import com.fs.erp.dto.BaseResponse;
 import com.fs.erp.dto.ErpRefundUpdateRequest;
+import com.fs.erp.dto.OrderQueryRequestDTO;
+import com.fs.erp.dto.OrderQueryResponseDTO;
+import com.fs.erp.dto.RefundItemDTO;
+import com.fs.erp.http.JstErpHttpService;
 import com.fs.erp.mapper.FsJstAftersalePushMapper;
 import com.fs.erp.mapper.FsJstAftersalePushScrmMapper;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.domain.*;
+import com.fs.his.dto.FsStoreCartDTO;
 import com.fs.his.enums.FsStoreAfterSalesStatusEnum;
 import com.fs.his.enums.FsStoreOrderLogEnum;
 import com.fs.his.enums.FsStoreOrderStatusEnum;
@@ -60,6 +65,7 @@ import com.fs.tzBankPay.doman.RefundParam;
 import com.fs.tzBankPay.doman.RefundResult;
 import com.fs.tzBankPay.doman.TzBankResult;
 import com.fs.ybPay.dto.RefundDTO;
+import com.fs.ybPay.dto.RefundOrderDTO;
 import com.fs.ybPay.dto.YopRefundRequestDTO;
 import com.fs.ybPay.dto.YopRefundResponseDTO;
 import com.fs.ybPay.service.IYopPayService;
@@ -77,6 +83,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.interceptor.TransactionAspectSupport;
@@ -145,6 +152,14 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
     @Qualifier("k9OrderScrmServiceImpl")
     private IErpOrderService k9OrderService;
 
+    /** 聚水潭 HTTP 客户端:用于调用 /open/orders/single/query 订单查询接口 */
+    @Autowired
+    private JstErpHttpService jstErpHttpService;
+
+    /** 聚水潭店铺编号,用于订单查询时填充 shop_id */
+    @Value("${jst.shop_code:0}")
+    private String jstShopCode;
+
     @Autowired
     private ISysConfigService configService;
 
@@ -377,6 +392,15 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
 //
 //            }
 //        }
+        //清理原始售后信息
+        List<FsStoreAfterSalesVO> afterSalesVOList = fsStoreAfterSalesMapper.selectFsStoreAfterSalesVOByOrderCode(order.getOrderCode());
+        if(!afterSalesVOList.isEmpty()){
+            afterSalesVOList.forEach(v -> {
+                fsStoreAfterSalesMapper.deleteFsStoreAfterSalesById(v.getId());
+                //详情删除
+                afterSalesItemService.deleteFsStoreAfterSalesItemById(v.getId());
+            });
+        }
 
         //生成售后订单
         FsStoreAfterSalesScrm storeAfterSales = new FsStoreAfterSalesScrm();
@@ -396,6 +420,29 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
         storeAfterSales.setCompanyUserId(order.getCompanyUserId());
         storeAfterSales.setPackageJson(order.getPackageJson());
         storeAfterSales.setIsPackage(order.getIsPackage());
+        // 兼容“一步提交”:若同时传了商家收货信息+物流信息,一并写入售后单
+        // 有寄回物流单号即视为退货退款,service_type 强制为 3
+        if (StringUtils.isNotBlank(storeAfterSalesParam.getDeliverySn())) {
+            storeAfterSales.setServiceType(3);
+        }
+        if (StringUtils.isNotBlank(storeAfterSalesParam.getConsignee())) {
+            storeAfterSales.setConsignee(storeAfterSalesParam.getConsignee());
+        }
+        if (StringUtils.isNotBlank(storeAfterSalesParam.getPhoneNumber())) {
+            storeAfterSales.setPhoneNumber(storeAfterSalesParam.getPhoneNumber());
+        }
+        if (StringUtils.isNotBlank(storeAfterSalesParam.getAddress())) {
+            storeAfterSales.setAddress(storeAfterSalesParam.getAddress());
+        }
+        if (StringUtils.isNotBlank(storeAfterSalesParam.getShipperCode())) {
+            storeAfterSales.setShipperCode(storeAfterSalesParam.getShipperCode());
+        }
+        if (StringUtils.isNotBlank(storeAfterSalesParam.getDeliverySn())) {
+            storeAfterSales.setDeliverySn(storeAfterSalesParam.getDeliverySn());
+        }
+        if (StringUtils.isNotBlank(storeAfterSalesParam.getDeliveryName())) {
+            storeAfterSales.setDeliveryName(storeAfterSalesParam.getDeliveryName());
+        }
         fsStoreAfterSalesMapper.insertFsStoreAfterSales(storeAfterSales);
         //售后商品详情
         for (FsStoreAfterSalesProductParam productParam : storeAfterSalesParam.getProductList()) {
@@ -418,23 +465,23 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
         storeAfterSalesStatus.setOperator(user.getNickname());
         afterSalesStatusService.insertFsStoreAfterSalesStatus(storeAfterSalesStatus);
 
-//        //更新OMS
-        IErpOrderService erpOrderService = getErpService();
-        ErpRefundUpdateRequest request=new ErpRefundUpdateRequest();
-        request.setTid(order.getOrderCode());
-        request.setOid(order.getOrderCode());
-        request.setRefund_state(1);
-        request.setStoreAfterSalesId(storeAfterSales.getId());
-        if (StringUtils.isNotBlank(order.getExtendOrderId())){
-            BaseResponse response=erpOrderService.refundUpdateScrm(request);
-            if(response.getSuccess()){
-                return R.ok();
-            }
-            else{
-                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
-                return R.error(response.getErrorDesc());
-            }
-        }
+//        //更新OMS-延迟取消,到退款的时候取消
+//        IErpOrderService erpOrderService = getErpService();
+//        ErpRefundUpdateRequest request=new ErpRefundUpdateRequest();
+//        request.setTid(order.getOrderCode());
+//        request.setOid(order.getOrderCode());
+//        request.setRefund_state(1);
+//        request.setStoreAfterSalesId(storeAfterSales.getId());
+//        if (StringUtils.isNotBlank(order.getExtendOrderId())){
+//            BaseResponse response=erpOrderService.refundUpdateScrm(request);
+//            if(response.getSuccess()){
+//                return R.ok();
+//            }
+//            else{
+//                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+//                return R.error(response.getErrorDesc());
+//            }
+//        }
         return R.ok();
     }
 
@@ -974,16 +1021,16 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
             //创建新的OMS订单
             if (storeAfterSales.getOrderStatus().equals(OrderInfoEnum.STATUS_1.getValue()) ) {
                 if(StringUtils.isNotEmpty(order.getExtendOrderId())){
-                    //更新订单code
-                    String orderSn = IdUtil.getSnowflake(0, 0).nextIdStr();
-                    FsStoreOrderScrm orderMap=new FsStoreOrderScrm();
-                    orderMap.setId(order.getId());
-                    orderMap.setOrderCode(orderSn);
-                    orderService.updateFsStoreOrder(orderMap);
-                    storeAfterSales.setOrderCode(orderSn);
-                    orderItemService.updateFsStoreOrderCode(order.getId(),orderSn);
-                    //生成新的订单
-                    orderService.createOmsOrder(order.getId());
+//                    //更新订单code
+//                    String orderSn = IdUtil.getSnowflake(0, 0).nextIdStr();
+//                    FsStoreOrderScrm orderMap=new FsStoreOrderScrm();
+//                    orderMap.setId(order.getId());
+//                    orderMap.setOrderCode(orderSn);
+//                    orderService.updateFsStoreOrder(orderMap);
+//                    storeAfterSales.setOrderCode(orderSn);
+//                    orderItemService.updateFsStoreOrderCode(order.getId(),orderSn);
+//                    //生成新的订单
+//                    orderService.createOmsOrder(order.getId());
                 }
             }
         }
@@ -1011,8 +1058,10 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public R audit1(FsStoreAfterSalesAudit1Param param) {
         FsStoreAfterSalesScrm storeAfterSales = fsStoreAfterSalesMapper.selectFsStoreAfterSalesById(param.getSalesId());
+        FsStoreOrderScrm order=orderService.selectFsStoreOrderByOrderCode(storeAfterSales.getOrderCode());
         if (storeAfterSales == null) {
             throw new CustomException("未查询到售后订单信息");
         }
@@ -1042,10 +1091,9 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
             afterSalesStatusService.insertFsStoreAfterSalesStatus(storeAfterSalesStatus);
         }
         //退款退货
-        else if(storeAfterSales.getServiceType().equals(1)){
+        else if(storeAfterSales.getServiceType().equals(1) || storeAfterSales.getServiceType().equals(3)){
             String json=configService.selectConfigByKey("store.config");
             StoreConfig config=JSONUtil.toBean(json,StoreConfig.class);
-            FsStoreOrderScrm order=orderService.selectFsStoreOrderByOrderCode(storeAfterSales.getOrderCode());
             if(order.getStoreHouseCode()!=null){
                 if(order.getStoreHouseCode().equals("CK01")){
                     storeAfterSales.setConsignee(config.getRefundConsignee());
@@ -1065,7 +1113,7 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
                 storeAfterSales.setAddress(config.getRefundAddress());
             }
             //退款退货
-            storeAfterSales.setStatus(1);
+            storeAfterSales.setStatus(2);
             fsStoreAfterSalesMapper.updateFsStoreAfterSales(storeAfterSales);
             FsStoreAfterSalesStatusScrm storeAfterSalesStatus = new FsStoreAfterSalesStatusScrm();
             storeAfterSalesStatus.setStoreAfterSalesId(storeAfterSales.getId());
@@ -1076,9 +1124,129 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
             afterSalesStatusService.insertFsStoreAfterSalesStatus(storeAfterSalesStatus);
         }
 
+        //更新OMS-取消订单放到退款接口来
+        IErpOrderService erpOrderService = getErpService();
+        String jstOrderStatus = queryJstOrderStatus(order.getOrderCode());
+        boolean alreadySent = jstOrderStatus != null
+                ? "Sent".equalsIgnoreCase(jstOrderStatus)
+                : order.getStatus().equals(OrderInfoEnum.STATUS_2.getValue());
+        if(alreadySent){
+            //订单已发货:同步调用聚水潭 /open/aftersale/upload 上传售后单
+            //   • 有寄回物流单号 = 退货退款: type=“普通退货”, shop_status=WAIT_SELLER_CONFIRM_GOODS,携带物流字段
+            //   • 无寄回物流单号 = 仅退款: type=“仅退款”, shop_status=WAIT_SELLER_AGREE
+            //   items[].type 仅接受 退货/换货/其它/补发,此处统一传“退货”
+            try {
+                boolean isReturnRefund = StringUtils.isNotBlank(storeAfterSales.getDeliverySn());
+                RefundOrderDTO refundOrderDTO = new RefundOrderDTO();
+                if (isReturnRefund) {
+                    refundOrderDTO.setShopStatus(AfterSalesOrderStatusEnum.WAIT_SELLER_CONFIRM_GOODS.getCode());
+                    refundOrderDTO.setType("普通退货");
+                    refundOrderDTO.setLogisticsCompany(storeAfterSales.getDeliveryName());
+                    refundOrderDTO.setLId(storeAfterSales.getDeliverySn());
+                } else {
+                    refundOrderDTO.setShopStatus(AfterSalesOrderStatusEnum.WAIT_SELLER_AGREE.getCode());
+                    refundOrderDTO.setType("仅退款");
+                }
+                refundOrderDTO.setQuestionType("可更新");
+                refundOrderDTO.setOuterAsId(String.valueOf(storeAfterSales.getId()));
+                refundOrderDTO.setRemark("用户退款");
+                refundOrderDTO.setSoId(storeAfterSales.getOrderCode());
+                refundOrderDTO.setTotalAmount(order.getTotalPrice());
+                refundOrderDTO.setRefund(order.getPayMoney());
+                refundOrderDTO.setPayment(BigDecimal.ZERO);
+                if (StringUtils.isNotBlank(jstShopCode) && !"0".equals(jstShopCode)) {
+                    try {
+                        refundOrderDTO.setShopId(Long.valueOf(jstShopCode));
+                    } catch (NumberFormatException ignore) {
+                        // shop_code 配置非数字时不传 shop_id,由聚水潭内部匹配
+                    }
+                }
+                // 查订单商品明细构造 items 节点
+                FsStoreOrderItemScrm itemQuery = new FsStoreOrderItemScrm();
+                itemQuery.setOrderId(order.getId());
+                List<FsStoreOrderItemScrm> orderItems = orderItemService.selectFsStoreOrderItemList(itemQuery);
+                List<RefundItemDTO> refundItems = new ArrayList<>();
+                if (orderItems != null) {
+                    for (FsStoreOrderItemScrm oi : orderItems) {
+                        FsStoreCartDTO cartDTO = JSONUtil.toBean(oi.getJsonInfo(), FsStoreCartDTO.class);
+                        RefundItemDTO ri = new RefundItemDTO();
+                        ri.setSkuId(cartDTO.getBarCode());
+                        ri.setQty(cartDTO.getNum());
+                        ri.setAmount(cartDTO.getPrice());
+                        // 文档映射:items[].type 只能是 退货/换货/其它/补发
+                        ri.setType("退货");
+                        refundItems.add(ri);
+                    }
+                }
+                refundOrderDTO.setItems(refundItems);
+                Object uploadResp = jstErpHttpService.aftersaleUpload(refundOrderDTO);
+                logger.info("[聚水潭售后上传] outerAsId={}, soId={}, response={}",
+                        storeAfterSales.getId(), storeAfterSales.getOrderCode(),
+                        JSON.toJSONString(uploadResp));
+            } catch (Exception ex) {
+                // ERP 调用异常不阻断本地审核流程,失败打印错误日志供排查
+                logger.error("[聚水潭售后上传] 异常 outerAsId={}, soId={}, 错误:{}",
+                        storeAfterSales.getId(), storeAfterSales.getOrderCode(), ex.getMessage(), ex);
+            }
+        }else {
+            ErpRefundUpdateRequest request=new ErpRefundUpdateRequest();
+            request.setTid(storeAfterSales.getOrderCode());
+            request.setOid(storeAfterSales.getOrderCode());
+            request.setRefund_state(1);
+            request.setStoreAfterSalesId(storeAfterSales.getId());
+            if (StringUtils.isNotBlank(order.getExtendOrderId())){
+                BaseResponse response=erpOrderService.refundUpdateScrm(request);
+                if(response.getSuccess()){
+                    return R.ok();
+                }
+                else{
+                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+                    return R.error(response.getErrorDesc());
+                }
+            }
+        }
         return R.ok("操作成功");
     }
 
+    /**
+     * 调用聚水潭 /open/orders/single/query 查询订单状态
+     * <p>
+     * 返回值为聚水潭订单状态枚举值字符串(Sent/Delivering/WaitConfirm/Cancelled 等),
+     * 查不到或调用异常时返回 null,由调用方自行降级。
+     *
+     * @param orderCode 线上订单号,即本地 FsStoreOrderScrm.orderCode,对应聚水潭 so_id
+     * @return 聚水潭订单状态值;若未查到或异常返回 null
+     */
+    private String queryJstOrderStatus(String orderCode) {
+        if (StringUtils.isBlank(orderCode)) {
+            return null;
+        }
+        try {
+            OrderQueryRequestDTO requestDTO = OrderQueryRequestDTO.builder()
+                    .soIds(java.util.Collections.singletonList(orderCode))
+                    .pageIndex(1)
+                    .pageSize(1)
+                    .isGetTotal(false)
+                    .build();
+            // 配置了店铺编号时一并传入,提升查询命中率
+            if (StringUtils.isNotBlank(jstShopCode) && !"0".equals(jstShopCode)) {
+                try {
+                    requestDTO.setShopId(Integer.valueOf(jstShopCode));
+                } catch (NumberFormatException ignore) {
+                    // shop_code 配置非数字时忽略该参数
+                }
+            }
+            OrderQueryResponseDTO responseDTO = jstErpHttpService.query(requestDTO);
+            if (responseDTO == null || responseDTO.getOrders() == null || responseDTO.getOrders().isEmpty()) {
+                return null;
+            }
+            return responseDTO.getOrders().get(0).getStatus();
+        } catch (Exception e) {
+            logger.error("查询聚水潭订单状态异常,orderCode={}, 错误:{}", orderCode, e.getMessage(), e);
+            return null;
+        }
+    }
+
     @Override
     public R audit2(FsStoreAfterSalesAudit2Param param) {
         FsStoreAfterSalesScrm storeAfterSales = fsStoreAfterSalesMapper.selectFsStoreAfterSalesById(param.getSalesId());
@@ -1686,4 +1854,9 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
 
         return i;
     }
+
+    @Override
+    public Integer getAfterSalesType(String orderCode) {
+        return fsStoreAfterSalesMapper.getAfterSalesType(orderCode);
+    }
 }

+ 313 - 1
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -56,6 +56,7 @@ import com.fs.erp.domain.*;
 import com.fs.erp.dto.*;
 import com.fs.erp.dto.df.*;
 import com.fs.erp.dto.df.*;
+import com.fs.erp.http.JstErpHttpService;
 import com.fs.erp.mapper.FsErpFinishPushMapper;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.his.config.FsSysConfig;
@@ -407,6 +408,9 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     @Autowired
     private IFsStorePreparedPriceChangeService fsStorePreparedPriceChangeService;
 
+    @Autowired
+    private JstErpHttpService jstErpHttpService;
+
 
     /**
      * 查询订单
@@ -1501,6 +1505,314 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         }
     }
 
+    /**
+     * 同步聚水潭溯源码和批次号到订单明细
+     * 通过 /open/jushuitan/skusn/query 接口查询唯一码和生产批次号
+     */
+    private void syncSkuSnFromJst(FsStoreOrderScrm order) {
+        try {
+            // 只有推送过聚水潭的订单才需要同步
+            if (StringUtils.isEmpty(order.getExtendOrderId())) {
+                return;
+            }
+            // 检查ERP类型是否为聚水潭(type=5)
+            FsSysConfig erpConfig = configUtil.getSysConfig();
+            if (erpConfig.getErpOpen() == null || erpConfig.getErpOpen() != 1
+                    || erpConfig.getErpType() == null || erpConfig.getErpType() != 5) {
+                return;
+            }
+
+            // 获取订单明细
+            List<FsStoreOrderItemScrm> items = fsStoreOrderItemScrmMapper.selectFsStoreOrderItemByOrderCode(order.getOrderCode());
+            if (items == null || items.isEmpty()) {
+                return;
+            }
+
+            // 提取商品编码(barCode)列表
+            List<String> skuIds = new ArrayList<>();
+            for (FsStoreOrderItemScrm item : items) {
+                if (StringUtils.isNotEmpty(item.getJsonInfo())) {
+                    try {
+                        JSONObject jsonObj = JSON.parseObject(item.getJsonInfo());
+                        String barCode = jsonObj.getString("barCode");
+                        if (StringUtils.isNotEmpty(barCode) && !skuIds.contains(barCode)) {
+                            skuIds.add(barCode);
+                        }
+                    } catch (Exception e) {
+                        log.warn("解析订单明细jsonInfo失败, itemId: {}", item.getItemId());
+                    }
+                }
+            }
+            if (skuIds.isEmpty()) {
+                return;
+            }
+
+            // 调用聚水潭 skusn 查询接口
+            SkuSnQueryRequestDTO requestDTO = SkuSnQueryRequestDTO.builder()
+                    .skuIds(skuIds)
+                    .status(Collections.singletonList("Out"))
+                    .pageIndex(1)
+                    .pageSize(50)
+                    .build();
+
+            SkuSnQueryResponseDTO response = jstErpHttpService.querySkuSn(requestDTO);
+            if (response == null || response.getData() == null || response.getData().isEmpty()) {
+                log.info("聚水潭溯源码查询无数据,订单号: {}", order.getOrderCode());
+                return;
+            }
+
+            // 过滤当前订单的数据(根据内部单号o_id匹配)
+            String extendOrderId = order.getExtendOrderId();
+            List<SkuSnQueryResponseDTO.SkuSnItem> matchedItems = new ArrayList<>();
+            for (SkuSnQueryResponseDTO.SkuSnItem snItem : response.getData()) {
+                if (extendOrderId.equals(snItem.getOId())) {
+                    matchedItems.add(snItem);
+                }
+            }
+
+            if (matchedItems.isEmpty()) {
+                log.info("聚水潭溯源码查询未匹配到当前订单, 订单号: {}, extendOrderId: {}", order.getOrderCode(), extendOrderId);
+                return;
+            }
+
+            // 按sku_id分组,拼接溯源码和批次号
+            Map<String, StringBuilder> skuSnMap = new HashMap<>();
+            Map<String, StringBuilder> batchMap = new HashMap<>();
+            for (SkuSnQueryResponseDTO.SkuSnItem snItem : matchedItems) {
+                String skuId = snItem.getSkuId();
+                if (StringUtils.isNotEmpty(skuId)) {
+                    // 溯源码(sku_sn)
+                    if (StringUtils.isNotEmpty(snItem.getSkuSn())) {
+                        skuSnMap.computeIfAbsent(skuId, k -> new StringBuilder());
+                        if (skuSnMap.get(skuId).length() > 0) {
+                            skuSnMap.get(skuId).append(",");
+                        }
+                        skuSnMap.get(skuId).append(snItem.getSkuSn());
+                    }
+                    // 生产批次号(lc_id)
+                    String batchNo = StringUtils.isNotEmpty(snItem.getLcId()) ? snItem.getLcId() : snItem.getBatchId();
+                    if (StringUtils.isNotEmpty(batchNo)) {
+                        batchMap.computeIfAbsent(skuId, k -> new StringBuilder());
+                        if (batchMap.get(skuId).length() > 0 && !batchMap.get(skuId).toString().contains(batchNo)) {
+                            batchMap.get(skuId).append(",");
+                            batchMap.get(skuId).append(batchNo);
+                        } else if (batchMap.get(skuId).length() == 0) {
+                            batchMap.get(skuId).append(batchNo);
+                        }
+                    }
+                }
+            }
+
+            // 构建批量更新列表
+            List<FsStoreOrderItemScrm> updateList = new ArrayList<>();
+            for (FsStoreOrderItemScrm item : items) {
+                if (StringUtils.isNotEmpty(item.getJsonInfo())) {
+                    try {
+                        JSONObject jsonObj = JSON.parseObject(item.getJsonInfo());
+                        String barCode = jsonObj.getString("barCode");
+                        if (StringUtils.isNotEmpty(barCode)) {
+                            String verifyCode = skuSnMap.containsKey(barCode) ? skuSnMap.get(barCode).toString() : null;
+                            String batchNumber = batchMap.containsKey(barCode) ? batchMap.get(barCode).toString() : null;
+                            if (StringUtils.isNotEmpty(verifyCode) || StringUtils.isNotEmpty(batchNumber)) {
+                                FsStoreOrderItemScrm updateItem = new FsStoreOrderItemScrm();
+                                updateItem.setOrderCode(order.getOrderCode());
+                                updateItem.setProductId(item.getProductId());
+                                updateItem.setVerifyCode(verifyCode);
+                                updateItem.setBatchNumber(batchNumber);
+                                updateList.add(updateItem);
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.warn("构建溯源码更新数据失败, itemId: {}", item.getItemId());
+                    }
+                }
+            }
+
+            // 批量更新
+            if (!updateList.isEmpty()) {
+                fsStoreOrderItemScrmMapper.batchUpdateVerifyCodeByOrderCode(updateList);
+                log.info("聚水潭溯源码同步成功,订单号: {}, 更新{}条明细", order.getOrderCode(), updateList.size());
+            }
+        } catch (Exception e) {
+            log.error("同步聚水潭溯源码失败,订单号: {}", order.getOrderCode(), e);
+        }
+    }
+
+    /**
+     * 批量同步聚水潭溯源码和批次号(通过销售出库查询接口,按内部单号批量查询)
+     */
+    @Override
+    public void syncSkuSnFromJstBatch(List<FsStoreOrderScrm> orders) {
+        try {
+            FsSysConfig erpConfig = configUtil.getSysConfig();
+            if (erpConfig.getErpOpen() == null || erpConfig.getErpOpen() != 1
+                    || erpConfig.getErpType() == null || erpConfig.getErpType() != 5) {
+                return;
+            }
+
+            // 过滤有效订单(必须有extendOrderId)
+            List<FsStoreOrderScrm> validOrders = new ArrayList<>();
+            for (FsStoreOrderScrm order : orders) {
+                if (StringUtils.isNotEmpty(order.getExtendOrderId())) {
+                    validOrders.add(order);
+                }
+            }
+            if (validOrders.isEmpty()) {
+                return;
+            }
+
+            // 批量查询所有订单明细
+            List<String> orderCodes = new ArrayList<>();
+            for (FsStoreOrderScrm order : validOrders) {
+                orderCodes.add(order.getOrderCode());
+            }
+            List<FsStoreOrderItemScrm> allItems = fsStoreOrderItemScrmMapper.selectFsStoreOrderItemByOrderCodes(orderCodes);
+            Map<String, List<FsStoreOrderItemScrm>> orderItemsMap = new HashMap<>();
+            if (allItems != null) {
+                for (FsStoreOrderItemScrm item : allItems) {
+                    orderItemsMap.computeIfAbsent(item.getOrderCode(), k -> new ArrayList<>()).add(item);
+                }
+            }
+
+            // 收集所有extendOrderId,按最多50个一组分批查询销售出库单
+            List<String> allOIds = new ArrayList<>();
+            Map<String, FsStoreOrderScrm> extendOrderMap = new HashMap<>();
+            for (FsStoreOrderScrm order : validOrders) {
+                allOIds.add(order.getExtendOrderId());
+                extendOrderMap.put(order.getExtendOrderId(), order);
+            }
+
+            // 分批查询出库单(o_ids最多50条)
+            int batchSize = 50;
+            List<OutboundOrderQueryResponseDTO.OutboundOrder> allOutboundOrders = new ArrayList<>();
+            for (int i = 0; i < allOIds.size(); i += batchSize) {
+                List<String> batchOIds = allOIds.subList(i, Math.min(i + batchSize, allOIds.size()));
+
+                // 分页查询当前批次
+                int pageIndex = 1;
+                while (true) {
+                    OutboundOrderQueryRequestDTO requestDTO = OutboundOrderQueryRequestDTO.builder()
+                            .oIds(batchOIds)
+                            .status("Confirmed")
+                            .pageIndex(pageIndex)
+                            .pageSize(50)
+                            .build();
+                    OutboundOrderQueryResponseDTO response = jstErpHttpService.queryOutboundOrder(requestDTO);
+                    if (response == null || response.getData() == null
+                            || response.getData().getDatas() == null || response.getData().getDatas().isEmpty()) {
+                        break;
+                    }
+                    allOutboundOrders.addAll(response.getData().getDatas());
+                    // 不足一页就退出
+                    if (!Boolean.TRUE.equals(response.getData().getHasNext())) {
+                        break;
+                    }
+                    pageIndex++;
+                }
+            }
+
+            if (allOutboundOrders.isEmpty()) {
+                log.info("聚水潭销售出库批量查询无数据,订单数: {},查询的oIds: {}", validOrders.size(), allOIds);
+                return;
+            }
+
+            // 按o_id分组出库单
+            Map<String, List<OutboundOrderQueryResponseDTO.OutboundOrder>> outboundByOId = new HashMap<>();
+            for (OutboundOrderQueryResponseDTO.OutboundOrder outOrder : allOutboundOrders) {
+                if (outOrder.getOId() != null) {
+                    String oIdStr = String.valueOf(outOrder.getOId());
+                    outboundByOId.computeIfAbsent(oIdStr, k -> new ArrayList<>()).add(outOrder);
+                }
+            }
+
+            // 遍历每个订单,从出库单中提取溯源码和批次号
+            for (FsStoreOrderScrm order : validOrders) {
+                List<OutboundOrderQueryResponseDTO.OutboundOrder> matchedOutOrders = outboundByOId.get(order.getExtendOrderId());
+                if (matchedOutOrders == null || matchedOutOrders.isEmpty()) {
+                    log.info("订单 {} 未匹配到出库单,extendOrderId: {}", order.getOrderCode(), order.getExtendOrderId());
+                    continue;
+                }
+
+                List<FsStoreOrderItemScrm> items = orderItemsMap.get(order.getOrderCode());
+                if (items == null || items.isEmpty()) {
+                    continue;
+                }
+
+                // 从出库单中提取: sns[]→溯源码, batchs[]→批次号,按sku_id分组
+                Map<String, StringBuilder> skuSnMap = new HashMap<>();
+                Map<String, StringBuilder> batchMap = new HashMap<>();
+
+                for (OutboundOrderQueryResponseDTO.OutboundOrder outOrder : matchedOutOrders) {
+                    log.info("订单 {} 出库单oId: {}, sns数量: {}, batchs数量: {}",
+                            order.getOrderCode(), outOrder.getOId(),
+                            outOrder.getSns() != null ? outOrder.getSns().size() : 0,
+                            outOrder.getBatchs() != null ? outOrder.getBatchs().size() : 0);
+                    // 提取唯一码(sns)
+                    if (outOrder.getSns() != null) {
+                        for (OutboundOrderQueryResponseDTO.SnInfo snInfo : outOrder.getSns()) {
+                            String skuId = snInfo.getSkuId();
+                            if (StringUtils.isNotEmpty(skuId) && StringUtils.isNotEmpty(snInfo.getSn())) {
+                                skuSnMap.computeIfAbsent(skuId, k -> new StringBuilder());
+                                if (skuSnMap.get(skuId).length() > 0) {
+                                    skuSnMap.get(skuId).append(",");
+                                }
+                                skuSnMap.get(skuId).append(snInfo.getSn());
+                            }
+                        }
+                    }
+                    // 提取批次号(batchs)
+                    if (outOrder.getBatchs() != null) {
+                        for (OutboundOrderQueryResponseDTO.BatchInfo batchInfo : outOrder.getBatchs()) {
+                            String skuId = batchInfo.getSkuId();
+                            String batchNo = batchInfo.getBatchNo();
+                            if (StringUtils.isNotEmpty(skuId) && StringUtils.isNotEmpty(batchNo)) {
+                                batchMap.computeIfAbsent(skuId, k -> new StringBuilder());
+                                if (batchMap.get(skuId).length() > 0 && !batchMap.get(skuId).toString().contains(batchNo)) {
+                                    batchMap.get(skuId).append(",");
+                                    batchMap.get(skuId).append(batchNo);
+                                } else if (batchMap.get(skuId).length() == 0) {
+                                    batchMap.get(skuId).append(batchNo);
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // 构建更新列表:通过barCode匹配sku_id
+                List<FsStoreOrderItemScrm> updateList = new ArrayList<>();
+                for (FsStoreOrderItemScrm item : items) {
+                    if (StringUtils.isNotEmpty(item.getJsonInfo())) {
+                        try {
+                            JSONObject jsonObj = JSON.parseObject(item.getJsonInfo());
+                            String barCode = jsonObj.getString("barCode");
+                            if (StringUtils.isNotEmpty(barCode)) {
+                                String verifyCode = skuSnMap.containsKey(barCode) ? skuSnMap.get(barCode).toString() : null;
+                                String batchNumber = batchMap.containsKey(barCode) ? batchMap.get(barCode).toString() : null;
+                                if (StringUtils.isNotEmpty(verifyCode) || StringUtils.isNotEmpty(batchNumber)) {
+                                    FsStoreOrderItemScrm updateItem = new FsStoreOrderItemScrm();
+                                    updateItem.setOrderCode(order.getOrderCode());
+                                    updateItem.setProductId(item.getProductId());
+                                    updateItem.setVerifyCode(verifyCode);
+                                    updateItem.setBatchNumber(batchNumber);
+                                    updateList.add(updateItem);
+                                }
+                            }
+                        } catch (Exception e) {
+                            log.warn("构建溯源码更新数据失败, itemId: {}", item.getItemId());
+                        }
+                    }
+                }
+
+                if (!updateList.isEmpty()) {
+                    fsStoreOrderItemScrmMapper.batchUpdateVerifyCodeByOrderCode(updateList);
+                    log.info("聚水潭出库单批量同步成功,订单号: {}, 更新{}条明细", order.getOrderCode(), updateList.size());
+                }
+            }
+        } catch (Exception e) {
+            log.error("批量同步聚水潭溯源码失败", e);
+        }
+    }
+
     @Override
     public void updateDeliveryOrder(Long id, String deliveryId, String deliverCode, String deliverName) {
         FsStoreOrderScrm order = fsStoreOrderMapper.selectFsStoreOrderById(id);
@@ -1547,7 +1859,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                 //已完成订单
                 vo.setIsAfterSales(1);
                 if (vo.getFinishTime() != null) {
-                    String json = configService.selectConfigByKey("store.config");
+                    String json = configService.selectConfigByKey("his.store");
                     StoreConfig storeConfig = JSONUtil.toBean(json, StoreConfig.class);
                     if (storeConfig.getStoreAfterSalesDay() != null && storeConfig.getStoreAfterSalesDay() > 0) {
                         //判断完成时间是否超过指定时间

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsMyStoreOrderListQueryVO.java

@@ -69,4 +69,9 @@ public class FsMyStoreOrderListQueryVO implements Serializable
      * **/
     private String commonName;
 
+    /**
+     * 状态 0已提交等待平台审核 1平台已审核 等待用户发货 2 用户已发货待仓库审核 3财务审核 select o.order_code,p.*, from fs_store_order o  LEFT JOIN fs_store_order_status p ON p.order_id = o.id LEFT JOIN fs_store_after_sales s on s.order_code = o.order_code where o.create_time >= '2023-09-19 00:00:00' AND o.create_time <= '2023-09-23 00:00:00' and o.status = 2 and p.change_message = '确认收货'
+     * **/
+    private Integer afterSalesType;
+
 }

+ 4 - 4
fs-service/src/main/resources/application-config-druid-yjb.yml

@@ -10,10 +10,10 @@ logging:
     me.chanjar.weixin: DEBUG
 wx:
   open:
-#      appId: wx7796a33a71912e32 #互易享(三方入驻APP)
-#      secret: 51601ec1e3247fe1615ef7b55baf95c7 #互易享(三方入驻APP)
-      appId: wx98abee0aa8ccdd3c #鸿良(看课APP)
-      secret: 6c269d8ee939d1a241721d0dea834568 #鸿良(看课APP)
+      appId: wx7796a33a71912e32 #互易享(三方入驻APP)
+      secret: 51601ec1e3247fe1615ef7b55baf95c7 #互易享(三方入驻APP)
+#      appId: wx98abee0aa8ccdd3c #鸿良(看课APP)
+#      secret: 6c269d8ee939d1a241721d0dea834568 #鸿良(看课APP)
   miniapp:
     configs:
       - appid: wx9e61312fe7ac85c4   #医健宝

+ 12 - 12
fs-service/src/main/resources/application-dev-yjb.yml

@@ -31,16 +31,16 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                    url: jdbc:mysql://139.186.77.83:3306/yjb_his_scrm_test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: Rtroot
-                    password: Rtroot
+                    url: jdbc:mysql://nj-cdb-6306xy90.sql.tencentcdb.com:27077/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: yjb
+                    password: Ylrz_1q2w3e4r5t6y
                 # 从库数据源
                 slave:
                     # 从数据源开关/默认关闭
                     enabled: true
-                    url: jdbc:mysql://139.186.77.83:3306/yjb_his_scrm_test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: Rtroot
-                    password: Rtroot
+                    url: jdbc:mysql://nj-cdb-6306xy90.sql.tencentcdb.com:27077/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: yjb
+                    password: Ylrz_1q2w3e4r5t6y
                 # 初始连接数
                 initialSize: 5
                 # 最小连接池数量
@@ -86,15 +86,15 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                    url: jdbc:mysql://139.186.77.83:3306/yjb_fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: Rtroot
-                    password: Rtroot
+                    url: jdbc:mysql://nj-cdb-6306xy90.sql.tencentcdb.com:27077/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: yjb
+                    password: Ylrz_1q2w3e4r5t6y
                 read:
                     # 从数据源开关/默认关闭
                     enabled: true
-                    url: jdbc:mysql://139.186.77.83:3306/yjb_fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: Rtroot
-                    password: Rtroot
+                    url: jdbc:mysql://nj-cdb-6306xy90.sql.tencentcdb.com:27077/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: yjb
+                    password: Ylrz_1q2w3e4r5t6y
                 # 初始连接数
                 initialSize: 5
                 # 最小连接池数量

+ 1 - 0
fs-service/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml

@@ -81,6 +81,7 @@
             <if test="periodStartingTime != null "> and period_starting_time &gt;= #{periodStartingTime}</if>
             <if test="periodEndTime != null "> and period_end_time &lt;= #{periodEndTime}</if>
             <if test="periodLine != null "> and period_line = #{periodLine}</if>
+            <if test="periodStatus != null "> and fs_user_course_period.period_status = #{periodStatus}</if>
         </where>
         group by fs_user_course_period.period_id
         order by create_time desc

+ 4 - 0
fs-service/src/main/resources/mapper/hisStore/FsStoreAfterSalesItemScrmMapper.xml

@@ -73,5 +73,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{id}
         </foreach>
     </delete>
+
+    <delete id="deleteFsStoreAfterSalesItemByAfterSalesId" parameterType="Long">
+        delete from fs_store_after_sales_item_scrm where store_after_sales_id = #{id}
+    </delete>
     
 </mapper>

+ 3 - 0
fs-service/src/main/resources/mapper/hisStore/FsStoreAfterSalesScrmMapper.xml

@@ -156,4 +156,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
     </delete>
 
+    <select id="getAfterSalesType" resultType="Integer">
+        select service_type from fs_store_after_sales_scrm where order_code = #{orderCode}
+    </select>
 </mapper>

+ 6 - 0
fs-service/src/main/resources/mapper/hisStore/FsStoreOrderItemScrmMapper.xml

@@ -100,6 +100,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectFsStoreOrderItemByOrderCode" resultType="com.fs.hisStore.domain.FsStoreOrderItemScrm">
         select * from fs_store_order_item_scrm where order_code = #{orderCode}
     </select>
+    <select id="selectFsStoreOrderItemByOrderCodes" resultType="com.fs.hisStore.domain.FsStoreOrderItemScrm">
+        select * from fs_store_order_item_scrm where order_code in
+        <foreach collection="orderCodes" item="orderCode" open="(" separator="," close=")">
+            #{orderCode}
+        </foreach>
+    </select>
     <update id="batchUpdateVerifyCodeByOrderCode" parameterType="list">
         UPDATE fs_store_order_item_scrm
         SET verify_code =

+ 81 - 1
fs-store/src/main/java/com/fs/hisStore/controller/store/FsStoreAfterSalesScrmController.java

@@ -1,5 +1,6 @@
 package com.fs.hisStore.controller.store;
 
+import cn.hutool.core.util.StrUtil;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -8,13 +9,19 @@ import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.his.dto.ExpressInfoDTO;
+import com.fs.his.service.IFsExpressService;
 import com.fs.his.vo.FsStoreAfterSalesExcelVO;
 import com.fs.hisStore.domain.FsStoreAfterSalesItemScrm;
 import com.fs.hisStore.domain.FsStoreAfterSalesScrm;
 import com.fs.his.param.FsStoreAfterSalesParam;
 import com.fs.hisStore.domain.FsStoreOrderItemScrm;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
+import com.fs.hisStore.enums.ShipperCodeEnum;
 import com.fs.hisStore.mapper.FsStoreOrderItemScrmMapper;
+import com.fs.hisStore.param.FsStoreAfterSalesAudit1Param;
+import com.fs.hisStore.param.FsStoreAfterSalesAudit2Param;
+import com.fs.hisStore.param.FsStoreAfterSalesCancelParam;
 import com.fs.hisStore.service.IFsStoreAfterSalesItemScrmService;
 import com.fs.hisStore.service.IFsStoreAfterSalesScrmService;
 import com.fs.hisStore.service.IFsStoreOrderScrmService;
@@ -24,6 +31,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.web.bind.annotation.*;
 
+import java.text.ParseException;
+
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -51,6 +60,64 @@ public class FsStoreAfterSalesScrmController extends BaseController
     @Autowired
     private IFsStoreOrderScrmService fsStoreOrderService;
 
+    @Autowired
+    private IFsExpressService expressService;
+
+    /**
+     * 查询用户寄回快递轨迹(售后)
+     */
+    @GetMapping(value = "/getExpress/{id}")
+    public R getExpress(@PathVariable("id") Long id) {
+        FsStoreAfterSalesScrm afterSales = fsStoreAfterSalesService.selectFsStoreAfterSalesById(id);
+        ExpressInfoDTO expressInfoDTO = null;
+        if (afterSales != null
+                && StrUtil.isNotBlank(afterSales.getDeliverySn())
+                && StrUtil.isNotBlank(afterSales.getShipperCode())) {
+            String lastFourNumber = "";
+            // 顺丰需要寄件人手机后 4 位;寄回时商家手机号即寄件人对应的号码
+            if (ShipperCodeEnum.SF.getValue().equals(afterSales.getShipperCode())) {
+                String phone = afterSales.getPhoneNumber();
+                if (StrUtil.isNotBlank(phone) && phone.length() >= 4) {
+                    lastFourNumber = StrUtil.sub(phone, phone.length() - 4, phone.length());
+                }
+            }
+            expressInfoDTO = expressService.getExpressInfo(
+                    afterSales.getOrderCode(),
+                    afterSales.getShipperCode(),
+                    afterSales.getDeliverySn(),
+                    lastFourNumber);
+        }
+        return R.ok().put("data", expressInfoDTO);
+    }
+
+    /**
+     * 撤销(店铺端),与 admin 平台 /cancel 保持一致
+     */
+    @PostMapping("/cancel")
+    public R cancel(@RequestBody FsStoreAfterSalesCancelParam param) throws ParseException {
+        // 店铺端无 LoginUser.nickName,用店铺名作为操作人
+        try {
+            param.setOperator(UserUtil.getLoginUser().getFsStore().getStoreName());
+        } catch (Exception ignore) {
+            param.setOperator("店铺");
+        }
+        return fsStoreAfterSalesService.cancel(param);
+    }
+
+    /**
+     * 平台审核(店铺端),与 admin 平台 /audit1 保持一致
+     */
+    @PostMapping("/audit1")
+    public R audit1(@RequestBody FsStoreAfterSalesAudit1Param param) {
+        // 店铺端无 LoginUser.nickName,用店铺名作为操作人
+        try {
+            param.setOperator(UserUtil.getLoginUser().getFsStore().getStoreName());
+        } catch (Exception ignore) {
+            param.setOperator("店铺");
+        }
+        return fsStoreAfterSalesService.audit1(param);
+    }
+
     /**
      * 查询售后记录列表
      */
@@ -177,7 +244,20 @@ public class FsStoreAfterSalesScrmController extends BaseController
     @PutMapping("/depotAuditing")
     public AjaxResult depotAuditing(@RequestBody FsStoreAfterSalesScrm fsStoreAfterSales)
     {
-        return toAjax(fsStoreAfterSalesService.depotAuditing(fsStoreAfterSales));
+        // 仓库审核:对齐 admin 总后台 /audit2 逻辑
+        // 写入 fs_store_after_sales_status 表,避开 fs_store_after_sales_logs_scrm 主键冲突 BUG
+        FsStoreAfterSalesAudit2Param param = new FsStoreAfterSalesAudit2Param();
+        param.setSalesId(fsStoreAfterSales.getId());
+        try {
+            param.setOperator(UserUtil.getLoginUser().getFsStore().getStoreName());
+        } catch (Exception ignore) {
+            param.setOperator("店铺");
+        }
+        R r = fsStoreAfterSalesService.audit2(param);
+        if (r != null && r.get("code") != null && Integer.valueOf(r.get("code").toString()) == 200) {
+            return AjaxResult.success(r.get("msg") == null ? "操作成功" : r.get("msg").toString());
+        }
+        return AjaxResult.error(r == null || r.get("msg") == null ? "操作失败" : r.get("msg").toString());
     }
 
     /**

+ 149 - 0
fs-user-app/src/main/java/com/fs/app/controller/store/LogisticsCompanyScrmController.java

@@ -0,0 +1,149 @@
+package com.fs.app.controller.store;
+
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.StringUtils;
+import com.fs.erp.dto.LogisticsCompanyQueryRequestDTO;
+import com.fs.erp.dto.LogisticsCompanyQueryResponseDTO;
+import com.fs.erp.http.JstErpHttpService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 聚水潭物流(快递)公司查询Controller
+ *
+ * @author fs
+ */
+@Slf4j
+@Api(tags = "聚水潭快递公司")
+@RestController
+@RequestMapping("/store/app/logistics")
+public class LogisticsCompanyScrmController {
+
+    /** 缓存 key(全量快递公司列表) */
+    private static final String CACHE_KEY_ALL = "jst:logistics:all";
+    /** 缓存时长:30 分钟 */
+    private static final int CACHE_TTL_MINUTES = 30;
+    /** 聚水潭 pageSize 上限 */
+    private static final int JST_PAGE_SIZE = 50;
+
+    @Autowired
+    private JstErpHttpService jstErpHttpService;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    /**
+     * 下拉查询聚水潭快递公司(支持模糊匹配)
+     *
+     * @param keyword 关键字(模糊匹配快递公司编码 lcId 或名称 lcName,可空)
+     * @return 快递公司列表,只返回 lcId 和 lcName
+     */
+    @ApiOperation("下拉查询聚水潭快递公司(支持模糊匹配)")
+    @GetMapping("/query")
+    public R query(@RequestParam(value = "keyword", required = false) String keyword) {
+        List<LogisticsCompanyQueryResponseDTO.LogisticsCompany> all = getAllLogisticsFromCacheOrRemote();
+        if (all == null || all.isEmpty()) {
+            return R.ok().put("data", Collections.emptyList());
+        }
+
+        List<Map<String, String>> result = new ArrayList<>(all.size());
+        String kw = keyword == null ? null : keyword.trim().toLowerCase();
+        for (LogisticsCompanyQueryResponseDTO.LogisticsCompany lc : all) {
+            if (lc == null) {
+                continue;
+            }
+            String lcId = lc.getLcId();
+            String lcName = lc.getLcName();
+            if (StringUtils.isEmpty(lcId) && StringUtils.isEmpty(lcName)) {
+                continue;
+            }
+            // 模糊匹配:编码或名称包含 keyword(忽略大小写)
+            if (StringUtils.isNotEmpty(kw)) {
+                boolean hit = (lcId != null && lcId.toLowerCase().contains(kw))
+                        || (lcName != null && lcName.toLowerCase().contains(kw));
+                if (!hit) {
+                    continue;
+                }
+            }
+            Map<String, String> item = new HashMap<>(4);
+            item.put("lcId", lcId);
+            item.put("lcName", lcName);
+            result.add(item);
+        }
+        return R.ok().put("data", result);
+    }
+
+    /**
+     * 刷新缓存(强制从聚水潭重新拉取)
+     */
+    @ApiOperation("刷新快递公司缓存")
+    @GetMapping("/refresh")
+    public R refresh() {
+        redisCache.deleteObject(CACHE_KEY_ALL);
+        List<LogisticsCompanyQueryResponseDTO.LogisticsCompany> all = fetchAllFromJst();
+        if (all != null && !all.isEmpty()) {
+            redisCache.setCacheObject(CACHE_KEY_ALL, all, CACHE_TTL_MINUTES, TimeUnit.MINUTES);
+        }
+        return R.ok().put("total", all == null ? 0 : all.size());
+    }
+
+    /**
+     * 优先走 Redis 缓存,缓存无则从聚水潭拉取并回写
+     */
+    private List<LogisticsCompanyQueryResponseDTO.LogisticsCompany> getAllLogisticsFromCacheOrRemote() {
+        List<LogisticsCompanyQueryResponseDTO.LogisticsCompany> cached = redisCache.getCacheObject(CACHE_KEY_ALL);
+        if (cached != null && !cached.isEmpty()) {
+            return cached;
+        }
+        List<LogisticsCompanyQueryResponseDTO.LogisticsCompany> all = fetchAllFromJst();
+        if (all != null && !all.isEmpty()) {
+            redisCache.setCacheObject(CACHE_KEY_ALL, all, CACHE_TTL_MINUTES, TimeUnit.MINUTES);
+        }
+        return all;
+    }
+
+    /**
+     * 分页从聚水潭拉取全部启用的快递公司
+     */
+    private List<LogisticsCompanyQueryResponseDTO.LogisticsCompany> fetchAllFromJst() {
+        List<LogisticsCompanyQueryResponseDTO.LogisticsCompany> all = new ArrayList<>();
+        int pageIndex = 1;
+        while (true) {
+            try {
+                LogisticsCompanyQueryRequestDTO req = LogisticsCompanyQueryRequestDTO.builder()
+                        .pageIndex(pageIndex)
+                        .pageSize(JST_PAGE_SIZE)
+                        .build();
+                LogisticsCompanyQueryResponseDTO resp = jstErpHttpService.queryLogisticsCompany(req);
+                if (resp == null || resp.getData() == null || resp.getData().getDatas() == null
+                        || resp.getData().getDatas().isEmpty()) {
+                    break;
+                }
+                all.addAll(resp.getData().getDatas());
+                if (!Boolean.TRUE.equals(resp.getData().getHasNext())) {
+                    break;
+                }
+                pageIndex++;
+            } catch (Exception e) {
+                log.error("拉取聚水潭快递公司失败, pageIndex: {}", pageIndex, e);
+                break;
+            }
+        }
+        log.info("聚水潭快递公司拉取完成, 共 {} 条", all.size());
+        return all;
+    }
+}

+ 24 - 6
fs-user-app/src/main/java/com/fs/app/controller/store/StoreAfterSalesScrmController.java

@@ -6,12 +6,8 @@ import com.fs.app.controller.AppBaseController;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.ParseUtils;
-import com.fs.his.domain.FsStoreAfterSales;
-import com.fs.his.domain.FsStoreAfterSalesItem;
-import com.fs.his.domain.FsStoreOrder;
-import com.fs.his.param.FsStoreAfterSalesListUParam;
-import com.fs.his.vo.FsStoreAfterSalesListUVO;
-import com.fs.his.vo.FsStoreOrderItemListUVO;
+import com.fs.common.utils.StringUtils;
+import com.fs.hisStore.config.StoreConfig;
 import com.fs.hisStore.domain.FsStoreAfterSalesItemScrm;
 import com.fs.hisStore.domain.FsStoreAfterSalesScrm;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
@@ -25,6 +21,8 @@ import com.fs.hisStore.service.IFsStoreOrderItemScrmService;
 import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.hisStore.vo.FsStoreAfterSalesQueryVO;
 import com.fs.hisStore.vo.FsStoreOrderItemVO;
+import com.fs.system.service.ISysConfigService;
+import cn.hutool.json.JSONUtil;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -51,6 +49,8 @@ public class StoreAfterSalesScrmController extends AppBaseController {
     private IFsStoreOrderItemScrmService itemService;
     @Autowired
     private IFsStoreOrderScrmService orderService;
+    @Autowired
+    private ISysConfigService configService;
     @Login
     @ApiOperation("获取订单项列表")
     @GetMapping({"/getStoreOrderItems", "/getMyStoreOrderItemByOrderId"})
@@ -85,6 +85,24 @@ public class StoreAfterSalesScrmController extends AppBaseController {
         return storeAfterSalesService.addDelivery(param);
     }
 
+    @Login
+    @GetMapping("/getRefundAddress")
+    @ApiOperation(value = "获取商家退货收货信息", notes = "从 his.store 配置中读取退货收货人/手机号/地址")
+    public R getRefundAddress() {
+        String json = configService.selectConfigByKey("his.store");
+        if (StringUtils.isEmpty(json)) {
+            return R.ok()
+                    .put("refundConsignee", "")
+                    .put("refundPhoneNumber", "")
+                    .put("refundAddress", "");
+        }
+        StoreConfig config = JSONUtil.toBean(json, StoreConfig.class);
+        return R.ok()
+                .put("refundConsignee", config.getRefundConsignee())
+                .put("refundPhoneNumber", config.getRefundPhoneNumber())
+                .put("refundAddress", config.getRefundAddress());
+    }
+
 
 
     @Login

+ 18 - 12
fs-user-app/src/main/java/com/fs/app/controller/store/StoreOrderScrmController.java

@@ -232,7 +232,7 @@ public class StoreOrderScrmController extends AppBaseController {
         List<FsStoreOrderItemVO> list=itemService.selectFsStoreOrderItemListByOrderId(orderId);
         Calendar calendar = Calendar.getInstance();
         calendar.setTime(order.getCreateTime());
-        String json=configService.selectConfigByKey("store.config");
+        String json=configService.selectConfigByKey("his.store");
         StoreConfig config=JSONUtil.toBean(json,StoreConfig.class);
         if(order.getIsPrescribe() != null && order.getIsPrescribe() == 1){
             config.setUnPayTime(4320);
@@ -253,17 +253,19 @@ public class StoreOrderScrmController extends AppBaseController {
         }
         //处理是否可以申请售后
         Integer isAfterSales=0;
-        if(order.getStatus().equals(OrderInfoEnum.STATUS_3.getValue())) {
+        if(order.getStatus() >= 2) {//优化类型判断,发货中也出现退货退款
             //已完成订单
             isAfterSales=1;
-            if (order.getFinishTime() != null) {
-                if (config.getStoreAfterSalesDay() != null && config.getStoreAfterSalesDay() > 0) {
-                    //判断完成时间是否超过指定时间
-                    Calendar calendarAfterSales = new GregorianCalendar();
-                    calendarAfterSales.setTime(order.getFinishTime());
-                    calendarAfterSales.add(calendarAfterSales.DATE, config.getStoreAfterSalesDay()); //把日期往后增加一天,整数  往后推,负数往前移动
-                    if (calendarAfterSales.getTime().getTime() < new Date().getTime()) {
-                        isAfterSales = 0;
+            if(order.getStatus().equals(OrderInfoEnum.STATUS_3.getValue())){
+                if (order.getFinishTime() != null) {
+                    if (config.getStoreAfterSalesDay() != null && config.getStoreAfterSalesDay() > 0) {
+                        //判断完成时间是否超过指定时间
+                        Calendar calendarAfterSales = new GregorianCalendar();
+                        calendarAfterSales.setTime(order.getFinishTime());
+                        calendarAfterSales.add(calendarAfterSales.DATE, config.getStoreAfterSalesDay()); //把日期往后增加一天,整数  往后推,负数往前移动
+                        if (calendarAfterSales.getTime().getTime() < new Date().getTime()) {
+                            isAfterSales = 0;
+                        }
                     }
                 }
             }
@@ -293,7 +295,7 @@ public class StoreOrderScrmController extends AppBaseController {
 //            order.setVerifyCodes(verifyCodes.stream().map(FsStoreVerifyCodeScrm::getVerifyCode).collect(Collectors.joining()));
 //        }
         return R.ok().put("isAfterSales",isAfterSales).put("order",order).put("items",list).put("payLimitTime",payLimitTime).put("prescribe",prescribe).put("prescriptionAuditStatus",prescriptionAuditStatus)
-                .put("storeName",storeName);
+                .put("storeName",storeName).put("afterSalesType",afterSalesService.getAfterSalesType(order.getOrderCode()));
     }
 
     @Login
@@ -574,6 +576,8 @@ public class StoreOrderScrmController extends AppBaseController {
                         FsStorePaymentScrm mt = new FsStorePaymentScrm();
                         mt.setPaymentId(storePayment.getPaymentId());
                         mt.setTradeNo(uniqueOrderNo);
+                        mt.setAppId(param.getAppId());
+                        mt.setOpenId(user.getMaOpenId());
                         fsStorePaymentMapper.updateFsStorePayment(mt);
 
                         String fundProcessType = yopResult.getFundProcessType();
@@ -800,12 +804,14 @@ public class StoreOrderScrmController extends AppBaseController {
                     yopDto.setCombinationOrderId(fsStoreOrder.getCombinationOrderId());
                     YopPayResponseDTO yopResult = yopPayService.pay(yopDto);
                     if (yopResult != null && yopResult.isSuccess()) {
-                        // 下单成功,更新所有子订单payment的易宝交易号
+                        // 下单成功,更新所有子订单payment的易宝交易号、appId及对应openId
                         String uniqueOrderNo = yopResult.getUniqueOrderNo();
                         for (Long paymentId : paymentIds) {
                             FsStorePaymentScrm mt = new FsStorePaymentScrm();
                             mt.setPaymentId(paymentId);
                             mt.setTradeNo(uniqueOrderNo);
+                            mt.setAppId(param.getAppId());
+                            mt.setOpenId(fsUser.getMaOpenId());
                             fsStorePaymentMapper.updateFsStorePayment(mt);
                         }