Browse Source

Merge remote-tracking branch 'origin/master'

xgb 2 days ago
parent
commit
70257c0055
34 changed files with 898 additions and 62 deletions
  1. 7 5
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java
  2. 3 3
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  3. 14 1
      fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java
  4. 1 1
      fs-company/src/main/java/com/fs/company/controller/live/LiveDataController.java
  5. 1 1
      fs-live-app/src/main/java/com/fs/live/controller/LiveController.java
  6. 1 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseAnswerLogsMapper.java
  7. 1 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java
  8. 2 0
      fs-service/src/main/java/com/fs/course/param/FsCourseAnswerLogsParam.java
  9. 1 0
      fs-service/src/main/java/com/fs/course/param/FsCourseRedPacketLogParam.java
  10. 2 0
      fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java
  11. 0 19
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  12. 1 0
      fs-service/src/main/java/com/fs/his/service/impl/MerchantAppConfigServiceImpl.java
  13. 10 0
      fs-service/src/main/java/com/fs/hisStore/mapper/MergedOrderMapper.java
  14. 31 0
      fs-service/src/main/java/com/fs/hisStore/param/MergedAfterSalesDeliveryParam.java
  15. 43 0
      fs-service/src/main/java/com/fs/hisStore/param/MergedAfterSalesParam.java
  16. 23 0
      fs-service/src/main/java/com/fs/hisStore/param/MergedAfterSalesQueryParam.java
  17. 22 0
      fs-service/src/main/java/com/fs/hisStore/param/MergedAfterSalesRevokeParam.java
  18. 25 0
      fs-service/src/main/java/com/fs/hisStore/param/MergedOrderDeleteParam.java
  19. 56 1
      fs-service/src/main/java/com/fs/hisStore/service/IMergedOrderService.java
  20. 42 4
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  21. 230 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/MergedOrderServiceImpl.java
  22. 103 0
      fs-service/src/main/java/com/fs/hisStore/vo/MergedAfterSalesVO.java
  23. 1 1
      fs-service/src/main/java/com/fs/live/mapper/LiveVideoMapper.java
  24. 1 1
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  25. 7 1
      fs-service/src/main/java/com/fs/qw/param/QwExternalContactParam.java
  26. 3 3
      fs-service/src/main/resources/application-config-druid-bjzm-test.yml
  27. 3 3
      fs-service/src/main/resources/application-config-druid-bjzm.yml
  28. 3 0
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  29. 81 0
      fs-service/src/main/resources/mapper/hisStore/MergedOrderMapper.xml
  30. 10 7
      fs-service/src/main/resources/mapper/live/LiveOrderMapper.xml
  31. 6 0
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  32. 97 4
      fs-user-app/src/main/java/com/fs/app/controller/live/LiveController.java
  33. 57 4
      fs-user-app/src/main/java/com/fs/app/controller/live/OrderController.java
  34. 10 2
      fs-user-app/src/main/java/com/fs/app/controller/store/StoreOrderScrmController.java

+ 7 - 5
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java

@@ -117,8 +117,10 @@ public class FsStoreHealthOrderScrmController extends BaseController {
                     }
                 }
                 //
