Browse Source

Merge remote-tracking branch 'origin/master' into Payment-Configuration

# Conflicts:
#	fs-service/src/main/java/com/fs/huifuPay/service/impl/HuiFuServiceImpl.java
yfh 2 ngày trước cách đây
mục cha
commit
b103a6533c
55 tập tin đã thay đổi với 1776 bổ sung71 xóa
  1. 4 1
      fs-admin/src/main/java/com/fs/course/controller/FsCourseRedPacketLogController.java
  2. 26 0
      fs-admin/src/main/java/com/fs/course/controller/FsCourseWatchLogController.java
  3. 41 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java
  4. 27 0
      fs-admin/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java
  5. 1 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java
  6. 55 0
      fs-admin/src/main/java/com/fs/live/controller/OrderController.java
  7. 7 1
      fs-company/src/main/java/com/fs/company/controller/live/LiveDataController.java
  8. 6 2
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  9. 1 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseProductOrderServiceImpl.java
  10. 4 2
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  11. 8 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogStatisticsListVO.java
  12. 54 0
      fs-service/src/main/java/com/fs/course/vo/FsPeriodCountExportVO.java
  13. 2 0
      fs-service/src/main/java/com/fs/his/service/impl/FsInquiryOrderServiceImpl.java
  14. 8 2
      fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderServiceImpl.java
  15. 1 0
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreAfterSalesServiceImpl.java
  16. 1 0
      fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java
  17. 1 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserInformationCollectionServiceImpl.java
  18. 10 0
      fs-service/src/main/java/com/fs/hisStore/mapper/MergedOrderMapper.java
  19. 4 0
      fs-service/src/main/java/com/fs/hisStore/service/IMergedOrderService.java
  20. 2 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java
  21. 1 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  22. 26 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
  23. 6 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsUserAddressScrmServiceImpl.java
  24. 55 11
      fs-service/src/main/java/com/fs/hisStore/service/impl/MergedOrderServiceImpl.java
  25. 15 0
      fs-service/src/main/java/com/fs/huifuPay/sdk/opps/core/request/V2TradePaymentScanpayRefundRequest.java
  26. 37 0
      fs-service/src/main/java/com/fs/live/enums/LiveGoodsAddErrorEnum.java
  27. 85 0
      fs-service/src/main/java/com/fs/live/param/MergedOrderQueryParam.java
  28. 1 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java
  29. 96 2
      fs-service/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java
  30. 29 1
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  31. 195 0
      fs-service/src/main/java/com/fs/live/vo/MergedOrderVO.java
  32. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  33. 3 2
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  34. 3 0
      fs-service/src/main/java/com/fs/sop/domain/QwSopTempRules.java
  35. 19 0
      fs-service/src/main/java/com/fs/sop/mapper/QwSopTempRulesMapper.java
  36. 2 0
      fs-service/src/main/java/com/fs/sop/service/IQwSopTempContentService.java
  37. 1 0
      fs-service/src/main/java/com/fs/sop/service/IQwSopTempDayService.java
  38. 2 0
      fs-service/src/main/java/com/fs/sop/service/IQwSopTempRulesService.java
  39. 6 0
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempContentServiceImpl.java
  40. 7 0
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempDayServiceImpl.java
  41. 7 2
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempRulesServiceImpl.java
  42. 111 35
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  43. 1 1
      fs-service/src/main/resources/application-config-druid-ddgy.yml
  44. 101 0
      fs-service/src/main/resources/application-config-druid-hsyy.yml
  45. 1 1
      fs-service/src/main/resources/application-config-druid-yxj.yml
  46. 3 1
      fs-service/src/main/resources/application-druid-cfryt.yml
  47. 172 0
      fs-service/src/main/resources/application-druid-hsyy.yml
  48. 93 0
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  49. 1 1
      fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml
  50. 1 0
      fs-service/src/main/resources/mapper/hisStore/FsUserAddressScrmMapper.xml
  51. 403 0
      fs-service/src/main/resources/mapper/hisStore/MergedOrderMapper.xml
  52. 3 3
      fs-service/src/main/resources/mapper/live/LiveDataMapper.xml
  53. 9 1
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  54. 15 0
      fs-user-app/src/main/java/com/fs/app/controller/IntegralController.java
  55. 1 1
      fs-user-app/src/main/java/com/fs/app/controller/store/AddressScrmController.java

+ 4 - 1
fs-admin/src/main/java/com/fs/course/controller/FsCourseRedPacketLogController.java

@@ -104,7 +104,10 @@ public class FsCourseRedPacketLogController extends BaseController
 
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {
-
+            if (ObjectUtil.isNotEmpty(fsCourseRedPacketLogListPVO.getPeriodId())){
+                FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodService.selectFsUserCoursePeriodById(fsCourseRedPacketLogListPVO.getPeriodId().longValue());
+                fsCourseRedPacketLogListPVO.setPeriodName(fsUserCoursePeriod.getPeriodName());
+            }
             fsCourseRedPacketLogListPVO.setPhone(PhoneUtil.decryptAutoPhoneMk(fsCourseRedPacketLogListPVO.getPhone()));
         }
 

+ 26 - 0
fs-admin/src/main/java/com/fs/course/controller/FsCourseWatchLogController.java

@@ -134,6 +134,32 @@ public class FsCourseWatchLogController extends BaseController
         return getDataTable(list);
     }
 
+    /**
+     * 会员看课统计导出
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:statisticsExport')")
+    @Log(title = "会员看课统计导出", businessType = BusinessType.EXPORT)
+    @PostMapping("/statisticsExport")
+    public AjaxResult statisticsExport(@RequestBody FsCourseWatchLogStatisticsListParam param)
+    {
+
+        // 如果看指定用户就不用设置公司
+        if(param.getCompanyId() == null){
+            if(param.getUserId() == null) {
+                throw new CustomException("查看公司或者用户必填!");
+            }
+        }
+        if (param.getSTime()==null||param.getETime()==null){
+            throw new CustomException("必须选择开始时间和结束时间!");
+        }
+
+        List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVONew(param);
+
+        ExcelUtil<FsCourseWatchLogStatisticsListVO> util = new ExcelUtil<FsCourseWatchLogStatisticsListVO>(FsCourseWatchLogStatisticsListVO.class);
+        return util.exportExcel(list, "会员看课统计");
+    }
+
+
     /**
      * 导出短链课程看课记录列表
      */

+ 41 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java

@@ -1,5 +1,6 @@
 package com.fs.course.controller;
 
+import com.fs.baidu.domain.BdAccount;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -15,6 +16,7 @@ import com.fs.course.service.IFsUserCoursePeriodDaysService;
 import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.service.IFsUserCourseVideoRedPackageService;
 import com.fs.course.vo.*;
+import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
 import com.fs.his.vo.OptionsVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
@@ -27,6 +29,7 @@ import org.springframework.web.bind.annotation.*;
 
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -231,6 +234,44 @@ public class FsUserCoursePeriodController extends BaseController {
         return R.ok(result);
     }
 
+    /**
+     * @Description: 会员营期导出课程统计
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/12/12 11:37
+     */
+    @PostMapping("/exportInfo")
+    @ApiOperation("营期统计")
+    public AjaxResult exportInfo(@RequestBody PeriodCountParam param) {
+        List<FsPeriodCountExportVO> exportList = new ArrayList<>();
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<FsPeriodCountVO> list = fsUserCoursePeriodDaysService.periodCourseCount(param);
+        list.forEach(item -> {
+            FsPeriodCountExportVO exportVO = new FsPeriodCountExportVO();
+            exportVO.setTitle(item.getTitle());
+            exportVO.setDayDate(item.getDayDate());
+            if(item.getCountDetailsVO() != null){
+                FsCourseAnalysisCountVO countDetailsVO =item.getCountDetailsVO();
+                exportVO.setCourseWatchTimes(countDetailsVO.getCourseWatchTimes());
+                exportVO.setCourseCompleteTimes(countDetailsVO.getCourseCompleteTimes());
+                exportVO.setCourseWatchNum(countDetailsVO.getCourseWatchNum());
+                exportVO.setCourseCompleteNum(countDetailsVO.getCourseCompleteNum());
+                exportVO.setCompleteRate(countDetailsVO.getCompleteRate()+"%");
+                exportVO.setAnswerTimes(countDetailsVO.getAnswerTimes());
+                exportVO.setAnswerNum(countDetailsVO.getAnswerNum());
+                exportVO.setAnswerRightNum(countDetailsVO.getAnswerRightNum());
+                exportVO.setAnswerRightRate(countDetailsVO.getAnswerRightRate()+"%");
+                exportVO.setRedPacketNum(countDetailsVO.getRedPacketNum());
+                exportVO.setRedPacketAmount(countDetailsVO.getRedPacketAmount());
+            }
+            exportList.add(exportVO);
+        });
+
+        ExcelUtil<FsPeriodCountExportVO> util = new ExcelUtil<FsPeriodCountExportVO>(FsPeriodCountExportVO.class);
+        return util.exportExcel(exportList, "百度账号数据");
+    }
+
     @GetMapping("/getPeriodListLikeName")
     public R getPeriodListLikeName(@RequestParam(required = false) String name,
                                    @RequestParam(required = false) Long campId,

+ 27 - 0
fs-admin/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java

@@ -89,6 +89,33 @@ public class QwFsCourseWatchLogController extends BaseController
         return getDataTable(list);
     }
 
+    /**
+     * 会员看课统计导出
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:statisticsExport')")
+    @Log(title = "企微看课统计导出", businessType = BusinessType.EXPORT)
+    @PostMapping("/statisticsExport")
+    public AjaxResult statisticsExport(@RequestBody FsCourseWatchLogStatisticsListParam param)
+    {
+
+        if (param.getSTime()==null||param.getETime()==null){
+            throw new CustomException("必须选择开始时间和结束时间!");
+        }
+
+        param.setSendType(2); //企微
+        List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVO(param);
+        if("济南联志健康".equals(signProjectName)){
+            FsCourseWatchLogStatisticsListVO totalData = fsCourseWatchLogService.getTotalDataAddItem(param);
+            list.add(totalData);
+        }
+
+        ExcelUtil<FsCourseWatchLogStatisticsListVO> util = new ExcelUtil<FsCourseWatchLogStatisticsListVO>(FsCourseWatchLogStatisticsListVO.class);
+
+        return util.exportExcel(list, "企微看课统计");
+    }
+
+
+
     @GetMapping("/getSignProjectName")
     public R getSignProjectName(){
         return R.ok().put("signProjectName", signProjectName);

+ 1 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java

@@ -240,6 +240,7 @@ public class FsStorePaymentScrmController extends BaseController
                 request.setOrdAmt(payment.getPayMoney().toString());
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setReqSeqId("refund-"+payment.getPayCode());
+                request.setAppId(payment.getAppId());
                 Map<String, Object> extendInfoMap = new HashMap<>();
                 extendInfoMap.put("org_party_order_id", payment.getBankSerialNo());
                 request.setExtendInfo(extendInfoMap);

+ 55 - 0
fs-admin/src/main/java/com/fs/live/controller/OrderController.java

@@ -0,0 +1,55 @@
+package com.fs.live.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ParseUtils;
+import com.fs.his.utils.PhoneUtil;
+import com.fs.hisStore.service.IMergedOrderService;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+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.RestController;
+
+import java.util.List;
+
+/**
+ * 合并订单Controller
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Api("合并订单管理")
+@RestController
+@RequestMapping("/order")
+public class OrderController extends BaseController
+{
+    @Autowired
+    private IMergedOrderService mergedOrderService;
+
+    /**
+     * 查询合并订单列表
+     */
+    @ApiOperation("查询合并订单列表")
+    @GetMapping("/list")
+    public TableDataInfo list(MergedOrderQueryParam param)
+    {
+        startPage();
+        List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
+        for (MergedOrderVO vo : list) {
+            vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
+            vo.setPhone(ParseUtils.parsePhone(vo.getPhone()));
+            vo.setSalesPhone(ParseUtils.parsePhone(vo.getSalesPhone()));
+            vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
+        }
+        return getDataTable(list);
+    }
+}

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