-                if ((loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*") && (vo.getCost() !=null && vo.getTotalNum() != null))) {
-                    vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
+                if (loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*") ) {
+                    if((vo.getCost() !=null && vo.getTotalNum() != null)){
+                        vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
+                    }
                 } else {
                     vo.setPayPostage(BigDecimal.ZERO);
                     vo.setCost(BigDecimal.ZERO);
@@ -306,7 +308,7 @@ public class FsStoreHealthOrderScrmController extends BaseController {
                     }
                 }
                 //
-                if (loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                if ((loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*") ) && !Objects.isNull(vo.getCost())) {
                     vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
                 } else {
                     vo.setPayPostage(BigDecimal.ZERO);
@@ -371,7 +373,7 @@ public class FsStoreHealthOrderScrmController extends BaseController {
                             } catch (Exception e) {
                             }
                         }
-                        if (loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                        if ((loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*") ) && !Objects.isNull(vo.getCost())) {
                             vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
                         } else {
                             vo.setPayPostage(BigDecimal.ZERO);
@@ -397,7 +399,7 @@ public class FsStoreHealthOrderScrmController extends BaseController {
                     } catch (Exception e) {
                     }
                 }
-                if (loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                if ((loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*") ) && !Objects.isNull(vo.getCost())) {
                     vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
                 } else {
                     vo.setPayPostage(BigDecimal.ZERO);

+ 3 - 3
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java

@@ -498,7 +498,7 @@ public class FsStoreOrderScrmController extends BaseController {
                     }
                 }
                 //
-                if (loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                if ((loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*") ) && !Objects.isNull(vo.getCost())) {
                     vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
                 } else {
                     vo.setPayPostage(BigDecimal.ZERO);
@@ -567,7 +567,7 @@ public class FsStoreOrderScrmController extends BaseController {
                         }
                     }
                     //
-                    if (loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                    if ((loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*") ) && !Objects.isNull(vo.getCost())) {
                         vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
                     } else {
                         vo.setPayPostage(BigDecimal.ZERO);
@@ -595,7 +595,7 @@ public class FsStoreOrderScrmController extends BaseController {
                     }
                 }
                 //
-                if (loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                if ((loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*") ) && !Objects.isNull(vo.getCost())) {
                     vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
                 } else {
                     vo.setPayPostage(BigDecimal.ZERO);

+ 14 - 1
fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java

@@ -5,14 +5,17 @@ import java.util.Objects;
 
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.ObjectUtil;
+import com.fs.common.core.domain.R;
 import com.fs.common.exception.ServiceException;
 import com.fs.qw.param.QwExternalContactParam;
 import com.fs.qw.param.QwTagSearchParam;
+import com.fs.qw.service.IQwExternalContactInfoService;
 import com.fs.qw.service.IQwTagService;
 import com.fs.qw.vo.QwExternalContactUnionIdExportVO;
 import com.fs.qw.vo.QwExternalContactVO;
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -33,7 +36,7 @@ import com.fs.common.core.page.TableDataInfo;
 
 /**
  * 企业微信客户Controller
- * 
+ *
  * @author fs
  * @date 2025-06-13
  */
@@ -44,6 +47,9 @@ public class QwExternalContactController extends BaseController
     private IQwExternalContactService qwExternalContactService;
     private IQwTagService iQwTagService;
 
+    @Autowired
+    private IQwExternalContactInfoService qwExternalContactInfoService;
+
     QwExternalContactController(IQwExternalContactService qwExternalContactService,IQwTagService iQwTagService){
         this.qwExternalContactService=qwExternalContactService;
         this.iQwTagService=iQwTagService;
@@ -168,4 +174,11 @@ public class QwExternalContactController extends BaseController
     {
         return toAjax(qwExternalContactService.deleteQwExternalContactByIds(ids));
     }
+
+    @GetMapping(value = "getUserInfo/{id}")
+    public R getUserInfo(@PathVariable("id") Long id)
+    {
+        return R.ok().put("data",qwExternalContactInfoService.selectQwExternalContactInfoByExternalContactId(id));
+    }
+
 }

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/live/LiveDataController.java

@@ -102,7 +102,7 @@ public class LiveDataController extends BaseController
         CompanyUser user = tokenService.getLoginUser(request).getUser();
         List<LiveUserDetailExportVO> list = new ArrayList<>();
         if ("00".equals(user.getUserType())) {
-            liveDataService.getLiveUserDetailListBySql(liveId, user.getCompanyId(), null);
+            list = liveDataService.exportLiveUserDetail(liveId, user.getCompanyId(), null);
         } else {
             list = liveDataService.exportLiveUserDetail(liveId,user.getCompanyId(),user.getUserId());
         }

+ 1 - 1
fs-live-app/src/main/java/com/fs/live/controller/LiveController.java

@@ -126,7 +126,7 @@ public class LiveController {
 
 		LinkedHashMap<String,Object> result = (LinkedHashMap<String,Object>) params.get("WorkflowExecution");
 		String string = result.get("Object").toString();
-		videoService.updateFinishStatus(videoUrl + string.replace(".mp4", "-1080.m3u8"));
+		videoService.updateFinishStatus(string.replace(".mp4", ".m3u8"));
 
 		return R.ok();
 //		{app=200149.push.tlivecloud.com, appid=1319721001, appname=live, channel_id=673,

+ 1 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseAnswerLogsMapper.java

@@ -53,6 +53,7 @@ public interface FsCourseAnswerLogsMapper
             "\tLEFT JOIN company c on cal.company_id=c.company_id " +
             "        <where>  \n" +
             "            <if test=\"map.phone != null \"> and fu.phone = #{map.phone}</if>\n" +
+            "            <if test=\"map.logId != null \"> and cal.log_id = #{map.logId}</if>\n" +
             "            <if test=\"map.courseId != null \"> and uc.course_id = #{map.courseId}</if>\n" +
             "            <if test=\"map.videoId != null \"> and cal.video_id = #{map.videoId}</if>\n" +
             "            <if test=\"map.watchLogId != null \"> and cal.watch_log_id = #{map.watchLogId}</if>\n" +

+ 1 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java

@@ -116,6 +116,7 @@ public interface FsCourseRedPacketLogMapper
             "LEFT JOIN qw_user qu on qu.id= l.qw_user_id  \n" +
             "where 1=1   " +
             "<if test = ' maps.userId !=null '> and l.user_id = #{maps.userId} </if>" +
+            "<if test = ' maps.logId !=null '> and l.log_id = #{maps.logId} </if>" +
             "<if test = ' maps.watchLogId !=null '> and l.watch_log_id = #{maps.watchLogId} </if>" +
             "<if test = ' maps.companyId !=null '> and l.company_id = #{maps.companyId} </if>" +
             "<if test = ' maps.companyUserId !=null '> and l.company_user_id = #{maps.companyUserId} </if>" +

+ 2 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseAnswerLogsParam.java

@@ -12,6 +12,8 @@ import java.util.Set;
 
 @Data
 public class FsCourseAnswerLogsParam  extends BaseEntity  {
+
+    private Long logId;
     private String phone;
     private String phoneMk;
     private String courseId;

+ 1 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseRedPacketLogParam.java

@@ -11,6 +11,7 @@ import java.util.stream.Collectors;
 @Data
 public class FsCourseRedPacketLogParam implements Serializable {
 
+    private Long logId;
     private Long userId;
 
     private Long companyId;

+ 2 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java

@@ -11,6 +11,8 @@ import java.util.stream.Collectors;
 @Data
 public class FsCourseWatchLogListParam implements Serializable {
 
+    private Long logId;
+
     private Long userId;
 
     private Long qwUserId;

+ 0 - 19
fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -380,7 +380,6 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
 
         List<FsCourseWatchLog> logs = new ArrayList<>();
-        List<FsCourseWatchLog> finishedLogs = new ArrayList<>();
         for (String key : keys) {
             //取key中数据
             String[] parts = key.split(":");
@@ -419,17 +418,12 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
                     redisCache.deleteObject(heartbeatKey);
                     // 完课删除看课时长记录
                     redisCache.deleteObject(key);
-                    finishedLogs.add(watchLog);
                 }
             }
             //集合中增加
             logs.add(watchLog);
         }
         batchUpdateFsUserCourseWatchLog(logs,100);
-
-        if(CollectionUtils.isNotEmpty(finishedLogs)){
-            fsTagUpdateService.onCourseWatchFinishedBatch(finishedLogs);
-        }
     }
     public Long getFsUserVideoDuration(Long videoId){
         //将视频时长也存到redis
@@ -458,7 +452,6 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         Collection<String> keys = redisCache.keys("h5wxuser:watch:heartbeat:*");
         LocalDateTime now = LocalDateTime.now();
         List<FsCourseWatchLog> logs = new ArrayList<>();
-        List<FsCourseWatchLog> watchingLogs = new ArrayList<>();
         for (String key : keys) {
             FsCourseWatchLog watchLog = new FsCourseWatchLog();
             String[] parts = key.split(":");
@@ -483,14 +476,10 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
                 redisCache.deleteObject(key);
             }else {
                 watchLog.setLogType(1);
-                watchingLogs.add(watchLog);
             }
             logs.add(watchLog);
         }
         batchUpdateFsUserCourseWatchLog(logs,100);
-        if(CollectionUtils.isNotEmpty(watchingLogs)){
-            fsTagUpdateService.onCourseWatchingBatch(watchingLogs);
-        }
     }
 
     @Override
@@ -513,7 +502,6 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
 
         List<FsCourseWatchLog> logs = new ArrayList<>();
-        List<FsCourseWatchLog> finishedLogs = new ArrayList<>();
         for (String key : keys) {
             //取key中数据
             Long videoId=null;
@@ -562,8 +550,6 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
                     redisCache.deleteObject(heartbeatKey);
                     // 完课删除看课时长记录
                     redisCache.deleteObject(key);
-
-                    finishedLogs.add(watchLog);
                 }
             }
             //集合中增加
@@ -571,11 +557,6 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         }
 
         batchUpdateFsCourseWatchLogIsOpen(logs,100);
-
-        // 完课打标签
-        if(CollectionUtils.isNotEmpty(finishedLogs)){
-            fsTagUpdateService.onCourseWatchFinishedBatch(finishedLogs);
-        }
     }
 
     public void batchUpdateFsCourseWatchLogIsOpen(List<FsCourseWatchLog> logs, int batchSize) {

+ 1 - 0
fs-service/src/main/java/com/fs/his/service/impl/MerchantAppConfigServiceImpl.java

@@ -52,6 +52,7 @@ public class MerchantAppConfigServiceImpl extends ServiceImpl<MerchantAppConfigM
                 Thread.sleep(5000);
                 Integer count = baseMapper.checkTableExists();
                 if (ObjectUtil.isNotNull(count)&&count>0) {
+                    log.info("异步初始化商户配置表完成");
                     return;
                 }
                 // 1. 检查并创建表

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

@@ -1,7 +1,9 @@
 package com.fs.hisStore.mapper;
 
 import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.hisStore.param.MergedAfterSalesQueryParam;
 import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.fs.hisStore.vo.MergedAfterSalesVO;
 import com.fs.live.param.MergedOrderQueryParam;
 import com.fs.live.vo.MergedOrderVO;
 import org.apache.ibatis.annotations.Param;
@@ -25,6 +27,14 @@ public interface MergedOrderMapper
      */
     List<MergedOrderVO> selectMergedOrderList(@Param("maps") MergedOrderQueryParam param);
 
+    /**
+     * 查询合并的售后列表(商城售后+直播售后)
+     *
+     * @param param 查询参数
+     * @return 合并后的售后列表
+     */
+    List<MergedAfterSalesVO> selectMergedAfterSalesList(@Param("maps") MergedAfterSalesQueryParam param);
+
     /**
      * 查询合并的订单列表(商城订单+直播订单)
      *

+ 31 - 0
fs-service/src/main/java/com/fs/hisStore/param/MergedAfterSalesDeliveryParam.java

@@ -0,0 +1,31 @@
+package com.fs.hisStore.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+/**
+ * 合并售后物流参数
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedAfterSalesDeliveryParam implements Serializable
+{
+    private Long userId;
+    
+    /** 售后ID */
+    private Long salesId;
+
+    @NotBlank(message = "物流单号不能为空")
+    private String deliverySn;
+
+    @NotBlank(message = "物流公司不能为空")
+    private String deliveryName;
+
+    /** 售后类型 1商城售后 2直播售后 */
+    private Integer afterSalesType;
+}
+

+ 43 - 0
fs-service/src/main/java/com/fs/hisStore/param/MergedAfterSalesParam.java

@@ -0,0 +1,43 @@
+package com.fs.hisStore.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 合并售后申请参数
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedAfterSalesParam implements Serializable
+{
+    /** 订单号 */
+    @NotBlank
+    private String orderCode;
+
+    /** 服务类型 0仅退款1退货退款 */
+    @NotBlank
+    private Integer serviceType;
+
+    /** 申请原因 */
+    @NotBlank
+    private String reasons;
+
+    /** 申请说明 */
+    private String explains;
+
+    /** 申请说明图片 */
+    private String explainImg;
+
+    private BigDecimal refundAmount;
+
+    /** 商品数据 */
+    @NotBlank
+    private List<FsStoreAfterSalesProductParam> productList;
+}
+

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

@@ -0,0 +1,23 @@
+package com.fs.hisStore.param;
+
+import com.fs.common.param.BaseQueryParam;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 合并售后查询参数
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedAfterSalesQueryParam extends BaseQueryParam implements Serializable
+{
+    /** 状态 1待处理 2已完成 */
+    private Integer status;
+    
+    /** 用户ID */
+    private Long userId;
+}
+

+ 22 - 0
fs-service/src/main/java/com/fs/hisStore/param/MergedAfterSalesRevokeParam.java

@@ -0,0 +1,22 @@
+package com.fs.hisStore.param;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 合并售后撤销参数
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedAfterSalesRevokeParam implements Serializable
+{
+    /** 售后ID */
+    private Long salesId;
+
+    /** 售后类型 1商城售后 2直播售后 */
+    private Integer afterSalesType;
+}
+

+ 25 - 0
fs-service/src/main/java/com/fs/hisStore/param/MergedOrderDeleteParam.java

@@ -0,0 +1,25 @@
+package com.fs.hisStore.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 合并订单删除参数
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedOrderDeleteParam implements Serializable
+{
+    /** 订单ID */
+    @NotNull(message = "订单ID不能为空")
+    private Long orderId;
+
+    /** 订单类型 1商城订单 2直播订单 */
+    @NotNull(message = "订单类型不能为空")
+    private Integer orderType;
+}
+

+ 56 - 1
fs-service/src/main/java/com/fs/hisStore/service/IMergedOrderService.java

@@ -1,10 +1,13 @@
 package com.fs.hisStore.service;
 
-import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.common.core.domain.R;
+import com.fs.hisStore.param.*;
 import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.fs.hisStore.vo.MergedAfterSalesVO;
 import com.fs.live.param.MergedOrderQueryParam;
 import com.fs.live.vo.MergedOrderVO;
 
+import java.text.ParseException;
 import java.util.List;
 
 /**
@@ -24,5 +27,57 @@ public interface IMergedOrderService
     List<FsMergedOrderListQueryVO> selectMergedOrderListVO(FsMyStoreOrderQueryParam param);
 
     List<MergedOrderVO> selectMergedOrderList(MergedOrderQueryParam param);
+
+    /**
+     * 查询合并的售后列表(商城售后+直播售后)
+     *
+     * @param param 查询参数
+     * @return 合并后的售后列表
+     */
+    List<MergedAfterSalesVO> selectMergedAfterSalesList(MergedAfterSalesQueryParam param);
+
+    /**
+     * 申请售后
+     *
+     * @param userId 用户ID
+     * @param param 售后参数
+     * @return 结果
+     */
+    R applyForAfterSales(String userId, MergedAfterSalesParam param);
+
+    /**
+     * 撤销售后
+     *
+     * @param userId 用户ID
+     * @param param 撤销参数
+     * @return 结果
+     */
+    R revokeAfterSales(String userId, MergedAfterSalesRevokeParam param) throws ParseException;
+
+    /**
+     * 提交物流信息
+     *
+     * @param param 物流参数
+     * @return 结果
+     */
+    R addDelivery(MergedAfterSalesDeliveryParam param);
+
+    /**
+     * 查询售后详情
+     *
+     * @param salesId 售后ID
+     * @param afterSalesType 售后类型 1商城售后 2直播售后
+     * @return 售后详情
+     */
+    MergedAfterSalesVO selectMergedAfterSalesById(Long salesId, Integer afterSalesType);
+
+    /**
+     * 删除订单(逻辑删除)
+     *
+     * @param userId 用户ID
+     * @param param 删除参数
+     * @return 结果
+     */
+    R deleteOrder(String userId, MergedOrderDeleteParam param);
 }
 

+ 42 - 4
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -756,6 +756,11 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         }
         FsStoreOrderPriceDTO priceGroup = this.getOrderPriceGroup(carts, userAddress);
         BigDecimal payPostage = priceGroup.getStorePostage();
+        BigDecimal badCode = BigDecimal.valueOf(-1);
+        // 检查运费计算结果,如果是 -1 表示偏远地区不可购买
+        if (payPostage.compareTo(badCode) == 0) {
+            throw new ServiceException("偏远地区暂不可购买");
+        }
         payPrice = NumberUtil.add(payPrice, payPostage);
 
         FsUserScrm user = userService.selectFsUserById(uid);
@@ -827,7 +832,16 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         FsStoreOrderComputedParam computedParam = new FsStoreOrderComputedParam();
         BeanUtils.copyProperties(param, computedParam);
         //计算金额
-        FsStoreOrderComputeDTO dto = this.computedOrder(userId, computedParam);
+        FsStoreOrderComputeDTO dto;
+        try {
+            dto = this.computedOrder(userId, computedParam);
+        } catch (ServiceException e) {
+            // 捕获运费模板检查异常,直接返回错误
+            if ("偏远地区暂不可购买".equals(e.getMessage())) {
+                return R.error("偏远地区暂不可购买");
+            }
+            throw e;
+        }
         String cartIds = redisCache.getCacheObject("orderKey:" + param.getOrderKey());
         Integer payType = redisCache.getCacheObject("createOrderPayType:" + param.getCreateOrderKey());
         if (payType != null) {
@@ -841,7 +855,8 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             //获取地址
             FsUserAddressScrm address = userAddressMapper.selectFsUserAddressById(param.getAddressId());
             //生成分布式唯一值
-            String orderSn = IdUtil.getSnowflake(0, 0).nextIdStr();
+
+            String orderSn = OrderCodeUtils.getOrderSn();
             //是否使用积分
             Boolean isIntegral = false;
             //组合数据
@@ -3072,6 +3087,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
      */
     private BigDecimal handlePostage(List<FsStoreCartQueryVO> cartInfo, FsUserAddressScrm userAddress) {
         BigDecimal storePostage = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if (userAddress != null) {
             if (userAddress.getCityId() == null) {
                 return storePostage;
@@ -3083,14 +3099,26 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             List<Long> tempIds = cartInfo
                     .stream()
                     .map(FsStoreCartQueryVO::getTempId)
+                    .filter(tempId -> tempId != null)
                     .collect(Collectors.toList());
 
+            if (tempIds.isEmpty()) {
+                return storePostage;
+            }
+
             //获取商品用到的运费模板
             List<FsShippingTemplatesScrm> shippingTemplatesList = shippingTemplatesService.selectFsShippingTemplatesByIds(StringUtils.join(tempIds, ","));
 
             //获取运费模板区域列表按照城市排序
             List<FsShippingTemplatesRegionScrm> shippingTemplatesRegionList = shippingTemplatesRegionService.selectFsShippingTemplatesRegionListByTempIdsAndCityIds(StringUtils.join(tempIds, ","), StringUtils.join(citys, ","));
 
+            // 有运费模板,但当前城市没有匹配的区域
+            if (shippingTemplatesList != null && !shippingTemplatesList.isEmpty()
+                    && (shippingTemplatesRegionList == null || shippingTemplatesRegionList.isEmpty())) {
+                logger.error("运费模板存在,但城市不在运费模板区域内,cityId: {}", cityId);
+                return badCode;
+            }
+
             //提取运费模板类型
             Map<Long, Integer> shippingTemplatesMap = shippingTemplatesList
                     .stream()
@@ -3107,6 +3135,18 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             Map<Long, TemplateDTO> templateDTOMap = new HashMap<>();
             for (FsStoreCartQueryVO storeCartVO : cartInfo) {
                 Long tempId = storeCartVO.getTempId();
+                if (tempId == null) {
+                    continue;
+                }
+                // 检查该商品的运费模板是否有对应的区域配置
+                FsShippingTemplatesRegionScrm shippingTemplatesRegion = shippingTemplatesRegionMap.get(tempId);
+                // 如果商品有运费模板,但没有找到对应的区域配置,返回错误码
+                if (shippingTemplatesList != null && !shippingTemplatesList.isEmpty()
+                        && shippingTemplatesList.stream().anyMatch(t -> t.getId().equals(tempId))
+                        && shippingTemplatesRegion == null) {
+                    logger.error("商品运费模板存在,但城市不在运费模板区域内,tempId: {}, cityId: {}", tempId, cityId);
+                    return badCode;
+                }
                 //根据模板类型获取相应的数量
                 double num = 0d;
                 if (ShippingTempEnum.TYPE_1.getValue().equals(shippingTemplatesMap.get(tempId))) {
@@ -3118,8 +3158,6 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                     num = NumberUtil.mul(storeCartVO.getCartNum(),
                             storeCartVO.getVolume()).doubleValue();
                 }
-
-                FsShippingTemplatesRegionScrm shippingTemplatesRegion = shippingTemplatesRegionMap.get(tempId);
                 BigDecimal price = NumberUtil.round(NumberUtil.mul(storeCartVO.getCartNum(),
                         storeCartVO.getPrice()), 2);
                 if (!templateDTOMap.containsKey(tempId)) {

+ 230 - 1
fs-service/src/main/java/com/fs/hisStore/service/impl/MergedOrderServiceImpl.java

@@ -2,20 +2,37 @@ package com.fs.hisStore.service.impl;
 
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONUtil;
+import com.fs.common.core.domain.R;
 import com.fs.common.utils.StringUtils;
+import com.fs.hisStore.domain.FsStoreAfterSalesItemScrm;
+import com.fs.hisStore.domain.FsStoreAfterSalesScrm;
 import com.fs.hisStore.enums.OrderInfoEnum;
 import com.fs.hisStore.mapper.MergedOrderMapper;
-import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.hisStore.param.*;
+import com.fs.hisStore.service.IFsStoreAfterSalesItemScrmService;
+import com.fs.hisStore.service.IFsStoreAfterSalesScrmService;
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.hisStore.service.IMergedOrderService;
 import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
 import com.fs.hisStore.vo.FsStoreOrderItemVO;
+import com.fs.hisStore.vo.MergedAfterSalesVO;
+import com.fs.live.domain.LiveAfterSales;
+import com.fs.live.domain.LiveAfterSalesItem;
+import com.fs.live.param.LiveAfterSalesDeliveryParam;
+import com.fs.live.param.LiveAfterSalesParam;
+import com.fs.live.param.LiveAfterSalesRevokeParam;
 import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.service.ILiveAfterSalesItemService;
+import com.fs.live.service.ILiveAfterSalesService;
+import com.fs.live.service.ILiveOrderService;
 import com.fs.live.vo.MergedOrderVO;
 import com.fs.store.config.StoreConfig;
 import com.fs.system.service.ISysConfigService;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.text.ParseException;
 import java.util.*;
 
 /**
@@ -65,6 +82,24 @@ public class MergedOrderServiceImpl implements IMergedOrderService
     @Autowired
     private ISysConfigService configService;
 
+    @Autowired
+    private IFsStoreAfterSalesScrmService storeAfterSalesService;
+
+    @Autowired
+    private ILiveAfterSalesService liveAfterSalesService;
+
+    @Autowired
+    private IFsStoreOrderScrmService storeOrderService;
+
+    @Autowired
+    private ILiveOrderService liveOrderService;
+
+    @Autowired
+    private IFsStoreAfterSalesItemScrmService storeAfterSalesItemService;
+
+    @Autowired
+    private ILiveAfterSalesItemService liveAfterSalesItemService;
+
     /*
      * 小程序合并
      * */
@@ -130,5 +165,199 @@ public class MergedOrderServiceImpl implements IMergedOrderService
 
         return list;
     }
+
+    @Override
+    public List<MergedAfterSalesVO> selectMergedAfterSalesList(MergedAfterSalesQueryParam param) {
+        List<MergedAfterSalesVO> list = mergedOrderMapper.selectMergedAfterSalesList(param);
+        
+        // 填充售后商品列表
+        for (MergedAfterSalesVO vo : list) {
+            if (vo.getAfterSalesType() != null && vo.getAfterSalesType() == 1) {
+                // 商城售后
+                FsStoreAfterSalesItemScrm itemParam = new FsStoreAfterSalesItemScrm();
+                itemParam.setStoreAfterSalesId(vo.getId());
+                List<FsStoreAfterSalesItemScrm> items = storeAfterSalesItemService.selectFsStoreAfterSalesItemList(itemParam);
+                vo.setItems(items);
+            } else if (vo.getAfterSalesType() != null && vo.getAfterSalesType() == 2) {
+                // 直播售后
+                List<LiveAfterSalesItem> items = liveAfterSalesItemService.selectLiveAfterSalesItemByAfterId(vo.getId());
+                vo.setItems(items);
+            }
+        }
+        
+        return list;
+    }
+
+    @Override
+    public R applyForAfterSales(String userId, MergedAfterSalesParam param) {
+        // 根据订单号判断是商城订单还是直播订单
+        try {
+            // 先尝试查询商城订单
+            com.fs.hisStore.domain.FsStoreOrderScrm storeOrder = storeOrderService.selectFsStoreOrderByOrderCode(param.getOrderCode());
+            if (storeOrder != null) {
+                // 商城订单,调用商城售后服务
+                FsStoreAfterSalesParam storeParam = new FsStoreAfterSalesParam();
+                BeanUtils.copyProperties(param, storeParam);
+                return storeAfterSalesService.applyForAfterSales(Long.parseLong(userId), storeParam);
+            }
+        } catch (Exception e) {
+            // 商城订单不存在,继续尝试直播订单
+        }
+        
+        // 尝试查询直播订单
+        com.fs.live.domain.LiveOrder liveOrder = liveOrderService.selectLiveOrderByOrderCode(param.getOrderCode());
+        if (liveOrder != null) {
+            // 直播订单,调用直播售后服务
+            LiveAfterSalesParam liveParam = new LiveAfterSalesParam();
+            liveParam.setOrderCode(param.getOrderCode());
+            liveParam.setServiceType(param.getServiceType());
+            liveParam.setReasons(param.getReasons());
+            liveParam.setExplains(param.getExplains());
+            liveParam.setExplainImg(param.getExplainImg());
+            liveParam.setRefundAmount(param.getRefundAmount());
+            // 转换商品列表
+            if (param.getProductList() != null) {
+                List<com.fs.live.param.LiveAfterSalesProductParam> liveProductList = new ArrayList<>();
+                for (FsStoreAfterSalesProductParam product : param.getProductList()) {
+                    com.fs.live.param.LiveAfterSalesProductParam liveProduct = new com.fs.live.param.LiveAfterSalesProductParam();
+                    liveProduct.setProductId(product.getProductId());
+                    liveProduct.setNum(product.getNum());
+                    liveProductList.add(liveProduct);
+                }
+                liveParam.setProductList(liveProductList);
+            }
+            return liveAfterSalesService.applyForAfterSales(userId, liveParam);
+        }
+        
+        return R.error("订单不存在");
+    }
+
+    @Override
+    public R revokeAfterSales(String userId, MergedAfterSalesRevokeParam param) throws ParseException {
+        if (param.getAfterSalesType() != null && param.getAfterSalesType() == 1) {
+            // 商城售后
+            return storeAfterSalesService.revoke(Long.parseLong(userId), param.getSalesId());
+        } else if (param.getAfterSalesType() != null && param.getAfterSalesType() == 2) {
+            // 直播售后
+            LiveAfterSalesRevokeParam liveParam = new LiveAfterSalesRevokeParam();
+            liveParam.setId(param.getSalesId());
+            return liveAfterSalesService.revoke(userId, liveParam);
+        }
+        return R.error("售后类型错误");
+    }
+
+    @Override
+    public R addDelivery(MergedAfterSalesDeliveryParam param) {
+        if (param.getAfterSalesType() != null && param.getAfterSalesType() == 1) {
+            // 商城售后
+            FsStoreAfterSalesDeliveryParam storeParam = new FsStoreAfterSalesDeliveryParam();
+            storeParam.setUserId(param.getUserId());
+            storeParam.setSalesId(param.getSalesId());
+            storeParam.setDeliverySn(param.getDeliverySn());
+            storeParam.setDeliveryName(param.getDeliveryName());
+            return storeAfterSalesService.addDelivery(storeParam);
+        } else if (param.getAfterSalesType() != null && param.getAfterSalesType() == 2) {
+            // 直播售后
+            LiveAfterSalesDeliveryParam liveParam = new LiveAfterSalesDeliveryParam();
+            liveParam.setUserId(param.getUserId());
+            liveParam.setId(param.getSalesId());
+            liveParam.setDeliverySn(param.getDeliverySn());
+            liveParam.setDeliveryName(param.getDeliveryName());
+            return liveAfterSalesService.addDelivery(liveParam);
+        }
+        return R.error("售后类型错误");
+    }
+
+    @Override
+    public MergedAfterSalesVO selectMergedAfterSalesById(Long salesId, Integer afterSalesType) {
+        MergedAfterSalesVO vo = new MergedAfterSalesVO();
+        
+        if (afterSalesType != null && afterSalesType == 1) {
+            // 商城售后
+            FsStoreAfterSalesScrm storeAfterSales = storeAfterSalesService.selectFsStoreAfterSalesById(salesId);
+            if (storeAfterSales != null) {
+                BeanUtils.copyProperties(storeAfterSales, vo);
+                vo.setAfterSalesType(1);
+                vo.setAfterSalesTypeName("商城售后");
+                vo.setOrderCode(storeAfterSales.getOrderCode());
+                
+                // 填充商品列表
+                FsStoreAfterSalesItemScrm itemParam = new FsStoreAfterSalesItemScrm();
+                itemParam.setStoreAfterSalesId(salesId);
+                List<FsStoreAfterSalesItemScrm> items = storeAfterSalesItemService.selectFsStoreAfterSalesItemList(itemParam);
+                vo.setItems(items);
+            }
+        } else if (afterSalesType != null && afterSalesType == 2) {
+            // 直播售后
+            LiveAfterSales liveAfterSales = liveAfterSalesService.selectLiveAfterSalesById(salesId);
+            if (liveAfterSales != null) {
+                BeanUtils.copyProperties(liveAfterSales, vo);
+                vo.setAfterSalesType(2);
+                vo.setAfterSalesTypeName("直播售后");
+                
+                // 查询订单号
+                com.fs.live.domain.LiveOrder liveOrder = liveOrderService.selectLiveOrderByOrderId(String.valueOf(liveAfterSales.getOrderId()));
+                if (liveOrder != null) {
+                    vo.setOrderCode(liveOrder.getOrderCode());
+                }
+                
+                // 填充商品列表
+                List<LiveAfterSalesItem> items = liveAfterSalesItemService.selectLiveAfterSalesItemByAfterId(salesId);
+                vo.setItems(items);
+            }
+        }
+        
+        return vo;
+    }
+
+    @Override
+    public R deleteOrder(String userId, MergedOrderDeleteParam param) {
+        Long orderId = param.getOrderId();
+        Integer orderType = param.getOrderType();
+        
+        if (orderType == null) {
+            return R.error("订单类型不能为空");
+        }
+        
+        if (orderType == 1) {
+            // 商城订单
+            com.fs.hisStore.domain.FsStoreOrderScrm storeOrder = storeOrderService.selectFsStoreOrderById(orderId);
+            if (storeOrder == null) {
+                return R.error("订单不存在");
+            }
+            // 检查订单是否属于当前用户
+            if (!storeOrder.getUserId().equals(Long.parseLong(userId))) {
+                return R.error("无权删除该订单");
+            }
+            // 逻辑删除:设置 isDel = 1
+            storeOrder.setIsDel(1);
+            int result = storeOrderService.updateFsStoreOrder(storeOrder);
+            if (result > 0) {
+                return R.ok("删除成功");
+            } else {
+                return R.error("删除失败");
+            }
+        } else if (orderType == 2) {
+            // 直播订单
+            com.fs.live.domain.LiveOrder liveOrder = liveOrderService.selectLiveOrderByOrderId(String.valueOf(orderId));
+            if (liveOrder == null) {
+                return R.error("订单不存在");
+            }
+            // 检查订单是否属于当前用户
+            if (!liveOrder.getUserId().equals(userId)) {
+                return R.error("无权删除该订单");
+            }
+            // 逻辑删除:设置 isDel = "1"
+            liveOrder.setIsDel("1");
+            int result = liveOrderService.updateLiveOrder(liveOrder);
+            if (result > 0) {
+                return R.ok("删除成功");
+            } else {
+                return R.error("删除失败");
+            }
+        } else {
+            return R.error("订单类型错误");
+        }
+    }
 }
 

+ 103 - 0
fs-service/src/main/java/com/fs/hisStore/vo/MergedAfterSalesVO.java

@@ -0,0 +1,103 @@
+package com.fs.hisStore.vo;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 合并售后VO
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedAfterSalesVO implements Serializable
+{
+    /** 售后ID */
+    @Excel(name = "售后ID")
+    private Long id;
+
+    /** 订单号 */
+    @Excel(name = "订单号")
+    private String orderCode;
+
+    /** 退款金额 */
+    @Excel(name = "退款金额")
+    private BigDecimal refundAmount;
+
+    /** 服务类型0仅退款1退货退款 */
+    @Excel(name = "服务类型0仅退款1退货退款")
+    private Integer serviceType;
+
+    /** 申请原因 */
+    @Excel(name = "申请原因")
+    private String reasons;
+
+    /** 说明 */
+    @Excel(name = "说明")
+    private String explains;
+
+    /** 说明图片->多个用逗号分割 */
+    @Excel(name = "说明图片->多个用逗号分割")
+    private String explainImg;
+
+    /** 物流公司编码 */
+    @Excel(name = "物流公司编码")
+    private String shipperCode;
+
+    /** 物流单号 */
+    @Excel(name = "物流单号")
+    private String deliverySn;
+
+    /** 物流名称 */
+    @Excel(name = "物流名称")
+    private String deliveryName;
+
+    /** 状态 0已提交等待平台审核 1平台已审核 等待用户发货/退款 2 用户已发货 3退款成功 */
+    @Excel(name = "状态 0已提交等待平台审核 1平台已审核 等待用户发货/退款 2 用户已发货 3退款成功")
+    private Integer status;
+
+    /** 售后状态-0正常1用户取消2商家拒绝 */
+    @Excel(name = "售后状态-0正常1用户取消2商家拒绝")
+    private Integer salesStatus;
+
+    @Excel(name = "订单当前状态")
+    private Integer orderStatus;
+
+    /** 逻辑删除 */
+    @Excel(name = "逻辑删除")
+    private Integer isDel;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private Long userId;
+
+    /** 商家收货人 */
+    @Excel(name = "商家收货人")
+    private String consignee;
+
+    /** 商家手机号 */
+    @Excel(name = "商家手机号")
+    private String phoneNumber;
+
+    /** 商家地址 */
+    @Excel(name = "商家地址")
+    private String address;
+
+    private Integer isPackage;
+
+    private String packageJson;
+
+    /** 售后类型 1商城售后 2直播售后 */
+    private Integer afterSalesType;
+
+    /** 售后类型名称 */
+    private String afterSalesTypeName;
+
+    /** 售后商品列表 */
+    private List<?> items;
+}
+

+ 1 - 1
fs-service/src/main/java/com/fs/live/mapper/LiveVideoMapper.java

@@ -92,6 +92,6 @@ public interface LiveVideoMapper
     List<LiveVideo> selectByAll();
 
 
-    @Update("update live_video set finish_status = 1 where video_url = #{fileName}")
+    @Update("update live_video set finish_status = 1 where video_url like concat('%',#{fileName})")
     void updateFinishStatus(@Param("fileName") String fileName);
 }

+ 1 - 1
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -3795,7 +3795,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
     private void refundCoupon(LiveOrder order) {
         if(order.getCouponUserId()!=null){
-            LiveCouponUser couponUser=liveCouponUserService.selectLiveCouponUserById(order.getCouponUserId());
+            LiveCouponUser couponUser=liveCouponUserService.selectLiveCouponUserById(order.getUserCouponId());
             if(couponUser!=null){
                 couponUser.setStatus(0);
                 couponUser.setUseTime(null);

+ 7 - 1
fs-service/src/main/java/com/fs/qw/param/QwExternalContactParam.java

@@ -10,8 +10,12 @@ import java.util.List;
 
 @Data
 public class QwExternalContactParam {
+
     private Long id;
 
+    @TableField(exist = false)
+    private Long extId;
+
     /** 属于用户id */
     @Excel(name = "属于用户id")
     private String userId;
@@ -24,10 +28,12 @@ public class QwExternalContactParam {
 
     /** 外部联系人id */
     @Excel(name = "外部联系人id")
-    private Long externalUserId;
+    private String externalUserId;
 
     private Long companyUserId;
 
+    private Long fsUserId;
+
     /**
      * 销售员工信息(昵称,手机号)
      */

+ 3 - 3
fs-service/src/main/resources/application-config-druid-bjzm-test.yml

@@ -10,9 +10,9 @@ logging:
 wx:
   miniapp:
     configs:
-      - appid: wx94951f52d3ac5e25   #北京存在文化
-        secret: bfe27b20c6e3c4232a1d4ef36228e84b #北京存在文化
-        token: Ncbnd7lJvkripxxna6NAWCxCrvC
+      - appid: wx44beed5640bcb1ba   #北京卓美
+        secret: 1bfcfa420f741801575a74d94752d014 #北京卓美
+        token: cbnd7lJvkripVOpyTFAna6NAWCxCrvC
         aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
         msgDataFormat: JSON
   cp:

+ 3 - 3
fs-service/src/main/resources/application-config-druid-bjzm.yml

@@ -10,9 +10,9 @@ logging:
 wx:
   miniapp:
     configs:
-      - appid: wx94951f52d3ac5e25   #北京卓美
-        secret: bfe27b20c6e3c4232a1d4ef36228e84b #北京卓美
-        token: Ncbnd7lJvkripxxna6NAWCxCrvC
+      - appid: wx44beed5640bcb1ba   #北京卓美
+        secret: 1bfcfa420f741801575a74d94752d014 #北京卓美
+        token: cbnd7lJvkripVOpyTFAna6NAWCxCrvC
         aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
         msgDataFormat: JSON
   cp:

+ 3 - 0
fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml

@@ -82,6 +82,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test ='maps.userId !=null'>
                 and l.user_id = #{maps.userId}
             </if>
+            <if test ='maps.logId !=null'>
+                and l.log_id = #{maps.logId}
+            </if>
             <if test ='maps.project !=null'>
                 and l.project = #{maps.project}
             </if>

+ 81 - 0
fs-service/src/main/resources/mapper/hisStore/MergedOrderMapper.xml

@@ -399,5 +399,86 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         ORDER BY create_time DESC
     </select>
 
+    <!-- 查询合并的售后列表(商城售后+直播售后) -->
+    <select id="selectMergedAfterSalesList" parameterType="com.fs.hisStore.param.MergedAfterSalesQueryParam" resultType="com.fs.hisStore.vo.MergedAfterSalesVO">
+        SELECT * FROM (
+            -- 商城售后
+            SELECT
+                s.id,
+                s.order_code AS orderCode,
+                s.refund_amount AS refundAmount,
+                s.service_type AS serviceType,
+                s.reasons,
+                s.explains,
+                s.explain_img AS explainImg,
+                s.shipper_code AS shipperCode,
+                s.delivery_sn AS deliverySn,
+                s.delivery_name AS deliveryName,
+                s.status,
+                s.sales_status AS salesStatus,
+                s.order_status AS orderStatus,
+                s.is_del AS isDel,
+                s.user_id AS userId,
+                s.consignee,
+                s.phone_number AS phoneNumber,
+                s.address,
+                s.is_package AS isPackage,
+                s.package_json AS packageJson,
+                1 AS afterSalesType,
+                '商城售后' AS afterSalesTypeName,
+                s.create_time AS createTime
+            FROM fs_store_after_sales_scrm s
+            WHERE s.is_del = 0
+            <if test="maps.status != null and maps.status == 1">
+                AND s.sales_status = 0
+            </if>
+            <if test="maps.status != null and maps.status == 2">
+                AND s.sales_status = 3
+            </if>
+            <if test="maps.userId != null">
+                AND s.user_id = #{maps.userId}
+            </if>
+            UNION ALL
+            -- 直播售后
+            SELECT
+                s.id,
+                o.order_code AS orderCode,
+                s.refund_amount AS refundAmount,
+                s.refund_type AS serviceType,
+                s.reasons,
+                s.explains,
+                s.explain_img AS explainImg,
+                s.delivery_code AS shipperCode,
+                s.delivery_sn AS deliverySn,
+                s.delivery_name AS deliveryName,
+                s.status,
+                s.sales_status AS salesStatus,
+                s.order_status AS orderStatus,
+                s.is_del AS isDel,
+                s.user_id AS userId,
+                s.consignee,
+                s.phone_number AS phoneNumber,
+                s.address,
+                NULL AS isPackage,
+                NULL AS packageJson,
+                2 AS afterSalesType,
+                '直播售后' AS afterSalesTypeName,
+                s.create_time AS createTime
+            FROM live_after_sales s
+            LEFT JOIN live_order o ON o.order_id = s.order_id
+            WHERE s.is_del = 0
+            <if test="maps.status != null and maps.status == 1">
+                AND s.sales_status = 0
+            </if>
+            <if test="maps.status != null and maps.status == 2">
+                AND s.sales_status = 3
+            </if>
+            <if test="maps.userId != null">
+                AND s.user_id = #{maps.userId}
+            </if>
+        ) AS merged_after_sales
+        ORDER BY createTime DESC
+    </select>
+
 </mapper>
 

+ 10 - 7
fs-service/src/main/resources/mapper/live/LiveOrderMapper.xml

@@ -1133,15 +1133,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 left join fs_store_product_category_scrm fspcs on fspcs.cate_id = fsps.cate_id
                 left join company t3 on t1.company_id = t3.company_id
                 left join company_user  t4 on t4.user_id = t1.company_user_id
-                LEFT JOIN (
-                SELECT
-                    t5.*,
-                    ROW_NUMBER() OVER (PARTITION BY t5.business_code ORDER BY t5.status desc,t5.create_time DESC) as rn
-                FROM live_order_payment t5
-                WHERE t5.business_id IS NOT NULL
-            ) lop ON lop.business_id = t1.order_id AND lop.rn = 1
+        LEFT JOIN (
+        SELECT
+        sp.*,
+        ROW_NUMBER() OVER (PARTITION BY sp.business_code ORDER BY sp.create_time DESC) as rn
+        FROM live_order_payment sp
+        WHERE sp.business_code IS NOT NULL
+        ) lop ON lop.business_code = t1.order_code AND lop.rn = 1
         <where>
             t1.is_del = 0 and fsps.product_id IS NOT NULL
+            <if test="bankTransactionId != null and  bankTransactionId !=''">
+                and lop.bank_transaction_id like CONCAT('%',#{bankTransactionId},'%')
+            </if>
             <if test="orderId != null">
                 AND t1.order_id = #{orderId}
             </if>

+ 6 - 0
fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml

@@ -759,6 +759,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 <if test="isBindMini == 'noBindMini'">
                     and ec.fs_user_id is null
                 </if>
+                <if test="extId != null and extId != ''">
+                    and ec.id = #{extId}
+                </if>
+                <if test="fsUserId != null and fsUserId != ''">
+                    and ec.fs_user_id = #{fsUserId}
+                </if>
                 <if test="lossTime != null">
                     and DATE(ec.loss_time) = DATE(#{lossTime})
                 </if>

+ 97 - 4
fs-user-app/src/main/java/com/fs/app/controller/live/LiveController.java

@@ -1,5 +1,6 @@
 package com.fs.app.controller.live;
 
+import com.alibaba.fastjson.JSONObject;
 import com.fs.app.annotation.Login;
 import com.fs.app.controller.AppBaseController;
 import com.fs.app.facade.LiveFacadeService;
@@ -9,13 +10,23 @@ import com.fs.common.core.domain.BaseEntity;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.PageRequest;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.bean.BeanUtils;
+import com.fs.common.utils.http.HttpUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyMapper;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
 import com.fs.live.domain.Live;
 import com.fs.live.domain.LiveMsg;
 import com.fs.live.param.LiveNotifyParam;
 import com.fs.live.service.*;
 import com.fs.live.vo.LiveVo;
+import com.fs.wx.miniapp.config.WxMaProperties;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -30,10 +41,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 
 import java.time.LocalDateTime;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 
 @Api("直播信息接口")
@@ -55,6 +63,10 @@ public class LiveController extends AppBaseController {
 	private ILiveVideoService videoService;
 	@Autowired
 	private LiveFacadeService liveFacadeService;
+	@Autowired
+	private ICompanyUserService companyUserService;
+	@Autowired
+	private IFsUserService userService;
 
 	/**
 	 * 查询未结束直播间(销售专用)
@@ -280,5 +292,86 @@ public class LiveController extends AppBaseController {
 
 	}
 
+	@Autowired
+	private WxMaProperties properties;
+	
+	@Autowired
+	private RedisCache redisCache;
+	
+	@ApiOperation("微信直播间urlScheme")
+	@GetMapping("/getAppletScheme")
+	public R getAppletScheme(@RequestParam(value = "liveId") Long liveId,@RequestParam(value = "companyUserId") Long companyUserId) {
+		try {
+			String userId = getUserId();
+			Live live = liveService.selectLiveDbByLiveId(liveId);
+			if (live == null) {
+				return R.error("未找到直播间");
+			}
+			FsUser fsUser = userService.selectFsUserById(Long.valueOf(userId));
+			if (fsUser == null) {
+				return R.error("未查询到用户数据");
+			}
+			CompanyUser companyUser = companyUserService.selectCompanyUserById(companyUserId);
+			if (companyUser == null) {
+				return R.error("未查询到销售信息,链接生成失败");
+			}
+			String param = "liveId=" + liveId + "&companyUserId=" + companyUser.getUserId() + "&companyId=" + companyUser.getCompanyId() ;
+			String appId = properties.getConfigs().get(0).getAppid();
+			String secret = properties.getConfigs().get(0).getSecret();
+			
+			// 从 Redis 缓存中获取 access_token
+			String cacheKey = "wx:access_token:" + appId;
+			String access_token = redisCache.getCacheObject(cacheKey);
+			
+			// 如果缓存中没有或已过期,则重新获取
+			if (StringUtils.isEmpty(access_token)) {
+				String rspStr = HttpUtils.sendGet("https://api.weixin.qq.com/cgi-bin/token", "grant_type=client_credential&" + "appid=" + appId + "&secret=" + secret);
+				JSONObject obj = JSONObject.parseObject(rspStr);
+				access_token = obj.getString("access_token");
+				
+				// 检查是否获取成功
+				if (StringUtils.isEmpty(access_token)) {
+					log.error("获取微信 access_token 失败: {}", obj);
+					return R.error("获取微信 access_token 失败");
+				}
+				
+				// 将 access_token 存入 Redis,缓存时间为 7200 秒
+				redisCache.setCacheObject(cacheKey, access_token, 7200, java.util.concurrent.TimeUnit.SECONDS);
+				log.info("微信 access_token 已刷新并缓存,appId: {}", appId);
+			} else {
+				log.debug("从 Redis 缓存中获取 access_token,appId: {}", appId);
+			}
+			
+			JSONObject jump_wxaObj = new JSONObject();
+			// 跳转直播间
+			jump_wxaObj.put("page_url", "pages_course/living.html?" + param);
+			String paramStr = jump_wxaObj.toJSONString();
+			String postStr = HttpUtils.sendPost("https://api.weixin.qq.com/wxa/genwxashortlink?access_token=" + access_token, paramStr);
+			JSONObject obj = JSONObject.parseObject(postStr);
+			
+			// 如果 access_token 失效,清除缓存并重新获取
+			if (obj != null && (obj.getInteger("errcode") != null && obj.getInteger("errcode") == 40001)) {
+				log.warn("access_token 已失效,清除缓存并重新获取,appId: {}", appId);
+				redisCache.deleteObject(cacheKey);
+				// 重新获取 access_token
+				String rspStr = HttpUtils.sendGet("https://api.weixin.qq.com/cgi-bin/token", "grant_type=client_credential&" + "appid=" + appId + "&secret=" + secret);
+				JSONObject tokenObj = JSONObject.parseObject(rspStr);
+				access_token = tokenObj.getString("access_token");
+				if (StringUtils.isNotEmpty(access_token)) {
+					redisCache.setCacheObject(cacheKey, access_token, 7200, java.util.concurrent.TimeUnit.SECONDS);
+					// 重新调用接口
+					postStr = HttpUtils.sendPost("https://api.weixin.qq.com/wxa/genwxashortlink?access_token=" + access_token, paramStr);
+					obj = JSONObject.parseObject(postStr);
+				}
+			}
+			
+			//response.addHeader("Access-Control-Allow-Origin", "*");
+			return R.ok().put("result", obj);
+		} catch (Exception e) {
+			log.error("生成小程序 Scheme 失败", e);
+			return R.error("操作失败");
+		}
+	}
+
 
 }

+ 57 - 4
fs-user-app/src/main/java/com/fs/app/controller/live/OrderController.java

@@ -2,20 +2,22 @@ package com.fs.app.controller.live;
 
 import com.fs.app.annotation.Login;
 import com.fs.app.controller.AppBaseController;
+import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.R;
-import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.hisStore.param.*;
 import com.fs.hisStore.service.IMergedOrderService;
 import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.fs.hisStore.vo.MergedAfterSalesVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.text.ParseException;
 import java.util.List;
 
 @Api("订单信息接口")
@@ -54,4 +56,55 @@ public class OrderController extends AppBaseController {
         
         return R.ok().put("data", listPageInfo);
     }
+
+    @Login
+    @GetMapping("/getMergedAfterSalesList")
+    @ApiOperation(value = "获取合并售后列表", notes = "获取合并售后列表(商城售后+直播售后)")
+    public R getMergedAfterSalesList(MergedAfterSalesQueryParam param) {
+        PageHelper.startPage(param.getPage(), param.getPageSize());
+        param.setUserId(Long.parseLong(getUserId()));
+        List<MergedAfterSalesVO> list = mergedOrderService.selectMergedAfterSalesList(param);
+        PageInfo<MergedAfterSalesVO> listPageInfo = new PageInfo<>(list);
+        return R.ok().put("data", listPageInfo);
+    }
+
+//    @Login
+//    @PostMapping("/applyAfterSales")
+//    @ApiOperation(value = "申请售后", notes = "申请售后")
+//    @RepeatSubmit
+//    public R applyAfterSales(@RequestBody MergedAfterSalesParam param) {
+//        return mergedOrderService.applyForAfterSales(getUserId(), param);
+//    }
+//
+//    @Login
+//    @PostMapping("/revokeAfterSales")
+//    @ApiOperation(value = "撤销售后", notes = "撤销售后")
+//    @RepeatSubmit
+//    public R revokeAfterSales(@RequestBody MergedAfterSalesRevokeParam param) throws ParseException {
+//        return mergedOrderService.revokeAfterSales(getUserId(), param);
+//    }
+//
+//    @Login
+//    @PostMapping("/addAfterSalesDelivery")
+//    @ApiOperation(value = "提交物流信息", notes = "提交物流信息")
+//    public R addAfterSalesDelivery(@Validated @RequestBody MergedAfterSalesDeliveryParam param) {
+//        param.setUserId(Long.parseLong(getUserId()));
+//        return mergedOrderService.addDelivery(param);
+//    }
+
+//    @Login
+    @GetMapping("/getMergedAfterSalesDetails")
+    @ApiOperation(value = "获取合并售后详情", notes = "获取合并售后详情")
+    public R getMergedAfterSalesDetails(@RequestParam("salesId") Long salesId, @RequestParam("afterSalesType") Integer afterSalesType) {
+        MergedAfterSalesVO vo = mergedOrderService.selectMergedAfterSalesById(salesId, afterSalesType);
+        return R.ok().put("sales", vo);
+    }
+
+    @Login
+    @PostMapping("/deleteOrder")
+    @ApiOperation(value = "删除订单", notes = "删除订单(逻辑删除)")
+    @RepeatSubmit
+    public R deleteOrder(@Validated @RequestBody MergedOrderDeleteParam param) {
+        return mergedOrderService.deleteOrder(getUserId(), param);
+    }
 }

+ 10 - 2
fs-user-app/src/main/java/com/fs/app/controller/store/StoreOrderScrmController.java

@@ -194,8 +194,16 @@ public class StoreOrderScrmController extends AppBaseController {
     @ApiOperation("计算订单金额")
     @PostMapping("/computed")
     public R computed(@Validated @RequestBody FsStoreOrderComputedParam param, HttpServletRequest request){
-        FsStoreOrderComputeDTO dto=orderService.computedOrder(Long.parseLong(getUserId()),param);
-        return R.ok().put("data",dto);
+        try {
+            FsStoreOrderComputeDTO dto = orderService.computedOrder(Long.parseLong(getUserId()), param);
+            return R.ok().put("data", dto);
+        } catch (com.fs.common.exception.ServiceException e) {
+            // 捕获运费模板检查异常,直接返回错误
+            if ("偏远地区暂不可购买".equals(e.getMessage())) {
+                return R.error("偏远地区暂不可购买");
+            }
+            throw e;
+        }
     }
 
     @Login