@@ -22,6 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -99,7 +100,12 @@ public class LiveDataController extends BaseController
     @GetMapping("/exportLiveUserDetail")
     public AjaxResult exportLiveUserDetail(@RequestParam Long liveId, HttpServletRequest request) {
         CompanyUser user = tokenService.getLoginUser(request).getUser();
-        List<LiveUserDetailExportVO> list = liveDataService.exportLiveUserDetail(liveId,user.getCompanyId(),user.getUserId());
+        List<LiveUserDetailExportVO> list = new ArrayList<>();
+        if ("00".equals(user.getUserType())) {
+            liveDataService.getLiveUserDetailListBySql(liveId, user.getCompanyId(), null);
+        } else {
+            list = liveDataService.exportLiveUserDetail(liveId,user.getCompanyId(),user.getUserId());
+        }
         if (list == null || list.isEmpty()) {
             return AjaxResult.error("未找到用户详情数据");
         }

+ 6 - 2
fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java

@@ -265,10 +265,10 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "       and  send_type= #{sendType} " +
             "</if>\n" +
             "<if test= 'sTime != null '> " +
-            "       and DATE(o.create_time) &gt;= DATE(#{sTime})\n" +
+            "       and o.create_time &gt;= #{sTime}\n" +
             "</if>\n" +
             "<if test='eTime != null '> " +
-            "      and DATE(o.create_time) &lt;= DATE(#{eTime})\n" +
+            "      and o.create_time &lt; DATE_ADD(#{eTime}, INTERVAL 1 DAY)\n" +
             "</if>" +
             "<if test ='sendType != 1 and nickName !=null and nickName!=\"\"'>\n" +
             "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
@@ -295,6 +295,10 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "</script>"})
     List<FsCourseWatchLogStatisticsListVO> selectFsCourseWatchLogStatisticsListVO(FsCourseWatchLogStatisticsListParam param);
 
+    List<FsCourseWatchLogStatisticsListVO> selectFsCourseWatchLogStatisticsListVO_COUNT(FsCourseWatchLogStatisticsListParam param);
+
+
+
     @Select({"<script> " +
             " \tselect \n" +
             "\t'总合计' as qw_user_name,\n" +

+ 1 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseProductOrderServiceImpl.java

@@ -705,6 +705,7 @@ public class FsCourseProductOrderServiceImpl extends ServiceImpl<FsCourseProduct
                     request.setOrdAmt(payment.getPayMoney().toString());
                     request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                     request.setReqSeqId("refund-"+payment.getPayCode());
+                    request.setAppId(payment.getAppId());
                     Map<String, Object> extendInfoMap = new HashMap<>();
                     extendInfoMap.put("org_req_seq_id", "product-"+payment.getPayCode());
                     request.setExtendInfo(extendInfoMap);

+ 4 - 2
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -288,8 +288,10 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
             fsUserCourseVideo.setListingStartTime(null);
             fsUserCourseVideo.setListingEndTime(null);
         }
-        String videoRedisKey = "h5user:video:duration:" + fsUserCourseVideo.getVideoId();
-        redisCache.setCacheObject(videoRedisKey, fsUserCourseVideo.getDuration());
+        String videoRedisKey1 = "h5user:video:duration:" + fsUserCourseVideo.getVideoId();
+        redisCache.setCacheObject(videoRedisKey1, fsUserCourseVideo.getDuration());
+        String videoRedisKey2 = "h5wxuser:video:duration:" + fsUserCourseVideo.getVideoId();
+        redisCache.setCacheObject(videoRedisKey2, fsUserCourseVideo.getDuration());
         return fsUserCourseVideoMapper.updateFsUserCourseVideo(fsUserCourseVideo);
     }
 

+ 8 - 0
fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogStatisticsListVO.java

@@ -11,27 +11,35 @@ public class FsCourseWatchLogStatisticsListVO {
 
 
     private Integer project;
+
     @Excel(name = "项目名称")
     private String projectName;
 
     private Long courseId;
+
     @Excel(name = "课程名称")
     private String courseName;
 
     private Long videoId;
+
     @Excel(name = "小节名称")
     private String videoName;
 
     @Excel(name = "看课中")
     private String type1;
+
     @Excel(name = "已完课")
     private String type2;
+
     @Excel(name = "待看课")
     private String type3;
+
     @Excel(name = "看课中断")
     private String type4;
+
     @Excel(name = "上线数")
     private Long onLineNum;
+
     @Excel(name = "企业微信员工名称")
     private String qwUserName;
 

+ 54 - 0
fs-service/src/main/java/com/fs/course/vo/FsPeriodCountExportVO.java

@@ -0,0 +1,54 @@
+package com.fs.course.vo;
+
+import com.fs.common.annotation.Excel;
+import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Data
+@ApiModel
+public class FsPeriodCountExportVO {
+    @Excel(name = "课程名称")
+    private String title;
+
+    @Excel(name = "营期日期")
+    private LocalDate dayDate;
+
+    @Excel(name = "观看次数")
+    private int courseWatchTimes;
+
+    @Excel(name = "完播次数")
+    private int courseCompleteTimes;
+
+    @Excel(name = "观看人数")
+    private int courseWatchNum;
+
+    @Excel(name = "完播人数")
+    private int courseCompleteNum;
+
+    @Excel(name = "完播率")
+    private String completeRate;
+
+    @Excel(name = "答题次数")
+    private int answerTimes;
+
+    @Excel(name = "答题人数")
+    private int answerNum;
+
+    @Excel(name = "正确人数")
+    private int answerRightNum;
+
+    @Excel(name = "正确率")
+    private String answerRightRate;
+
+    @Excel(name = "答题红包个数")
+    private int redPacketNum;
+
+    @Excel(name = "答题红包金额(元)")
+    private BigDecimal redPacketAmount;
+
+}

+ 2 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsInquiryOrderServiceImpl.java

@@ -474,6 +474,7 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
                         request.setOrdAmt(payment.getPayMoney().toString());
                         request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                         request.setReqSeqId("refund-"+payment.getPayCode());
+                        request.setAppId(payment.getAppId());
                         Map<String, Object> extendInfoMap = new HashMap<>();
                         extendInfoMap.put("org_req_seq_id", "inquiry-"+payment.getPayCode());
                         request.setExtendInfo(extendInfoMap);
@@ -1621,6 +1622,7 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
                     request.setOrdAmt(payment.getPayMoney().toString());
                     request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                     request.setReqSeqId("refund-"+payment.getPayCode());
+                    request.setAppId(payment.getAppId());
                     Map<String, Object> extendInfoMap = new HashMap<>();
                     extendInfoMap.put("org_req_seq_id", "inquiry-"+payment.getPayCode());
                     request.setExtendInfo(extendInfoMap);

+ 8 - 2
fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderServiceImpl.java

@@ -380,15 +380,21 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService
 
         if(fsIntegralOrderMapper.insertFsIntegralOrder(order)>0){
             if (order.getPayType() != 2) {
+                // 验证积分是否足够,防止出现负数
+                long remainingIntegral = user.getIntegral() - totalIntegral;
+                if (remainingIntegral < 0) {
+                    throw new CustomException("积分不足,无法创建订单");
+                }
+                
                 //写入日志
                 FsUser userMap=new FsUser();
                 userMap.setUserId(user.getUserId());
-                userMap.setIntegral(user.getIntegral()-totalIntegral);
+                userMap.setIntegral(remainingIntegral);
                 fsUserMapper.updateFsUser(userMap);
                 FsUserIntegralLogs logs = new FsUserIntegralLogs();
                 logs.setIntegral(-totalIntegral);
                 logs.setUserId(order.getUserId());
-                logs.setBalance(userMap.getIntegral());
+                logs.setBalance(remainingIntegral);
                 logs.setLogType(5);
                 logs.setBusinessId(order.getOrderId().toString());
                 logs.setCreateTime(new Date());

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

@@ -574,6 +574,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setReqSeqId("refund-" + payment.getPayCode());
                 Map<String, Object> extendInfoMap = new HashMap<>();
+                request.setAppId(payment.getAppId());
                 extendInfoMap.put("org_req_seq_id", orderType + "-" + payment.getPayCode());
 
                 //处理分账退款

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

@@ -534,6 +534,7 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
                 request.setReqSeqId("refund-"+fsStorePayment.getPayCode());
                 Map<String, Object> extendInfoMap = new HashMap<>();
                 extendInfoMap.put("org_req_seq_id", orderType+"-"+fsStorePayment.getPayCode());
+                request.setAppId(fsStorePayment.getAppId());
                 request.setExtendInfo(extendInfoMap);
                 //处理分账退款
                 //1.判断是否是全额退款

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

@@ -951,6 +951,7 @@ public class FsUserInformationCollectionServiceImpl extends ServiceImpl<FsUserIn
         Map<String, Object> extendInfoMap = new HashMap<>();
         extendInfoMap.put("org_req_seq_id", orderType + "-" + payment.getPayCode());
         request.setExtendInfo(extendInfoMap);
+        request.setAppId(payment.getAppId());
         HuiFuRefundResult refund = huiFuService.refund(request);
         logger.info("订单退款返回结果:退款订单id:" + orderId + refund);
         if ((refund.getResp_code().equals("00000000") || refund.getResp_code().equals("00000100")) && (refund.getTrans_stat().equals("S") || refund.getTrans_stat().equals("P"))) {

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

@@ -2,6 +2,8 @@ package com.fs.hisStore.mapper;
 
 import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
 import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
@@ -15,6 +17,14 @@ import java.util.List;
  */
 public interface MergedOrderMapper
 {
+    /**
+     * 查询合并的订单列表(销售订单+商城订单+直播订单)
+     *
+     * @param param 查询参数
+     * @return 合并后的订单列表
+     */
+    List<MergedOrderVO> selectMergedOrderList(@Param("maps") MergedOrderQueryParam param);
+
     /**
      * 查询合并的订单列表(商城订单+直播订单)
      *

+ 4 - 0
fs-service/src/main/java/com/fs/hisStore/service/IMergedOrderService.java

@@ -2,6 +2,8 @@ package com.fs.hisStore.service;
 
 import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
 import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
 
 import java.util.List;
 
@@ -20,5 +22,7 @@ public interface IMergedOrderService
      * @return 合并后的订单列表
      */
     List<FsMergedOrderListQueryVO> selectMergedOrderListVO(FsMyStoreOrderQueryParam param);
+
+    List<MergedOrderVO> selectMergedOrderList(MergedOrderQueryParam param);
 }
 

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

@@ -868,6 +868,7 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
                         Map<String, Object> extendInfoMap = new HashMap<>();
                         extendInfoMap.put("org_party_order_id", payment.getBankSerialNo());
                         request.setExtendInfo(extendInfoMap);
+                        request.setAppId(payment.getAppId());
                         HuiFuRefundResult refund = huiFuService.refund(request);
                         logger.info("退款:"+refund);
                         if((refund.getResp_code().equals("00000000")||refund.getResp_code().equals("00000100"))&&(refund.getTrans_stat().equals("S")||refund.getTrans_stat().equals("P"))){
@@ -1598,6 +1599,7 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
                 Map<String, Object> extendInfoMap = new HashMap<>();
                 extendInfoMap.put("org_req_seq_id", orderType + "-" + payment.getPayCode());
                 request.setExtendInfo(extendInfoMap);
+                request.setAppId(payment.getAppId());
                 HuiFuRefundResult refund = huiFuService.refund(request);
                 logger.info("订单退款返回结果:退款订单id:" + order.getOrderId() + refund);
                 if ((refund.getResp_code().equals("00000000") || refund.getResp_code().equals("00000100")) && (refund.getTrans_stat().equals("S") || refund.getTrans_stat().equals("P"))) {

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

@@ -2525,6 +2525,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                         Map<String, Object> extendInfoMap = new HashMap<>();
                         extendInfoMap.put("org_party_order_id", payment.getBankSerialNo());
                         request.setExtendInfo(extendInfoMap);
+                        request.setAppId(payment.getAppId());
                         HuiFuRefundResult refund = huiFuService.refund(request);
                         logger.info("退款:" + refund);
                         if ((refund.getResp_code().equals("00000000") || refund.getResp_code().equals("00000100")) && (refund.getTrans_stat().equals("S") || refund.getTrans_stat().equals("P"))) {

+ 26 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java

@@ -1514,12 +1514,38 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
         FsStoreProductScrm fsStoreProductScrm = fsStoreProductMapper.selectFsStoreProductById(productId);
         if (fsStoreProductScrm == null) return R.error("商品不存在");
 
+        // 查询原商品的规格属性与属性值
+        List<FsStoreProductAttrScrm> attrList = fsStoreProductAttrMapper.selectFsStoreProductAttrByProductId(productId);
+        List<FsStoreProductAttrValueScrm> attrValueList = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueByProductId(productId);
+
         FsStoreProductScrm copy = new FsStoreProductScrm();
         BeanUtils.copyProperties(fsStoreProductScrm, copy);
         copy.setProductId(null);
         copy.setIsAudit("0");
         fsStoreProductMapper.insertFsStoreProduct(copy);
 
+        // 复制规格属性
+        if (attrList != null && !attrList.isEmpty()) {
+            for (FsStoreProductAttrScrm attr : attrList) {
+                FsStoreProductAttrScrm newAttr = new FsStoreProductAttrScrm();
+                BeanUtils.copyProperties(attr, newAttr);
+                newAttr.setId(null);
+                newAttr.setProductId(copy.getProductId());
+                fsStoreProductAttrMapper.insertFsStoreProductAttr(newAttr);
+            }
+        }
+
+        // 复制属性值
+        if (attrValueList != null && !attrValueList.isEmpty()) {
+            for (FsStoreProductAttrValueScrm val : attrValueList) {
+                FsStoreProductAttrValueScrm newVal = new FsStoreProductAttrValueScrm();
+                BeanUtils.copyProperties(val, newVal);
+                newVal.setId(null);
+                newVal.setProductId(copy.getProductId());
+                fsStoreProductAttrValueMapper.insertFsStoreProductAttrValue(newVal);
+            }
+        }
+
         return R.ok();
 
     }

+ 6 - 1
fs-service/src/main/java/com/fs/hisStore/service/impl/FsUserAddressScrmServiceImpl.java

@@ -68,7 +68,12 @@ public class FsUserAddressScrmServiceImpl implements IFsUserAddressScrmService
     {
         fsUserAddress.setDetail(fsUserAddress.getDetail().trim());
         fsUserAddress.setCreateTime(DateUtils.getNowDate());
-        return fsUserAddressMapper.insertFsUserAddress(fsUserAddress);
+        int result = fsUserAddressMapper.insertFsUserAddress(fsUserAddress);
+        // 将自增id赋值给addressId字段
+        if (result > 0 && fsUserAddress.getId() != null) {
+            fsUserAddress.setAddressId(fsUserAddress.getId());
+        }
+        return result;
     }
 
     /**

+ 55 - 11
fs-service/src/main/java/com/fs/hisStore/service/impl/MergedOrderServiceImpl.java

@@ -9,15 +9,14 @@ import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
 import com.fs.hisStore.service.IMergedOrderService;
 import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
 import com.fs.hisStore.vo.FsStoreOrderItemVO;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
 import com.fs.store.config.StoreConfig;
 import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.List;
+import java.util.*;
 
 /**
  * 合并订单Service实现类
@@ -31,27 +30,72 @@ public class MergedOrderServiceImpl implements IMergedOrderService
     @Autowired
     private MergedOrderMapper mergedOrderMapper;
 
+    /*
+     * 后端合并
+     * */
+    @Override
+    public List<MergedOrderVO> selectMergedOrderList(MergedOrderQueryParam param)
+    {
+        List<MergedOrderVO> list = mergedOrderMapper.selectMergedOrderList(param);
+
+        // 处理商品JSON
+        for (MergedOrderVO vo : list)
+        {
+            if (StringUtils.isNotEmpty(vo.getItemJson()))
+            {
+                try
+                {
+                    JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
+                    if (jsonArray != null && jsonArray.size() > 0)
+                    {
+                        vo.setItems(jsonArray);
+                    }
+                }
+                catch (Exception e)
+                {
+                    // JSON解析失败,忽略
+                }
+            }
+        }
+
+        return list;
+    }
+
+
     @Autowired
     private ISysConfigService configService;
 
+    /*
+     * 小程序合并
+     * */
     @Override
     public List<FsMergedOrderListQueryVO> selectMergedOrderListVO(FsMyStoreOrderQueryParam param)
     {
         List<FsMergedOrderListQueryVO> list = mergedOrderMapper.selectMergedOrderListVO(param);
-        
+
         for (FsMergedOrderListQueryVO vo : list)
         {
+
             // 处理商品JSON
             if (StringUtils.isNotEmpty(vo.getItemJson()))
             {
-                JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
-                List<FsStoreOrderItemVO> items = JSONUtil.toList(jsonArray, FsStoreOrderItemVO.class);
-                if (items != null && items.size() > 0)
-                {
+                List<FsStoreOrderItemVO> items = new ArrayList<>();
+                if (2 == vo.getOrderType()) {
+                    FsStoreOrderItemVO bean = JSONUtil.toBean(vo.getItemJson(), FsStoreOrderItemVO.class);
+                    items.add(bean);
                     vo.setItems(items);
+                }else {
+                    JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
+                    items = JSONUtil.toList(jsonArray, FsStoreOrderItemVO.class);
+                    if (items != null && items.size() > 0)
+                    {
+                        vo.setItems(items);
+                    }
                 }
+
+
             }
-            
+
             // 处理是否可以申请售后
             vo.setIsAfterSales(0);
             if (vo.getStatus() != null && vo.getStatus().equals(OrderInfoEnum.STATUS_3.getValue()))
@@ -83,7 +127,7 @@ public class MergedOrderServiceImpl implements IMergedOrderService
                 vo.setIsAfterSales(1);
             }
         }
-        
+
         return list;
     }
 }

+ 15 - 0
fs-service/src/main/java/com/fs/huifuPay/sdk/opps/core/request/V2TradePaymentScanpayRefundRequest.java

@@ -58,6 +58,12 @@ public class V2TradePaymentScanpayRefundRequest extends BaseRequest {
     private String appId;
 
 
+    /**
+     *小程序
+     * **/
+    private String appId;
+
+
     @Override
     public FunctionCodeEnum getFunctionCode() {
         return FunctionCodeEnum.V2_TRADE_PAYMENT_SCANPAY_REFUND;
@@ -138,4 +144,13 @@ public class V2TradePaymentScanpayRefundRequest extends BaseRequest {
     public void setAcctSplitBunch(String acctSplitBunch) {
         this.acctSplitBunch = acctSplitBunch;
     }
+
+
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
 }

+ 37 - 0
fs-service/src/main/java/com/fs/live/enums/LiveGoodsAddErrorEnum.java

@@ -0,0 +1,37 @@
+package com.fs.live.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 直播商品添加失败原因枚举
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Getter
+@AllArgsConstructor
+public enum LiveGoodsAddErrorEnum {
+
+    /**
+     * 未上架
+     */
+    NOT_SHELVED("未上架"),
+
+    /**
+     * 未审核
+     */
+    NOT_AUDITED("未审核"),
+
+    /**
+     * 已删除
+     */
+    DELETED("已删除");
+
+    /**
+     * 错误描述
+     */
+    private String desc;
+
+}
+

+ 85 - 0
fs-service/src/main/java/com/fs/live/param/MergedOrderQueryParam.java

@@ -0,0 +1,85 @@
+package com.fs.live.param;
+
+import com.fs.common.param.BaseQueryParam;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 合并订单查询参数
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedOrderQueryParam extends BaseQueryParam implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 订单状态 */
+    private String status;
+
+    /** 用户ID */
+    private Long userId;
+
+    /** 公司ID */
+    private Long companyId;
+
+    /** 部门ID */
+    private Long deptId;
+
+    /** 订单号 */
+    private String orderCode;
+
+    /** 订单号列表(多个订单号用逗号分隔) */
+    private String orderCodeList;
+
+    /** 运单号 */
+    private String deliveryId;
+
+    /** 银行交易流水号 */
+    private String bankTransactionId;
+
+    /** 手机号 */
+    private String userPhone;
+
+    /** 收件人 */
+    private String realName;
+
+    /** 产品名称 */
+    private String productName;
+
+    /** 物流状态 */
+    private Integer deliveryStatus;
+
+    /** 物流结算状态 */
+    private Integer deliveryPayStatus;
+
+    /** 支付方式 */
+    private String payType;
+
+    /** 下单时间范围 */
+    private String createTimeRange;
+
+    /** 支付时间范围 */
+    private String payTimeRange;
+
+    /** 发货时间范围 */
+    private String deliverySendTimeRange;
+
+    /** 回单时间范围 */
+    private String deliveryImportTimeRange;
+
+    /** 小程序AppId */
+    private String appId;
+
+    /** 订单类型筛选:1-销售订单,2-商城订单,3-直播订单,null-全部 */
+    private Integer orderTypeFilter;
+
+    /** 销售名称(company_user表的user_name) */
+    private String salesName;
+
+    /** 员工姓名(company_user表的nick_name) */
+    private String companyUserNickName;
+}
+

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

@@ -1096,6 +1096,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
                         Map<String, Object> extendInfoMap = new HashMap<>();
                         extendInfoMap.put("org_party_order_id", payment.getBankSerialNo());
                         request.setExtendInfo(extendInfoMap);
+                        request.setAppId(payment.getAppId());
                         HuiFuRefundResult refund = huiFuService.refund(request);
                         log.info("退款:" + refund);
                         if (refund != null && ("00000000".equals(refund.getResp_code()) || "00000100".equals(refund.getResp_code()))

+ 96 - 2
fs-service/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java

@@ -3,6 +3,7 @@ package com.fs.live.service.impl;
 
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysUser;
+import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.DateUtils;
 import com.fs.company.domain.CompanyUser;
 import com.fs.his.domain.FsStoreProduct;
@@ -10,6 +11,7 @@ import com.fs.his.mapper.FsStoreProductMapper;
 import com.fs.hisStore.domain.FsStoreProductScrm;
 import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
 import com.fs.live.domain.LiveGoods;
+import com.fs.live.enums.LiveGoodsAddErrorEnum;
 import com.fs.live.mapper.LiveGoodsMapper;
 import com.fs.live.service.ILiveAutoTaskService;
 import com.fs.live.service.ILiveGoodsService;
@@ -91,8 +93,8 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
         liveGoods.setUpdateTime(DateUtils.getNowDate());
         LiveGoods existGoods = baseMapper.selectLiveGoodsByGoodsId(liveGoods.getGoodsId());
         if (liveGoods.getStock() != null) {
-            if(liveGoods.getProductId() == null) return R.error("店铺已停止售卖商品!");
-            FsStoreProductScrm fsStoreProduct = fsStoreProductMapper.selectFsStoreProductById(liveGoods.getProductId());
+            if(existGoods.getProductId() == null) return R.error("店铺已停止售卖商品!");
+            FsStoreProductScrm fsStoreProduct = fsStoreProductMapper.selectFsStoreProductById(existGoods.getProductId());
             if(fsStoreProduct == null) return R.error("商品不存在");
             if(fsStoreProduct.getIsShow() == 0 || existGoods.getStatus() == 0) return R.error("商品已下架");
             if(fsStoreProduct.getStock() < liveGoods.getStock()) return R.error("商品库存不足");
@@ -223,6 +225,52 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
         //  查询商品信息列表(假设返回 List<StoreProduct>)
         List<FsStoreProductScrm> productInfoList = fsStoreProductMapper.selectFsStoreProductByProductIds(productIdsLong);
 
+        // 检查商品状态:未上架、未审核、已删除
+        StringBuilder errorMsg = new StringBuilder();
+        List<String> notShelvedProducts = new ArrayList<>();
+        List<String> notAuditedProducts = new ArrayList<>();
+        List<String> deletedProducts = new ArrayList<>();
+
+        for (FsStoreProductScrm product : productInfoList) {
+            // 检查是否未上架
+            if (product.getIsShow() == null || product.getIsShow() == 0) {
+                notShelvedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否未审核
+            if (product.getIsAudit() == null || !"1".equals(product.getIsAudit())) {
+                notAuditedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否已删除
+            if (product.getIsDel() != null && product.getIsDel() == 1) {
+                deletedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+        }
+
+        // 构建错误信息
+        if (!notShelvedProducts.isEmpty()) {
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_SHELVED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notShelvedProducts));
+        }
+        if (!notAuditedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_AUDITED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notAuditedProducts));
+        }
+        if (!deletedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.DELETED.getDesc()).append(":");
+            errorMsg.append(String.join("、", deletedProducts));
+        }
+
+        // 如果有错误,返回错误信息
+        if (errorMsg.length() > 0) {
+            return R.error("添加失败原因:" + errorMsg.toString());
+        }
+
         //  转换为 LiveGoods 并批量插入
         List<LiveGoods> liveGoodsList = productInfoList.stream()
                 .map(product -> {
@@ -265,6 +313,52 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
         //  查询商品信息列表(假设返回 List<StoreProduct>)
         List<FsStoreProductScrm> productInfoList = fsStoreProductMapper.selectFsStoreProductByProductIds(productIdsLong);
 
+        // 检查商品状态:未上架、未审核、已删除
+        StringBuilder errorMsg = new StringBuilder();
+        List<String> notShelvedProducts = new ArrayList<>();
+        List<String> notAuditedProducts = new ArrayList<>();
+        List<String> deletedProducts = new ArrayList<>();
+
+        for (FsStoreProductScrm product : productInfoList) {
+            // 检查是否未上架
+            if (product.getIsShow() == null || product.getIsShow() == 0) {
+                notShelvedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否未审核
+            if (product.getIsAudit() == null || !"1".equals(product.getIsAudit())) {
+                notAuditedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否已删除
+            if (product.getIsDel() != null && product.getIsDel() == 1) {
+                deletedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+        }
+
+        // 构建错误信息
+        if (!notShelvedProducts.isEmpty()) {
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_SHELVED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notShelvedProducts));
+        }
+        if (!notAuditedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_AUDITED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notAuditedProducts));
+        }
+        if (!deletedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.DELETED.getDesc()).append(":");
+            errorMsg.append(String.join("、", deletedProducts));
+        }
+
+        // 如果有错误,抛出异常(因为返回类型是int,不能返回R.error)
+        if (errorMsg.length() > 0) {
+            throw new ServiceException("添加失败原因:" + errorMsg.toString());
+        }
+
         //  转换为 LiveGoods 并批量插入
         List<LiveGoods> liveGoodsList = productInfoList.stream()
                 .map(product -> {

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

@@ -1308,6 +1308,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                         Map<String, Object> extendInfoMap = new HashMap<>();
                         extendInfoMap.put("org_party_order_id", payment.getBankSerialNo());
                         request.setExtendInfo(extendInfoMap);
+                        request.setAppId(payment.getAppId());
                         HuiFuRefundResult refund = huiFuService.refund(request);
                         log.info("退款:" + refund);
                         if (refund != null && ("00000000".equals(refund.getResp_code()) || "00000100".equals(refund.getResp_code()))
@@ -1504,6 +1505,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                         Map<String, Object> extendInfoMap = new HashMap<>();
                         extendInfoMap.put("org_party_order_id", payment.getBankSerialNo());
                         request.setExtendInfo(extendInfoMap);
+                        request.setAppId(payment.getAppId());
                         HuiFuRefundResult refund = huiFuService.refund(request);
                         log.info("退款:" + refund);
                         if (refund != null && ("00000000".equals(refund.getResp_code()) || "00000100".equals(refund.getResp_code()))
@@ -1879,8 +1881,12 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         }
         BigDecimal payPrice = BigDecimal.ZERO;
         BigDecimal payDelivery = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if (param.getCityId() != null) {
             payDelivery = handleDeliveryMoney(param.getCityId(), fsStoreProduct, param.getTotalNum());
+            if (payDelivery.compareTo(badCode) == 0) {
+                throw new ServiceException("偏远地区暂不可购买");
+            }
             totalPrice = totalPrice.add(payDelivery);
         }
         return LiveOrderComputeDTO.builder().payPrice(payPrice)
@@ -1948,6 +1954,9 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 //        payPrice = payPrice.add(serviceFee);
         // 生成
         BigDecimal deliveryMoney = handleDeliveryMoney(liveOrder);
+        if (deliveryMoney.compareTo(BigDecimal.valueOf(-1)) == 0) {
+            return R.error("偏远地区暂不可购买");
+        }
         totalPrice = totalPrice.add(deliveryMoney);
         liveOrder.setDiscountMoney(totalPrice);
 
@@ -2028,8 +2037,12 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         totalPrice = totalPrice.add(payPrice);
         BigDecimal payDelivery = BigDecimal.ZERO;
         BigDecimal deductionPrice = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if (param.getCityId() != null) {
             payDelivery = handleDeliveryMoney(param.getCityId(), fsStoreProduct, param.getTotalNum());
+            if (payDelivery.compareTo(badCode) == 0) {
+                throw new ServiceException("偏远地区暂不可购买");
+            }
             payPrice = payPrice.add(payDelivery);
         }
 
@@ -2472,6 +2485,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
     private BigDecimal handleDeliveryMoney(Long cityId, FsStoreProductScrm fsStoreProduct, String totalNumSize) {
         BigDecimal storePostage = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if (ObjectUtil.isNull(fsStoreProduct.getTempId())) {
             return storePostage;
         }
@@ -2487,6 +2501,11 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         String cityIds = String.join(",", citys.stream()
                 .map(String::valueOf).collect(Collectors.toList()));
         List<FsShippingTemplatesRegionScrm> shippingTemplatesRegionList = shippingTemplatesRegionService.selectFsShippingTemplatesRegionListByTempIdsAndCityIds(ids,cityIds);
+        // 有运费模板,但当前城市没有匹配的区域
+        if (shippingTemplatesList != null && !shippingTemplatesList.isEmpty()
+                && (shippingTemplatesRegionList == null || shippingTemplatesRegionList.isEmpty())) {
+            return badCode;
+        }
         Map<Long, Integer> shippingTemplatesMap = shippingTemplatesList
                 .stream()
                 .collect(Collectors.toMap(FsShippingTemplatesScrm::getId,
@@ -2521,7 +2540,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         FsShippingTemplatesRegionScrm shippingTemplatesRegion = shippingTemplatesRegionMap.get(tempId);
         if (shippingTemplatesRegion == null) {
             log.error("没有找到运费模板");
-            return storePostage;
+            return badCode;
         }
         BigDecimal price = NumberUtil.round(NumberUtil.mul(totalNum, fsStoreProduct.getPrice()), 2);
 
@@ -3546,6 +3565,9 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 //        payPrice = payPrice.add(serviceFee);
         // 生成
         BigDecimal deliveryMoney = handleDeliveryMoney(liveOrder);
+        if (deliveryMoney.compareTo(BigDecimal.valueOf(-1)) == 0) {
+            return R.error("偏远地区暂不可购买");
+        }
         payPrice = payPrice.add(deliveryMoney);
         liveOrder.setDiscountMoney(BigDecimal.ZERO);
 
@@ -3625,6 +3647,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
     private BigDecimal handleDeliveryMoney(LiveOrder liveOrder) {
         BigDecimal storePostage = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if(liveOrder.getUserAddress() == null || liveOrder.getCityId() == null) return storePostage;
         List<Long> citys = new ArrayList<>();
         citys.add(liveOrder.getCityId());
@@ -3637,6 +3660,11 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
         //获取运费模板区域列表按照城市排序
         List<FsShippingTemplatesRegionScrm> shippingTemplatesRegionList = shippingTemplatesRegionService.selectFsShippingTemplatesRegionListByTempIdsAndCityIds(ids, StringUtils.join(citys, ","));
+        // 有运费模板但城市未匹配到区域,返回 badCode
+        if (shippingTemplatesList != null && !shippingTemplatesList.isEmpty()
+                && (shippingTemplatesRegionList == null || shippingTemplatesRegionList.isEmpty())) {
+            return badCode;
+        }
 
         //提取运费模板类型
         Map<Long, Integer> shippingTemplatesMap = shippingTemplatesList

+ 195 - 0
fs-service/src/main/java/com/fs/live/vo/MergedOrderVO.java

@@ -0,0 +1,195 @@
+package com.fs.live.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 合并订单VO(销售订单+商城订单+直播订单)
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedOrderVO implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 订单ID */
+    private Long id;
+
+    /** 订单ID(直播订单使用) */
+    private Long orderId;
+
+    /** 直播ID(直播订单使用) */
+    private Long liveId;
+
+    /** 售后ID(直播订单使用) */
+    private Long afterSalesId;
+
+    /** 订单号 */
+    private String orderCode;
+
+    /** 实际支付金额 */
+    private BigDecimal payPrice;
+
+    /** 会员等级 */
+    private Integer userLevel;
+
+    /** 实付金额 */
+    private BigDecimal payMoney;
+
+    /** 优惠金额(直播订单使用) */
+    private Integer discountMoney;
+
+    /** 运费 */
+    private BigDecimal payDelivery;
+
+    /** 成本价 */
+    private BigDecimal cost;
+
+    /** 订单状态 */
+    @Excel(name = "订单状态",dictType="sys_live_order_status")
+    private Integer status;
+
+    /** 订单总价 */
+    private BigDecimal totalPrice;
+
+    /** 是否套餐 */
+    private Integer isPackage;
+
+    /** 套餐JSON */
+    private String packageJson;
+
+    /** 商品JSON */
+    private String itemJson;
+
+    /** 物流单号 */
+    private String deliveryId;
+
+    /** 是否可以申请售后 */
+    private Integer isAfterSales;
+
+    /** 完成时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date finishTime;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    /** 支付时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date payTime;
+
+    /** 发货时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date deliverySendTime;
+
+    /** 总数量(直播订单使用) */
+    private Integer totalNum;
+
+
+
+    /** 订单类型:1-销售订单,2-商城订单,3-直播订单 */
+    private Integer orderType;
+
+    /** 订单类型名称 */
+    private String orderTypeName;
+
+    /** 公司名称 */
+    private String companyName;
+
+    /** 销售名称(company_user表的user_name) */
+    private String salesName;
+
+    /** 销售创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date salesCreateTime;
+
+    /** 销售手机 */
+    private String salesPhone;
+
+    /** 员工昵称(company_user表的nick_name) */
+    private String companyUserNickName;
+
+    /** 用户昵称 */
+    private String nickname;
+
+    /** 用户手机号 */
+    private String phone;
+
+    /** 用户ID */
+    private Long userId;
+
+    /** 用户累计成交避暑 */
+    private Integer userOrderCount;
+
+    /** 用户累计成交避暑 */
+    private BigDecimal userTotalAmount;
+
+    /** 收货人姓名 */
+    private String realName;
+
+    /** 收货人电话 */
+    private String userPhone;
+
+    /** 店铺ID */
+    private Long storeId;
+
+    /** 店铺名字 */
+    private String storeName;
+
+    /** 产品ID */
+    private Long productId;
+
+    /** 产品名字 */
+    private String productName;
+    /** 商品规格 */
+    private String productSpec;
+
+    /** 商品编码 */
+    private String barCode;
+
+    /** 商品分类 */
+    private String cateName;
+
+
+
+    /** 收货地址 */
+    private String userAddress;
+
+    /** 支付方式 */
+    private String payType;
+
+    /** 物流状态 */
+    private Integer deliveryStatus;
+
+    /** 物流结算状态 */
+    private Integer deliveryPayStatus;
+
+
+
+    /** 订单商品列表 */
+    private List<?> items;
+
+    /** 小程序名称 */
+    private String miniProgramName;
+
+    /** ERP账号 */
+    private String erpAccount;
+
+    /** ERP电话 */
+    private String erpPhone;
+
+    /** 银行交易流水号 */
+    private String bankTransactionId;
+
+
+}
+

+ 2 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

@@ -48,6 +48,8 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
 
     public List<QwExternalContact> selectQwExternalContactByIds(@Param("ids") List<Long> ids);
 
+    public List<QwExternalContact> selectQwExternalContactByIdsStatus(@Param("ids") List<Long> ids);
+
     @Select("SELECT id,stage_status,name,fs_user_id from qw_external_contact where id=#{id}")
     public QwExternalContact selectQwExternalContactByIdForStageStatus(@Param("id") Long id);
 

+ 3 - 2
fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java

@@ -1258,10 +1258,11 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
             List<Future<?>> futures = new ArrayList<>();
 
 
-            // 1. 批量查询所有用户数据
+            // 1. 批量查询所有用户数据(流失客户不打了)
             List<QwExternalContact> contacts;
             try {
-                contacts = qwExternalContactMapper.selectQwExternalContactByIds(param.getUserIds());
+//                contacts = qwExternalContactMapper.selectQwExternalContactByIds(param.getUserIds());
+                contacts = qwExternalContactMapper.selectQwExternalContactByIdsStatus(param.getUserIds());
                 if (contacts == null || contacts.isEmpty()) {
                     return R.error("成功:0,失败:" + param.getUserIds().size());
                 }

+ 3 - 0
fs-service/src/main/java/com/fs/sop/domain/QwSopTempRules.java

@@ -68,6 +68,9 @@ public class QwSopTempRules{
     @Excel(name = "排序")
     private Integer sorts;
 
+    @TableField(exist = false)
+    private Long courseSort;
+
     @TableField(exist = false)
     private List<QwSopTempContent> settingList;
     @TableField(exist = false)

+ 19 - 0
fs-service/src/main/java/com/fs/sop/mapper/QwSopTempRulesMapper.java

@@ -70,6 +70,25 @@ public interface QwSopTempRulesMapper extends BaseMapper<QwSopTempRules> {
     @Select("select * from qw_sop_temp_rules where day_num is null ")
     List<QwSopTempRules> rulesNull();
 
+    @DataSource(DataSourceType.SOP)
+    @Select("<script>" +
+            "select tr.* from qw_sop_temp_rules tr " +
+            "left join qw_sop_temp st on tr.temp_id=st.id " +
+            " where tr.course_id = #{courseId}" +
+            " and st.send_type=11 and st.status = 1  " +
+            "</script>")
+    List<QwSopTempRules> listByCourseId(@Param("courseId") Long courseId);
+
+    @DataSource(DataSourceType.SOP)
+    @Select("<script>" +
+            "select tr.* from qw_sop_temp_rules tr " +
+            "left join qw_sop_temp st on tr.temp_id=st.id " +
+            " where tr.course_id = #{courseId}" +
+            " and tr.temp_id=#{tempId} " +
+            " and st.send_type=11 and st.status = 1  " +
+            "</script>")
+    List<QwSopTempRules> listByCourseIdAndTempId(@Param("courseId") Long courseId,@Param("tempId") String tempId);
+
 
 
     void deleteByTempId(@Param("id") String id);

+ 2 - 0
fs-service/src/main/java/com/fs/sop/service/IQwSopTempContentService.java

@@ -92,5 +92,7 @@ public interface IQwSopTempContentService extends IService<QwSopTempContent>{
 
     List<QwSopTempContent> listByTempIds(Collection<String> tempIds);
 
+    List<QwSopTempContent> listByTempId(String tempId);
+
     void removeByWrapper(LambdaQueryWrapper<QwSopTempContent> wrapper);
 }

+ 1 - 0
fs-service/src/main/java/com/fs/sop/service/IQwSopTempDayService.java

@@ -44,6 +44,7 @@ public interface IQwSopTempDayService extends IService<QwSopTempDay> {
     void removeByWrapper(LambdaQueryWrapper<QwSopTempDay> wrapper);
 
     List<QwSopTempDay> listByTempIds(LambdaQueryWrapper<QwSopTempDay> wrapper);
+    List<QwSopTempDay> listByTempById(LambdaQueryWrapper<QwSopTempDay> wrapper);
 
     void updateByWrapper(UpdateWrapper<QwSopTempDay> wrapper);
 }

+ 2 - 0
fs-service/src/main/java/com/fs/sop/service/IQwSopTempRulesService.java

@@ -94,6 +94,8 @@ public interface IQwSopTempRulesService extends IService<QwSopTempRules>{
 
     List<QwSopTempRules> listByCourseId(Long courseId);
 
+    List<QwSopTempRules> listByCourseIdAndTempId(Long courseId,String tempId);
+
     void removeByTempIds(Collection<String> tempIds);
 
     void removeByWrapper(LambdaQueryWrapper<QwSopTempRules> wrapper);

+ 6 - 0
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempContentServiceImpl.java

@@ -165,6 +165,12 @@ public class QwSopTempContentServiceImpl extends ServiceImpl<QwSopTempContentMap
         return baseMapper.selectList(new QueryWrapper<QwSopTempContent>().in("temp_id", tempIds));
     }
 
+    @Override
+    @DataSource(DataSourceType.SOP)
+    public List<QwSopTempContent> listByTempId(String tempId) {
+        return baseMapper.selectList(new QueryWrapper<QwSopTempContent>().eq("temp_id", tempId));
+    }
+
     @Override
     @DataSource(DataSourceType.SOP)
     public void removeByWrapper(LambdaQueryWrapper<QwSopTempContent> wrapper) {

+ 7 - 0
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempDayServiceImpl.java

@@ -109,6 +109,13 @@ public class QwSopTempDayServiceImpl extends ServiceImpl<QwSopTempDayMapper, QwS
         return this.list(wrapper);
     }
 
+    @Override
+    @DataSource(DataSourceType.SOP)
+    public List<QwSopTempDay> listByTempById(LambdaQueryWrapper<QwSopTempDay> wrapper) {
+        return this.list(wrapper);
+    }
+
+
     @Override
     @DataSource(DataSourceType.SOP)
     public void updateByWrapper(UpdateWrapper<QwSopTempDay> wrapper) {

+ 7 - 2
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempRulesServiceImpl.java

@@ -243,9 +243,14 @@ public class QwSopTempRulesServiceImpl extends ServiceImpl<QwSopTempRulesMapper,
     }
 
     @Override
-    @DataSource(DataSourceType.SOP)
     public List<QwSopTempRules> listByCourseId(Long courseId) {
-        return list(new QueryWrapper<QwSopTempRules>().eq("course_id", courseId));
+//        return list(new QueryWrapper<QwSopTempRules>().eq("course_id", courseId));
+        return qwSopTempRulesMapper.listByCourseId(courseId);
+    }
+
+    @Override
+    public List<QwSopTempRules> listByCourseIdAndTempId(Long courseId, String tempId) {
+        return qwSopTempRulesMapper.listByCourseIdAndTempId(courseId,tempId);
     }
 
     @Override

+ 111 - 35
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.fs.common.BeanCopyUtils;
 import com.fs.common.annotation.DataSource;
 import com.fs.common.enums.DataSourceType;
 import com.fs.common.exception.base.BaseException;
@@ -321,6 +322,7 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
         if (day == null) return;
         qwSopTempDayService.removeById(day.getId());
         qwSopTempContentService.removeByDayId(day.getId());
+        qwSopTempRulesService.removeByDayId(day.getId());
         reorder(day.getTempId());
     }
 
@@ -633,6 +635,7 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
         if (CollectionUtils.isEmpty(rulesList)) {
             return;
         }
+
         // 获取这些规则关联的模板ID集合
         Set<String> tempIds = rulesList.stream()
                 .map(QwSopTempRules::getTempId)
@@ -641,11 +644,7 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
 
         FsUserCourse fsUserCourse = fsUserCourseMapper.selectFsUserCourseByCourseId(courseId);
         List<FsUserCourseVideo> videoList = fsUserCourseVideoMapper.selectVideoByCourseId(fsUserCourse.getCourseId());
-        List<QwSopTemp> tempList = qwSopTempMapper.selectListByIds(tempIds);
-        List<QwSopTempContent> contentList = qwSopTempContentService.listByTempIds(tempIds);
-        List<QwSopTempDay> dayList = qwSopTempDayService.listByTempIds(new LambdaQueryWrapper<QwSopTempDay>().in(QwSopTempDay::getTempId, tempIds));
         List<Long> videoIdList = videoList.stream().map(FsUserCourseVideo::getVideoId).collect(Collectors.toList());
-        // videoList转Map key 为videoId value 为 courseSort
         Map<Long, Long> videoSortMap = videoList.stream().collect(Collectors.toMap(FsUserCourseVideo::getVideoId, FsUserCourseVideo::getCourseSort));
 
         // 将课程中已删除的视频在规则和日期中删除
@@ -656,54 +655,131 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
             qwSopTempContentService.removeByWrapper(new LambdaQueryWrapper<QwSopTempContent>().in(QwSopTempContent::getDayId, dayIdList));
         }
 
-        // 需要添加的课程视频
-        List<FsUserCourseVideo> addVideoList = videoList.stream()
-                .filter(e -> rulesList.stream().noneMatch(f -> f.getVideoId().equals(e.getVideoId()))).collect(Collectors.toList());
 
-        if (CollectionUtils.isNotEmpty(addVideoList)) {
-            for (QwSopTemp temp : tempList) {
+        tempIds.forEach(teId->{
+            QwSopTemp sopTemp = qwSopTempMapper.selectQwSopTempById(teId);
+            List<QwSopTempContent> contentList = qwSopTempContentService.listByTempId(teId);
+            List<QwSopTempDay> dayList = qwSopTempDayService.listByTempById(new LambdaQueryWrapper<QwSopTempDay>().in(QwSopTempDay::getTempId, teId));
+            List<QwSopTempRules> tempRulesList = qwSopTempRulesService.listByCourseIdAndTempId(courseId, teId);
+
+            List<FsUserCourseVideo> addVideoList = videoList.stream()
+                    .filter(e -> tempRulesList.stream()
+                            .noneMatch(f -> f.getVideoId().equals(e.getVideoId())))
+                    .collect(Collectors.toList());
+
+            if (CollectionUtils.isNotEmpty(addVideoList)) {
+
                 // 通过历史中的第一课的数据来构建
-                Optional<QwSopTempDay> first = dayList.stream().filter(e -> e.getTempId().equals(temp.getId())).findFirst();
+                Optional<QwSopTempDay> first = dayList.stream().filter(e -> e.getTempId().equals(sopTemp.getId())).findFirst();
                 if (!first.isPresent()) {
-                    break;
+                    return;
                 }
                 QwSopTempDay qwSopTempDay = first.get();
+
                 // 构造timeList timeDesc time
-                temp.setTime(LocalTime.now());
+                sopTemp.setTime(LocalTime.now());
 
-                temp.setTimeList(rulesList.stream()
-                        .filter(e -> e.getTempId().equals(temp.getId()) && Objects.equals(e.getIsOfficial(), "0")
+                sopTemp.setTimeList(tempRulesList.stream()
+                        .filter(e -> e.getTempId().equals(sopTemp.getId()) && Objects.equals(e.getIsOfficial(), "0")
                                 && Objects.equals(e.getDayId(), qwSopTempDay.getId())
                         ).map(QwSopTempRules::getTime)
                         .collect(Collectors.toList()));
 
-                temp.setTimeDesc(contentList.stream().filter(e -> e.getTempId().equals(temp.getId())
+                sopTemp.setTimeDesc(contentList.stream().filter(e -> e.getTempId().equals(sopTemp.getId())
                                 && Objects.isNull(e.getIsBindUrl())
                                 && Objects.equals(qwSopTempDay.getId(), e.getDayId())
                         )
                         .map(m -> JSONObject.parseObject(m.getContent()).getString("value")).collect(Collectors.toList()));
-                temp.setProject(fsUserCourse.getProject());
-                temp.setCourseId(courseId);
-                temp.setOpenOfficial("1");
-                qwSopTempMapper.updateQwSopTemp(temp);
-                createSopTempRules(temp, addVideoList, fsUserCourse);
-            }
-        }
+                sopTemp.setProject(fsUserCourse.getProject());
+                sopTemp.setCourseId(courseId);
+                sopTemp.setOpenOfficial("1");
+
+                qwSopTempMapper.updateQwSopTemp(sopTemp);
+
+                createSopTempRules(sopTemp, addVideoList, fsUserCourse);
+
+                // 更新排序
+                List<QwSopTempRules> newTempRulesList = qwSopTempRulesService.listByCourseIdAndTempId(courseId, teId);
+                // 整理排序
+                Long[] dayIdArray = newTempRulesList.stream().filter(e -> e.getTempId().equals(sopTemp.getId())).sorted(Comparator.comparing(a -> videoSortMap.get(a.getVideoId())))
+                        .map(QwSopTempRules::getDayId).distinct().toArray(Long[]::new);
+                for (int i = 0; i < dayIdArray.length; i++) {
+                    qwSopTempDayService.updateByWrapper(new UpdateWrapper<QwSopTempDay>()
+                            .eq("id", dayIdArray[i])
+                            .set("sorts", i + 1)
+                            .set("name", "第" + (i + 1) + "天")
+                            .set("day_num", i + 1)
+                    );
+                }
 
-        // 整理排序
-        List<QwSopTempRules> afterRuleList = qwSopTempRulesService.listByCourseId(courseId);
-        for (QwSopTemp temp : tempList) {
-            Long[] dayIdArray = afterRuleList.stream().filter(e -> e.getTempId().equals(temp.getId())).sorted(Comparator.comparing(a -> videoSortMap.get(a.getVideoId())))
-                    .map(QwSopTempRules::getDayId).distinct().toArray(Long[]::new);
-            for (int i = 0; i < dayIdArray.length; i++) {
-                qwSopTempDayService.updateByWrapper(new UpdateWrapper<QwSopTempDay>()
-                        .eq("id", dayIdArray[i])
-                        .set("sorts", i + 1)
-                        .set("name", "第" + (i + 1) + "天")
-                        .set("day_num", i + 1)
-                );
             }
-        }
+        });
+
+//        FsUserCourse fsUserCourse = fsUserCourseMapper.selectFsUserCourseByCourseId(courseId);
+//        List<FsUserCourseVideo> videoList = fsUserCourseVideoMapper.selectVideoByCourseId(fsUserCourse.getCourseId());
+//        List<QwSopTemp> tempList = qwSopTempMapper.selectListByIds(tempIds);
+//        List<QwSopTempContent> contentList = qwSopTempContentService.listByTempIds(tempIds);
+//        List<QwSopTempDay> dayList = qwSopTempDayService.listByTempIds(new LambdaQueryWrapper<QwSopTempDay>().in(QwSopTempDay::getTempId, tempIds));
+//        List<Long> videoIdList = videoList.stream().map(FsUserCourseVideo::getVideoId).collect(Collectors.toList());
+//        // videoList转Map key 为videoId value 为 courseSort
+//        Map<Long, Long> videoSortMap = videoList.stream().collect(Collectors.toMap(FsUserCourseVideo::getVideoId, FsUserCourseVideo::getCourseSort));
+
+        // 将课程中已删除的视频在规则和日期中删除
+//        Set<Long> dayIdList = rulesList.stream().filter(e -> !videoIdList.contains(e.getVideoId())).map(QwSopTempRules::getDayId).collect(Collectors.toSet());
+//        if (CollectionUtils.isNotEmpty(dayIdList)) {
+//            qwSopTempDayService.removeByWrapper(new LambdaQueryWrapper<QwSopTempDay>().in(QwSopTempDay::getId, dayIdList));
+//            qwSopTempRulesService.removeByWrapper(new LambdaQueryWrapper<QwSopTempRules>().in(QwSopTempRules::getDayId, dayIdList));
+//            qwSopTempContentService.removeByWrapper(new LambdaQueryWrapper<QwSopTempContent>().in(QwSopTempContent::getDayId, dayIdList));
+//        }
+
+        // 需要添加的课程视频
+//        List<FsUserCourseVideo> addVideoList = videoList.stream()
+//                .filter(e -> rulesList.stream().noneMatch(f -> f.getVideoId().equals(e.getVideoId()))).collect(Collectors.toList());
+
+//        if (CollectionUtils.isNotEmpty(addVideoList)) {
+//            for (QwSopTemp temp : tempList) {
+//                // 通过历史中的第一课的数据来构建
+//                Optional<QwSopTempDay> first = dayList.stream().filter(e -> e.getTempId().equals(temp.getId())).findFirst();
+//                if (!first.isPresent()) {
+//                    break;
+//                }
+//                QwSopTempDay qwSopTempDay = first.get();
+//                // 构造timeList timeDesc time
+//                temp.setTime(LocalTime.now());
+//
+//                temp.setTimeList(rulesList.stream()
+//                        .filter(e -> e.getTempId().equals(temp.getId()) && Objects.equals(e.getIsOfficial(), "0")
+//                                && Objects.equals(e.getDayId(), qwSopTempDay.getId())
+//                        ).map(QwSopTempRules::getTime)
+//                        .collect(Collectors.toList()));
+//
+//                temp.setTimeDesc(contentList.stream().filter(e -> e.getTempId().equals(temp.getId())
+//                                && Objects.isNull(e.getIsBindUrl())
+//                                && Objects.equals(qwSopTempDay.getId(), e.getDayId())
+//                        )
+//                        .map(m -> JSONObject.parseObject(m.getContent()).getString("value")).collect(Collectors.toList()));
+//                temp.setProject(fsUserCourse.getProject());
+//                temp.setCourseId(courseId);
+//                temp.setOpenOfficial("1");
+//                qwSopTempMapper.updateQwSopTemp(temp);
+//                createSopTempRules(temp, addVideoList, fsUserCourse);
+//            }
+//        }
+
+//        // 整理排序
+//        List<QwSopTempRules> afterRuleList = qwSopTempRulesService.listByCourseId(courseId);
+//        for (QwSopTemp temp : tempList) {
+//            Long[] dayIdArray = afterRuleList.stream().filter(e -> e.getTempId().equals(temp.getId())).sorted(Comparator.comparing(a -> videoSortMap.get(a.getVideoId())))
+//                    .map(QwSopTempRules::getDayId).distinct().toArray(Long[]::new);
+//            for (int i = 0; i < dayIdArray.length; i++) {
+//                qwSopTempDayService.updateByWrapper(new UpdateWrapper<QwSopTempDay>()
+//                        .eq("id", dayIdArray[i])
+//                        .set("sorts", i + 1)
+//                        .set("name", "第" + (i + 1) + "天")
+//                        .set("day_num", i + 1)
+//                );
+//            }
+//        }
     }
 
 }

+ 1 - 1
fs-service/src/main/resources/application-config-druid-ddgy.yml

@@ -92,7 +92,7 @@ headerImg:
   imgUrl: https://ddgy-1323137866.cos.ap-chongqing.myqcloud.com/fs/20251010/ddgy.jpg
 ipad:
   ipadUrl: http://ipad.dingdangtcm.cn
-  aiApi: http://
+  aiApi: http://49.232.181.28:3000/api
   voiceApi:
   commonApi:
 wx_miniapp_temp:

+ 101 - 0
fs-service/src/main/resources/application-config-druid-hsyy.yml

@@ -0,0 +1,101 @@
+baidu:
+  token: 1
+  back-domain: https://www.xxxx.com
+#配置
+logging:
+  level:
+    org.springframework.web: INFO
+    com.github.binarywang.demo.wx.cp: DEBUG
+    me.chanjar.weixin: DEBUG
+wx:
+  miniapp:
+    configs:
+      - appid: w   #中康智慧
+        secret: 5
+        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
+        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+        msgDataFormat: JSON
+      - appid: w   #中康未来智慧药房
+        secret: 9
+        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
+        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+        msgDataFormat: JSON
+  cp:
+    corpId: wwb
+    appConfigs:
+      - agentId: 100005
+        secret: ec7okROXJqkNafq66aKNv0asTzQIG0CYrj3vyBbo
+        token: PPKOdAloMO
+        aesKey: PKvaxtpSvNGpfTDm7VUHIK8Wok2ESyYX24qpXJAdMP
+  pay:
+    appId: wx #微信公众号或者小程序等的appid
+    mchId: 1611045 #微信支付商户号
+    mchKey: 8cab128997a3547c10898b877f38 #微信支付商户密钥
+    subAppId:  #服务商模式下的子商户公众账号ID
+    subMchId:  #服务商模式下的子商户号
+    keyPath: c:\\cert\\apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
+    notifyUrl: https://usepp.his.runtzh.com/app/wxpay/wxPayNotify
+  mp:
+    useRedis: false
+    redisConfig:
+      host: 127.0.0.1
+      port: 6379
+      timeout: 2000
+    configs:
+      - appId: wx17f36a56c701bdea # 第一个公众号的appid   //公众号名称
+        secret: 185030bbe7f8d7a0c16b94dd9d4ea542 # 公众号的appsecret
+        token: PPKOdAlCoMO # 接口配置里的Token值
+        aesKey: Eswa6VjwtVcw03qZy6Wllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
+aifabu:  #爱链接
+  appKey: 7b471be905ab17ef358c610dd117601d008
+watch:
+  watchUrl: watch.ylrzcloud.com/prod-api
+#  account: tcloud
+#  password: mdf-m2h_6yw2$hq
+  account1: ccif #866655060138751
+  password1: cp-t5or_6xw7$mt
+  account2: tcloud #rt500台
+  password2: mdf-m2h_6yw2$hq
+  account3: whr
+  password3: v9xsKuqn_$d2y
+
+fs :
+  commonApi: http://172.16.16.47:7771
+  h5CommonApi: http://172.16.16.47:7771
+  jwt:
+    # 加密秘钥
+    secret: f4h2s52034348y86y67cde581c0f9eb5
+    # token有效时长,7天,单位秒
+    expire: 31536000
+    header: AppToken
+nuonuo:
+  key: 10924508
+  secret: A2EB20764D304D16
+# 存储捅配置
+tencent_cloud_config:
+  secret_id: AKIDiMq9lDf2EOM9lIfqqfKo7FNgM5meD0sT
+  secret_key: u5SuS80342xzx8FRBukza9lVNHKNMSaB
+  bucket: heshanyy-1323137866
+  app_id: 1323137866
+  region: ap-chongqing
+  proxy: heshanyy
+cloud_host:
+  company_name: 河山医院
+  projectCode: heshanyy
+#看课授权时显示的头像
+headerImg:
+  imgUrl: https://hsyy-1348049832.cos.ap-chongqing.myqcloud.com/hsyy.jpg
+ipad:
+  ipadUrl: http://ipad.hshsyy.com
+  aiApi: http://49.
+  voiceApi:
+  commonApi:
+wx_miniapp_temp:
+  pay_order_temp_id:
+  inquiry_temp_id:
+# 聚水潭API配置
+jst:
+  app_key: 5dea3c46f0214985bd6428b2b12 #聚水潭2025-09-03
+  app_secret: 3f382758a4f4470e8912be0ce #聚水潭2025-09-0
+  authorization_code: 999999
+  shop_code: "188784"

+ 1 - 1
fs-service/src/main/resources/application-config-druid-yxj.yml

@@ -86,7 +86,7 @@ cloud_host:
 headerImg:
   imgUrl: https://yxj-1323137866.cos.ap-chongqing.myqcloud.com/app/yxj.jpg
 ipad:
-  ipadUrl: http://.top
+  ipadUrl: http://yxjipad.ylrztop.com
   aiApi: http://49/api
   voiceApi:
   commonApi:

+ 3 - 1
fs-service/src/main/resources/application-druid-cfryt.yml

@@ -168,5 +168,7 @@ openIM:
 im:
     type: OPENIM
 #是否为新商户,新商户不走mpOpenId
-isNewWxMerchant: true
+isNewWxMerchant: false
+
+enableRedPackAccount: 0
 

+ 172 - 0
fs-service/src/main/resources/application-druid-hsyy.yml

@@ -0,0 +1,172 @@
+# 数据源配置
+spring:
+    profiles:
+        include: config-druid-hsyy,common
+    # redis 配置
+    redis:
+        host: 172.16.16.46
+        port: 6379
+        # 数据库索引
+        database: 0
+        # 密码
+        password: Ylrz_tM8
+        # 连接超时时间
+        timeout: 10s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+    datasource:
+        #        clickhouse:
+        #            type: com.alibaba.druid.pool.DruidDataSource
+        #            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
+        #            url: jdbc:clickhouse://1.14.104.71:8123/sop_test?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
+        #            username: rt_2024
+        #            password: Yzx_19860213
+        #            initialSize: 10
+        #            maxActive: 100
+        #            minIdle: 10
+        #            maxWait: 6000
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://172.16.16.37:3306/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrz_tM818782145I@
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    url: jdbc:mysql://172.16.16.37:3306/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrz_tM818782145I@
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 2000
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://172.16.16.37:3306/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrz_tM818782145I@
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 200
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: voice-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+custom:
+    token: "1o62d3YxvdHd4LEUiltnu7sK"
+    encoding-aes-key: "UJfTQ5qKTKlegjkXtp1YuzJzxeHlUKvq5GyFbERN1iU"
+    corp-id: "ww51717e2b71d5e2d3"
+    secret: "6ODAmw-8W4t6h9mdzHh2Z4Apwj8mnsyRnjEDZOHdA7k"
+    private-key-path: "privatekey.pem"
+    webhook-url: "https://your-server.com/wecom/archive"
+# token配置
+token:
+    # 令牌自定义标识
+    header: Authorization
+    # 令牌密钥
+    secret: abcdefghijklmnopqrstuvwxyz
+    # 令牌有效期(默认30分钟)
+    expireTime: 180
+openIM:
+    secret: openIM123
+    userID: imAdmin
+    url: https://web.im.ysya.top/api
+#是否使用新im
+im:
+    type: OPENIM
+#是否为新商户,新商户不走mpOpenId
+isNewWxMerchant: true
+enableRedPackAccount: 0

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

@@ -1005,4 +1005,97 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             o.send_type
     </select>
 
+
+    <select id="selectFsCourseWatchLogStatisticsListVO_COUNT"
+            resultType="java.lang.Long">
+        SELECT COUNT(*) FROM (
+        SELECT
+        o.video_id,
+        <!-- 用choose保证sendType逻辑互斥,避免GROUP BY字段缺失 -->
+        <choose>
+            <when test="sendType != 1">
+                o.qw_user_id,
+            </when>
+            <when test="sendType == 1">
+                o.company_user_id,
+            </when>
+            <!-- sendType为null时兜底,避免空指针 -->
+            <otherwise>
+                o.qw_user_id,
+            </otherwise>
+        </choose>
+        DATE(o.create_time) create_time
+        FROM fs_course_watch_log o
+        <!-- 动态关联qw_user表 -->
+        <if test="sendType != 1">
+            LEFT JOIN qw_user qu ON qu.id = o.qw_user_id
+        </if>
+        LEFT JOIN fs_user_course_video v ON v.video_id = o.video_id
+        LEFT JOIN fs_user_course uc ON uc.course_id = v.course_id
+        <!-- 动态关联company_user表 -->
+        <if test="sendType == 1">
+            LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+        </if>
+        WHERE o.company_id = #{companyId}
+        <!-- 发送类型筛选 -->
+        <if test="sendType != null">
+            AND send_type = #{sendType}
+        </if>
+        <!-- 开始时间筛选:区分String/Date类型,避免类型比较异常 -->
+        <if test="sTime != null">
+            <choose>
+                <!-- sTime是String类型(yyyy-MM-dd) -->
+                <when test="sTime instanceof java.lang.String and sTime.trim() != ''">
+                    AND o.create_time &gt;= STR_TO_DATE(#{sTime}, '%Y-%m-%d')
+                </when>
+                <!-- sTime是Date类型 -->
+                <otherwise>
+                    AND o.create_time &gt;= #{sTime}
+                </otherwise>
+            </choose>
+        </if>
+        <!-- 结束时间筛选:区分String/Date类型 -->
+        <if test="eTime != null">
+            <choose>
+                <when test="eTime instanceof java.lang.String and eTime.trim() != ''">
+                    AND o.create_time &lt; DATE_ADD(STR_TO_DATE(#{eTime}, '%Y-%m-%d'), INTERVAL 1 DAY)
+                </when>
+                <otherwise>
+                    AND o.create_time &lt; DATE_ADD(#{eTime}, INTERVAL 1 DAY)
+                </otherwise>
+            </choose>
+        </if>
+        <!-- 昵称筛选:sendType!=1时关联qw_user -->
+        <if test="sendType != 1 and nickName != null and nickName.trim() != ''">
+            AND qu.qw_user_name LIKE CONCAT(#{nickName}, '%')
+        </if>
+        <!-- 昵称筛选:sendType==1时关联company_user -->
+        <if test="sendType == 1 and nickName != null and nickName.trim() != ''">
+            AND cu.nick_name LIKE CONCAT(#{nickName}, '%')
+        </if>
+        <!-- 课程ID筛选:加>0判断,避免null值拼接 -->
+        <if test="courseId != null and courseId > 0">
+            AND o.course_id = #{courseId}
+        </if>
+        <!-- 视频ID筛选:加>0判断 -->
+        <if test="videoId != null and videoId > 0">
+            AND o.video_id = #{videoId}
+        </if>
+        <!-- 分组条件:与子查询字段一致 -->
+        GROUP BY
+        o.video_id,
+        <choose>
+            <when test="sendType != 1">
+                o.qw_user_id,
+            </when>
+            <when test="sendType == 1">
+                o.company_user_id,
+            </when>
+            <otherwise>
+                o.qw_user_id,
+            </otherwise>
+        </choose>
+        DATE(o.create_time)
+        ) AS t
+    </select>
 </mapper>

+ 1 - 1
fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml

@@ -1795,7 +1795,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 <!--        </if>-->
 
         <where>
-            <if test="maps.appId != null and map.appId != ''">
+            <if test="maps.appId != null and maps.appId != ''">
                 and csc.appid = #{maps.appId}
             </if>
             <if test="maps.orderCodes != null  and maps.orderCodes.size > 0">

+ 1 - 0
fs-service/src/main/resources/mapper/hisStore/FsUserAddressScrmMapper.xml

@@ -6,6 +6,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <resultMap type="FsUserAddressScrm" id="FsUserAddressResult">
         <result property="id"    column="address_id"    />
+        <result property="addressId"    column="address_id"    />
         <result property="userId"    column="user_id"    />
         <result property="realName"    column="real_name"    />
         <result property="phone"    column="phone"    />

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

@@ -0,0 +1,403 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.hisStore.mapper.MergedOrderMapper">
+
+    <select id="selectMergedOrderList" parameterType="com.fs.live.param.MergedOrderQueryParam" resultType="com.fs.live.vo.MergedOrderVO">
+        SELECT * FROM (
+      SELECT
+      o.id,
+      '销售订单' AS order_type_name,
+      NULL AS order_id,
+      NULL AS live_id,
+      NULL AS after_sales_id,
+      o.order_code,
+      o.pay_price,
+      o.pay_money,
+      o.STATUS,
+      o.is_package,
+      o.package_json,
+      o.item_json,
+      o.delivery_id,
+      o.finish_time,
+      o.create_time,
+      o.pay_time,
+      o.delivery_send_time,
+      NULL AS total_num,
+      NULL AS discount_money,
+      1 AS order_type,
+
+        cu.phonenumber as salesPhone,
+        cu.create_time as salesCreateTime,
+        u.user_id as userId,
+        u.order_count as userOrderCount,
+        u.total_amount as userTotalAmount,
+        u.level as userLevel,
+        fspc.product_id as productId,
+        fspc.product_name as productName,
+        fspc.cost as cost,
+        o.pay_postage as payDelivery,
+        o.coupon_price as discountMoney,
+        fspc.prescribe_spec as productSpec,
+        fss.store_id as storeId,
+        fss.store_name as storeName,
+        fspcs.cate_name as cateName,
+        GROUP_CONCAT(JSON_UNQUOTE(JSON_EXTRACT(o.item_json, '$.barCode')) SEPARATOR ',') AS barCode,
+
+      c.company_name,
+      cu.user_name AS sales_name,
+      cu.nick_name AS company_user_nick_name,
+      u.nickname,
+      u.phone,
+      o.real_name,
+      o.user_phone,
+      o.user_address,
+      o.pay_type,
+      o.delivery_status,
+      o.delivery_pay_status,
+      o.total_price,
+      csc.NAME AS mini_program_name,
+      sp_latest.bank_transaction_id
+      FROM
+      fs_store_order_scrm o
+      left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.item_id  ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id and item_latest.rn = 1
+      LEFT JOIN fs_user u ON o.user_id = u.user_id
+
+      LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = item_latest.product_id
+      LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
+      left join fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
+
+      LEFT JOIN company c ON c.company_id = o.company_id
+      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_code ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_code = o.order_code
+      AND sp_latest.rn = 1
+      LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
+          WHERE o.is_del = 0 AND o.is_sys_del = 0 AND o.company_user_id IS NOT NULL AND o.company_user_id != 0
+          <if test="maps.status != null and maps.status != ''">
+            AND o.status = #{maps.status}
+          </if>
+          <if test="maps.orderCode != null and maps.orderCode != ''">
+            AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+          </if>
+          <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+            AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+          </if>
+          <if test="maps.deliveryId != null and maps.deliveryId != ''">
+            AND o.delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
+          </if>
+          <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
+            AND o.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+          </if>
+          <if test="maps.userPhone != null and maps.userPhone != ''">
+            AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+          </if>
+          <if test="maps.realName != null and maps.realName != ''">
+            AND o.real_name LIKE CONCAT('%', #{maps.realName}, '%')
+          </if>
+          <if test="maps.productName != null and maps.productName != ''">
+            AND fspc.productName LIKE CONCAT('%', #{maps.productName}, '%')
+          </if>
+          <if test="maps.deliveryStatus != null">
+            AND o.delivery_status = #{maps.deliveryStatus}
+          </if>
+          <if test="maps.deliveryPayStatus != null">
+            AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+          </if>
+          <if test="maps.payType != null and maps.payType != ''">
+            AND o.pay_type = #{maps.payType}
+          </if>
+          <if test="maps.companyId != null">
+            AND o.company_id = #{maps.companyId}
+          </if>
+          <if test="maps.deptId != null">
+            AND o.dept_id = #{maps.deptId}
+          </if>
+          <if test="maps.salesName != null and maps.salesName != ''">
+            AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
+          </if>
+          <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
+            AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+          </if>
+          <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+            AND DATE(o.create_time) BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+          </if>
+          <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+            AND DATE(o.pay_time) BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+          </if>
+          <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+            AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+          </if>
+          <if test="maps.deliveryImportTimeRange != null and maps.deliveryImportTimeRange != ''">
+            AND DATE(o.delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+          </if>
+          <if test="maps.appId != null and maps.appId != ''">
+            AND csc.appid = #{maps.appId}
+          </if>
+          group by o.id
+          UNION ALL
+          -- 商城订单(没有company_user_id的商城订单)
+      SELECT
+      o.id,
+      '商城订单' AS order_type_name,
+      NULL AS order_id,
+      NULL AS live_id,
+      NULL AS after_sales_id,
+      o.order_code,
+      o.pay_price,
+      o.pay_money,
+      o.STATUS,
+      o.is_package,
+      o.package_json,
+      o.item_json,
+      o.delivery_id,
+      o.finish_time,
+      o.create_time,
+      o.pay_time,
+      o.delivery_send_time,
+      NULL AS total_num,
+      NULL AS discount_money,
+      2 AS order_type,
+
+    cu.phonenumber as salesPhone,
+    cu.create_time as salesCreateTime,
+    u.user_id as userId,
+    u.order_count as userOrderCount,
+    u.total_amount as userTotalAmount,
+    u.level as userLevel,
+    fspc.product_id as productId,
+    fspc.product_name as productName,
+    fspc.prescribe_spec as productSpec,
+        fspc.cost as cost,
+        o.pay_postage as payDelivery,
+        o.coupon_price as discountMoney,
+    fss.store_id as storeId,
+    fss.store_name as storeName,
+    fspcs.cate_name as cateName,
+    GROUP_CONCAT(JSON_UNQUOTE(JSON_EXTRACT(o.item_json, '$.barCode')) SEPARATOR ',') AS barCode,
+
+      c.company_name,
+      cu.user_name AS sales_name,
+      cu.nick_name AS company_user_nick_name,
+      u.nickname,
+      u.phone,
+      o.real_name,
+      o.user_phone,
+      o.user_address,
+      o.pay_type,
+      o.delivery_status,
+      o.delivery_pay_status,
+      o.total_price,
+      csc.NAME AS mini_program_name,
+      sp_latest.bank_transaction_id
+      FROM
+      fs_store_order_scrm o
+        left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.item_id  ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id
+      LEFT JOIN fs_user u ON o.user_id = u.user_id
+
+        LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = item_latest.product_id
+        LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
+        left join fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
+
+      LEFT JOIN company c ON c.company_id = o.company_id
+      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_code ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_code = o.order_code
+      AND sp_latest.rn = 1
+      LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
+          WHERE o.is_del = 0 AND o.is_sys_del = 0 AND (o.company_user_id IS NULL OR o.company_user_id = 0)
+          <if test="maps.status != null and maps.status != ''">
+            AND o.status = #{maps.status}
+          </if>
+          <if test="maps.orderCode != null and maps.orderCode != ''">
+            AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+          </if>
+          <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+            AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+          </if>
+          <if test="maps.deliveryId != null and maps.deliveryId != ''">
+            AND o.delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
+          </if>
+          <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
+            AND o.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+          </if>
+          <if test="maps.userPhone != null and maps.userPhone != ''">
+            AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+          </if>
+          <if test="maps.realName != null and maps.realName != ''">
+            AND o.real_name LIKE CONCAT('%', #{maps.realName}, '%')
+          </if>
+          <if test="maps.productName != null and maps.productName != ''">
+            AND o.item_json LIKE CONCAT('%', #{maps.productName}, '%')
+          </if>
+          <if test="maps.deliveryStatus != null">
+            AND o.delivery_status = #{maps.deliveryStatus}
+          </if>
+          <if test="maps.deliveryPayStatus != null">
+            AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+          </if>
+          <if test="maps.payType != null and maps.payType != ''">
+            AND o.pay_type = #{maps.payType}
+          </if>
+          <if test="maps.companyId != null">
+            AND o.company_id = #{maps.companyId}
+          </if>
+          <if test="maps.deptId != null">
+            AND o.dept_id = #{maps.deptId}
+          </if>
+          <if test="maps.salesName != null and maps.salesName != ''">
+            AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
+          </if>
+          <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
+            AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+          </if>
+          <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+            AND DATE(o.create_time) BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+          </if>
+          <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+            AND DATE(o.pay_time) BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+          </if>
+          <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+            AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+          </if>
+          <if test="maps.deliveryImportTimeRange != null and maps.deliveryImportTimeRange != ''">
+            AND DATE(o.delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+          </if>
+          <if test="maps.appId != null and maps.appId != ''">
+            AND csc.appid = #{maps.appId}
+          </if>
+        group by o.id
+          UNION ALL
+          -- 直播订单
+      SELECT NULL AS
+      id,
+      '直播订单' AS order_type_name,
+      o.order_id,
+      o.live_id,
+      a.id AS after_sales_id,
+      o.order_code,
+      o.pay_price,
+      o.pay_money,
+      o.STATUS,
+      NULL AS is_package,
+      NULL AS package_json,
+      o.item_json,
+      o.delivery_sn AS delivery_id,
+      o.finish_time,
+      o.create_time,
+      o.pay_time,
+      o.delivery_send_time,
+      o.total_num,
+      o.discount_money,
+      3 AS order_type,
+
+        cu.phonenumber as salesPhone,
+        cu.create_time as salesCreateTime,
+        u.user_id as userId,
+        u.order_count as userOrderCount,
+        u.total_amount as userTotalAmount,
+        u.level as userLevel,
+        fspc.product_id as productId,
+        fspc.product_name as productName,
+        fspc.prescribe_spec as productSpec,
+        fspc.cost as cost,
+        o.pay_delivery as payDelivery,
+        o.discount_money as discountMoney,
+        fss.store_id as storeId,
+        fss.store_name as storeName,
+        fspcs.cate_name as cateName,
+        GROUP_CONCAT(JSON_UNQUOTE(JSON_EXTRACT(loi.json_info, '$.barCode')) SEPARATOR ',') AS barCode,
+
+      c.company_name,
+      cu.user_name AS sales_name,
+      cu.nick_name AS company_user_nick_name,
+      u.nickname,
+      u.phone,
+      o.user_name AS real_name,
+      o.user_phone,
+      o.user_address,
+      o.pay_type,
+      o.delivery_status,
+      o.delivery_pay_status,
+      o.total_price,
+      csc.NAME AS mini_program_name,
+      sp_latest.bank_transaction_id
+      FROM
+      live_order o
+      left join live_order_item loi on loi.order_id = o.order_id
+      LEFT JOIN fs_user u ON o.user_id = u.user_id
+
+        LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = o.product_id
+        LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
+        left join fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
+
+
+      LEFT JOIN company c ON c.company_id = o.company_id
+      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+      LEFT JOIN ( SELECT t.*, ROW_NUMBER() OVER ( PARTITION BY t.order_id ORDER BY t.create_time DESC ) AS rn FROM live_after_sales t ) a ON o.order_id = a.order_id
+      AND a.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 ) sp_latest ON sp_latest.business_code = o.order_code
+      AND sp_latest.rn = 1
+      LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
+          WHERE o.is_del = 0
+          <if test="maps.status != null and maps.status != ''">
+            AND o.status = #{maps.status}
+          </if>
+          <if test="maps.orderCode != null and maps.orderCode != ''">
+            AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+          </if>
+          <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+            AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+          </if>
+          <if test="maps.deliveryId != null and maps.deliveryId != ''">
+            AND o.delivery_sn LIKE CONCAT('%', #{maps.deliveryId}, '%')
+          </if>
+          <if test="maps.userPhone != null and maps.userPhone != ''">
+            AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+          </if>
+          <if test="maps.realName != null and maps.realName != ''">
+            AND o.user_name LIKE CONCAT('%', #{maps.realName}, '%')
+          </if>
+          <if test="maps.productName != null and maps.productName != ''">
+            AND o.item_json LIKE CONCAT('%', #{maps.productName}, '%')
+          </if>
+          <if test="maps.deliveryStatus != null">
+            AND o.delivery_status = #{maps.deliveryStatus}
+          </if>
+          <if test="maps.deliveryPayStatus != null">
+            AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+          </if>
+          <if test="maps.payType != null and maps.payType != ''">
+            AND o.pay_type = #{maps.payType}
+          </if>
+          <if test="maps.companyId != null">
+            AND o.company_id = #{maps.companyId}
+          </if>
+          <if test="maps.deptId != null">
+            AND o.dept_id = #{maps.deptId}
+          </if>
+          <if test="maps.salesName != null and maps.salesName != ''">
+            AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
+          </if>
+          <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
+            AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+          </if>
+          <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+            AND DATE(o.create_time) BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+          </if>
+          <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+            AND DATE(o.pay_time) BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+          </if>
+          <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+            AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+          </if>
+        group by o.order_id
+        ) AS merged_orders
+        WHERE 1=1
+        <if test="maps.orderTypeFilter != null">
+          AND order_type = #{maps.orderTypeFilter}
+        </if>
+        ORDER BY create_time DESC
+    </select>
+
+</mapper>
+

+ 3 - 3
fs-service/src/main/resources/mapper/live/LiveDataMapper.xml

@@ -283,7 +283,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <!-- 查询直播间统计数据 -->
     <select id="selectLiveDataStatistics" resultType="com.fs.live.vo.LiveDataStatisticsVo">
         SELECT
-            COUNT( lwu.user_id) AS totalViewers,
+            COUNT( distinct lwu.user_id) AS totalViewers,
             COUNT( CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.user_id END) AS liveViewers,
             COUNT( CASE WHEN lwu.live_flag = 0 and lwu.replay_flag = 1 THEN lwu.user_id END) AS playbackViewers,
             COALESCE(AVG(CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.online_seconds END), 0) AS liveAvgDuration,
@@ -360,7 +360,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             l.status AS status,
             l.start_time AS startTime,
             l.finish_time AS finishTime,
-            COUNT(1) AS totalViewers,
+            COUNT(distinct lwu.user_id) AS totalViewers,
             COUNT(CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.user_id END) AS liveViewers,
             COUNT(CASE WHEN lwu.live_flag = 0 and lwu.replay_flag = 1 THEN lwu.user_id END) AS playbackViewers,
             COALESCE(AVG(CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.online_seconds END), 0) AS liveAvgDuration,
@@ -420,7 +420,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             CASE
                 WHEN COUNT(DISTINCT lwu.user_id) > 0 THEN
                     ROUND(COUNT(DISTINCT CASE
-                        WHEN COALESCE(user_duration.total_duration, 0) >= 1800
+                        WHEN COALESCE(user_duration.total_duration, 0) >= 1200
                         THEN lwu.user_id
                     END) * 100.0 / COUNT(DISTINCT lwu.user_id), 2)
                 ELSE 0

+ 9 - 1
fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml

@@ -89,7 +89,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <select id="selectQwExternalContactByIds" resultType="com.fs.qw.domain.QwExternalContact">
         select * from qw_external_contact
-        where id in
+        where  id in
+        <foreach collection="ids" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
+
+    <select id="selectQwExternalContactByIdsStatus" resultType="com.fs.qw.domain.QwExternalContact">
+        select * from qw_external_contact
+        where status !=3 and id in
         <foreach collection="ids" item="id" open="(" separator="," close=")">
             #{id}
         </foreach>

+ 15 - 0
fs-user-app/src/main/java/com/fs/app/controller/IntegralController.java

@@ -66,6 +66,21 @@ public class IntegralController extends  AppBaseController {
     @Cacheable(value = "getIntegralGoodsById", key = "#goodsId")
     public R getIntegralGoodsById(@RequestParam("goodsId")Long goodsId, HttpServletRequest request){
         FsIntegralGoods goods=goodsService.selectFsIntegralGoodsByGoodsId(goodsId);
+        // 填充默认值
+        if (goods != null) {
+            if (goods.getImages() == null) {
+                goods.setImages("");
+            }
+            if (goods.getNum() == null) {
+                goods.setNum(0);
+            }
+            if (goods.getRemark() == null) {
+                goods.setRemark("");
+            }
+            if (goods.getSearchValue() == null) {
+                goods.setSearchValue("");
+            }
+        }
         return R.ok().put("data",goods);
     }
 

+ 1 - 1
fs-user-app/src/main/java/com/fs/app/controller/store/AddressScrmController.java

@@ -113,7 +113,7 @@ public class AddressScrmController extends AppBaseController {
         FsUserAddressScrm userAddress=new FsUserAddressScrm();
         BeanUtil.copyProperties(address, userAddress);
         addressService.insertFsUserAddress(userAddress);
-        return R.ok("操作成功");
+        return R.ok("操作成功").put("addressId", userAddress.getId());
     }
 
     @Login