Parcourir la source

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
xyx il y a 21 heures
Parent
commit
f83ebbd2e3
100 fichiers modifiés avec 2957 ajouts et 305 suppressions
  1. 20 5
      fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java
  2. 2 1
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreAfterSalesScrmController.java
  3. 3 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java
  4. 8 3
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  5. 16 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java
  6. 61 9
      fs-admin/src/main/java/com/fs/hisStore/task/LiveTask.java
  7. 6 0
      fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java
  8. 19 6
      fs-admin/src/main/java/com/fs/live/controller/LiveAfterSalesController.java
  9. 1 1
      fs-admin/src/main/java/com/fs/live/controller/LiveController.java
  10. 43 0
      fs-admin/src/main/java/com/fs/live/controller/LiveDataController.java
  11. 59 13
      fs-admin/src/main/java/com/fs/live/controller/LiveOrderController.java
  12. 6 6
      fs-company/src/main/java/com/fs/company/controller/live/LiveAfterSalesController.java
  13. 8 1
      fs-company/src/main/java/com/fs/company/controller/live/LiveController.java
  14. 8 8
      fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java
  15. 45 13
      fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java
  16. 5 5
      fs-service/pom.xml
  17. 13 13
      fs-service/src/main/java/com/cloud/host/CloudHostConfig.java
  18. 4 1
      fs-service/src/main/java/com/fs/company/mapper/CompanyMapper.java
  19. 5 0
      fs-service/src/main/java/com/fs/company/vo/CompanyVO.java
  20. 2 0
      fs-service/src/main/java/com/fs/course/service/IFsCoursePlaySourceConfigService.java
  21. 6 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCoursePlaySourceConfigServiceImpl.java
  22. 228 2
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  23. 4 4
      fs-service/src/main/java/com/fs/course/vo/FsCourseProductOrderVO.java
  24. 6 4
      fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java
  25. 3 0
      fs-service/src/main/java/com/fs/his/domain/FsAdv.java
  26. 3 0
      fs-service/src/main/java/com/fs/his/domain/FsUserInformationCollection.java
  27. 0 1
      fs-service/src/main/java/com/fs/his/mapper/FsAdvMapper.java
  28. 3 4
      fs-service/src/main/java/com/fs/his/mapper/FsPackageOrderMapper.java
  29. 3 4
      fs-service/src/main/java/com/fs/his/service/IFsPackageOrderService.java
  30. 29 12
      fs-service/src/main/java/com/fs/his/service/impl/FsPackageOrderServiceImpl.java
  31. 6 4
      fs-service/src/main/java/com/fs/his/service/impl/FsPackageServiceImpl.java
  32. 8 2
      fs-service/src/main/java/com/fs/his/service/impl/FsPrescribeServiceImpl.java
  33. 23 6
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java
  34. 20 0
      fs-service/src/main/java/com/fs/his/utils/IdCardUtil.java
  35. 12 0
      fs-service/src/main/java/com/fs/his/vo/FsUserInfoCollectionAndStoreOrderVo.java
  36. 3 1
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java
  37. 12 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStorePaymentScrmMapper.java
  38. 8 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsUserScrmMapper.java
  39. 6 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderParam.java
  40. 5 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStorePaymentScrmService.java
  41. 2 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsUserScrmService.java
  42. 5 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java
  43. 9 5
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  44. 93 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStorePaymentScrmServiceImpl.java
  45. 285 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
  46. 5 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsUserScrmServiceImpl.java
  47. 3 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportVO.java
  48. 4 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderVO.java
  49. 20 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStorePaymentUsetVo.java
  50. 23 23
      fs-service/src/main/java/com/fs/im/service/impl/OpenIMServiceImpl.java
  51. 26 4
      fs-service/src/main/java/com/fs/ipad/IpadSendUtils.java
  52. 2 0
      fs-service/src/main/java/com/fs/live/domain/Live.java
  53. 3 0
      fs-service/src/main/java/com/fs/live/mapper/LiveCouponMapper.java
  54. 16 0
      fs-service/src/main/java/com/fs/live/mapper/LiveDataMapper.java
  55. 8 5
      fs-service/src/main/java/com/fs/live/mapper/LiveMapper.java
  56. 18 3
      fs-service/src/main/java/com/fs/live/mapper/LiveOrderMapper.java
  57. 3 0
      fs-service/src/main/java/com/fs/live/mapper/LiveWatchUserMapper.java
  58. 1 0
      fs-service/src/main/java/com/fs/live/param/LiveDataParam.java
  59. 21 0
      fs-service/src/main/java/com/fs/live/param/LiveNotifyParam.java
  60. 3 0
      fs-service/src/main/java/com/fs/live/param/LiveOrderParam.java
  61. 7 0
      fs-service/src/main/java/com/fs/live/service/ILiveAutoTaskService.java
  62. 28 0
      fs-service/src/main/java/com/fs/live/service/ILiveDataService.java
  63. 7 1
      fs-service/src/main/java/com/fs/live/service/ILiveOrderService.java
  64. 3 2
      fs-service/src/main/java/com/fs/live/service/ILiveService.java
  65. 15 4
      fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java
  66. 242 4
      fs-service/src/main/java/com/fs/live/service/impl/LiveAutoTaskServiceImpl.java
  67. 52 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveCouponIssueServiceImpl.java
  68. 84 4
      fs-service/src/main/java/com/fs/live/service/impl/LiveCouponServiceImpl.java
  69. 417 3
      fs-service/src/main/java/com/fs/live/service/impl/LiveDataServiceImpl.java
  70. 43 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java
  71. 36 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveLotteryConfServiceImpl.java
  72. 82 13
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  73. 36 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java
  74. 36 21
      fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java
  75. 1 2
      fs-service/src/main/java/com/fs/live/service/impl/LiveWatchUserServiceImpl.java
  76. 5 0
      fs-service/src/main/java/com/fs/live/vo/FsMyLiveOrderListQueryVO.java
  77. 98 0
      fs-service/src/main/java/com/fs/live/vo/LiveDataDetailVo.java
  78. 4 0
      fs-service/src/main/java/com/fs/live/vo/LiveOrderVO.java
  79. 3 0
      fs-service/src/main/java/com/fs/live/vo/LiveOrderVoZm.java
  80. 40 0
      fs-service/src/main/java/com/fs/live/vo/LiveUserDetailVo.java
  81. 2 0
      fs-service/src/main/java/com/fs/live/vo/LiveVo.java
  82. 28 0
      fs-service/src/main/java/com/fs/live/vo/ProductSalesVo.java
  83. 6 6
      fs-service/src/main/java/com/fs/qw/service/IQwUserService.java
  84. 48 48
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  85. 89 0
      fs-service/src/main/java/com/fs/utils/TwelveDigitSnowflake.java
  86. 1 1
      fs-service/src/main/resources/application-config-druid-gzzdy.yml
  87. 6 0
      fs-service/src/main/resources/application-config-druid-hzyy.yml
  88. 1 1
      fs-service/src/main/resources/application-config-fzbt.yml
  89. 1 1
      fs-service/src/main/resources/application-config-myhk.yml
  90. 5 5
      fs-service/src/main/resources/application-druid-bjzm.yml
  91. 1 1
      fs-service/src/main/resources/mapper/course/FsUserCourseTrainingCampMapper.xml
  92. 10 1
      fs-service/src/main/resources/mapper/his/FsAdvMapper.xml
  93. 6 0
      fs-service/src/main/resources/mapper/his/FsPackageOrderMapper.xml
  94. 4 0
      fs-service/src/main/resources/mapper/his/FsUserInformationCollectionMapper.xml
  95. 22 2
      fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml
  96. 24 0
      fs-service/src/main/resources/mapper/hisStore/FsStorePaymentScrmMapper.xml
  97. 136 0
      fs-service/src/main/resources/mapper/live/LiveDataMapper.xml
  98. 16 0
      fs-service/src/main/resources/mapper/live/LiveMapper.xml
  99. 39 14
      fs-service/src/main/resources/mapper/live/LiveOrderMapper.xml
  100. 1 0
      fs-service/src/main/resources/mapper/qw/QwGroupChatMapper.xml

+ 20 - 5
fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java

@@ -128,6 +128,9 @@ public class FsStoreOrderController extends BaseController
     private IFsDfAccountService fsDfAccountService;
     @Autowired
     private FsUserInformationCollectionMapper userInformationCollectionMapper;
+    @Autowired
+    private IFsPackageOrderService fsPackageOrderService;
+
     /**
      * 查询订单列表
      */
@@ -652,8 +655,8 @@ public class FsStoreOrderController extends BaseController
     {
         String nickName = getLoginUser().getUser().getNickName();
         if(CloudHostUtils.hasCloudHostName("金牛明医")){
-            FsUserInformationCollection fsUserInformationCollection = userInformationCollectionMapper.selectFsUserInformationCollectionByOrderCode(fsStoreOrder.getOrderCode());
-            if (fsUserInformationCollection != null&&fsUserInformationCollection.getDoctorType2Confirm()!=1) {
+            FsUserInfoCollectionAndStoreOrderVo info = fsPackageOrderService.selectInformationCollectionByStoreOrderId(fsStoreOrder.getOrderId());
+            if (info != null&&info.getDoctorType2Confirm()!=1) {
                 return AjaxResult.error("药师未确认");
             }
         }
@@ -760,8 +763,16 @@ public class FsStoreOrderController extends BaseController
         if (orderIds.isEmpty()){
             return R.ok();
         }
-        orderIds.forEach(orderId->{
+        //失败的订单
+        StringBuilder msg = new StringBuilder();
+        for (Long orderId : orderIds) {
             try {
+                //判断是否是信息采集订单 且 药师已经确认
+                FsUserInfoCollectionAndStoreOrderVo info = fsPackageOrderService.selectInformationCollectionByStoreOrderId(orderId);
+                if (info != null&&info.getDoctorType2Confirm()!=1) {
+                    msg.append("订单编号:").append(info.getStoreOrderCode()).append(",推送erp失败,失败原因-信息采集订单,药师暂未确认!\n");
+                    continue;
+                }
                 df.setOrderId(orderId);
                 FsStoreOrderDf temp = fsStoreOrderDfService.selectFsStoreOrderDfByOrderId(df.getOrderId());
                 if (temp == null){
@@ -777,8 +788,12 @@ public class FsStoreOrderController extends BaseController
                 throw new RuntimeException(e);
             }
 
-        });
-        return R.ok();
+        }
+        if (msg.toString().isEmpty()){
+            return R.ok();
+        } else {
+            return R.error(msg.toString());
+        }
     }
 
 

+ 2 - 1
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreAfterSalesScrmController.java

@@ -9,6 +9,7 @@ import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.domain.FsUser;
@@ -79,7 +80,7 @@ public class FsStoreAfterSalesScrmController extends BaseController
     @GetMapping("/export")
     public AjaxResult export(FsStoreAfterSalesScrm fsStoreAfterSales)
     {
-        if (fsStoreAfterSales.getBeginTime().equals("") && fsStoreAfterSales.getEndTime().equals("")){
+        if (StringUtils.isNotEmpty(fsStoreAfterSales.getBeginTime()) && StringUtils.isNotEmpty(fsStoreAfterSales.getEndTime())){
             fsStoreAfterSales.setBeginTime(null);
             fsStoreAfterSales.setEndTime(null);
         }

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

@@ -121,6 +121,7 @@ public class FsStoreHealthOrderScrmController extends BaseController {
                     vo.setPayDelivery(BigDecimal.ZERO);
                     vo.setBarCode("");
                     vo.setCateName("");
+                    vo.setBankTransactionId("");
                 }
 
             }
@@ -308,6 +309,7 @@ public class FsStoreHealthOrderScrmController extends BaseController {
                     vo.setFPrice(BigDecimal.ZERO);
                     vo.setBarCode("");
                     vo.setCateName("");
+                    vo.setBankTransactionId("");
                 }
             }
         }
@@ -359,6 +361,7 @@ public class FsStoreHealthOrderScrmController extends BaseController {
                     vo.setFPrice(BigDecimal.ZERO);
                     vo.setBarCode("");
                     vo.setCateName("");
+                    vo.setBankTransactionId("");
                 }
             }
         }

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

@@ -53,6 +53,7 @@ import com.fs.hisStore.service.*;
 import com.fs.hisStore.vo.*;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
+import com.github.pagehelper.PageHelper;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -173,7 +174,7 @@ public class FsStoreOrderScrmController extends BaseController {
     @PreAuthorize("@ss.hasPermi('store:storeOrder:list')")
     @PostMapping("/list")
     public TableDataInfo list(@RequestBody FsStoreOrderParam param) {
-        startPage();
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
         if(!StringUtils.isEmpty(param.getCreateTimeRange())){
             param.setCreateTimeList(param.getCreateTimeRange().split("--"));
         }
@@ -217,6 +218,7 @@ public class FsStoreOrderScrmController extends BaseController {
                     vo.setPayDelivery(BigDecimal.ZERO);
                     vo.setBarCode("");
                     vo.setCateName("");
+                    vo.setBankTransactionId("");
                 }
             }
         }
@@ -248,7 +250,7 @@ public class FsStoreOrderScrmController extends BaseController {
     @PreAuthorize("@ss.hasPermi('store:storeOrder:payRemainList')")
     @GetMapping("/payRemainList")
     public TableDataInfo payRemainList(FsStoreOrderParam param) {
-        startPage();
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
         if(!StringUtils.isEmpty(param.getCreateTimeRange())){
             param.setCreateTimeList(param.getCreateTimeRange().split("--"));
         }
@@ -473,6 +475,7 @@ public class FsStoreOrderScrmController extends BaseController {
                     vo.setFPrice(BigDecimal.ZERO);
                     vo.setBarCode("");
                     vo.setCateName("");
+                    vo.setBankTransactionId("");
                 }
             }
         }
@@ -528,6 +531,7 @@ public class FsStoreOrderScrmController extends BaseController {
                     vo.setFPrice(BigDecimal.ZERO);
                     vo.setBarCode("");
                     vo.setCateName("");
+                    vo.setBankTransactionId("");
                 }
             }
         }
@@ -790,7 +794,8 @@ public class FsStoreOrderScrmController extends BaseController {
 
     @GetMapping("/getCustomerOrderList")
     public TableDataInfo getCustomerOrderList(FsStoreOrderParam param) {
-        startPage();
+//        startPage();
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
         List<FsStoreOrderVO> list = fsStoreOrderService.selectFsCustomerStoreOrderListVO(param);
         if (list != null) {
             for (FsStoreOrderVO vo : list) {

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

@@ -334,4 +334,20 @@ public class FsStorePaymentScrmController extends BaseController
     {
         return toAjax(fsStorePaymentService.deleteFsStorePaymentByIds(paymentIds));
     }
+
+    /**
+     * 一键发货
+     * @return R
+     * **/
+    @Log(title = "发货同步导入", businessType = BusinessType.IMPORT)
+    @PostMapping("/oneClickShipping")
+    public R oneClickShipping() {
+
+        try {
+            return fsStorePaymentService.oneClickShipping();
+        }catch (Exception e){
+            e.getStackTrace();
+        }
+        return R.ok();
+    }
 }

+ 61 - 9
fs-admin/src/main/java/com/fs/hisStore/task/LiveTask.java

@@ -2,7 +2,9 @@ package com.fs.hisStore.task;
 
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
+import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.DateUtils;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.vo.RedPacketMoneyVO;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
@@ -21,7 +23,11 @@ import com.fs.hisStore.dto.DateComparisonConfigDTO;
 import com.fs.hisStore.enums.ShipperCodeEnum;
 import com.fs.hisStore.mapper.FsStoreProductAttrValueScrmMapper;
 import com.fs.hisStore.param.FsStoreOrderAddTuiMoneyParam;
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.hisStore.service.IFsStoreProductScrmService;
+import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
+import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
+import com.fs.huifuPay.service.HuiFuService;
 import com.fs.live.domain.LiveAfterSales;
 import com.fs.live.domain.LiveOrder;
 import com.fs.live.domain.LiveOrderItem;
@@ -49,8 +55,10 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.time.LocalTime;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
@@ -89,9 +97,6 @@ public class LiveTask {
     @Autowired
     private ILiveOrderPaymentService liveOrderPaymentService;
 
-    @Autowired
-    private IFsUserService userService;
-
     @Autowired
     private ICompanyService companyService;
 
@@ -149,11 +154,53 @@ public class LiveTask {
     @Autowired
     private JdbcTemplate jdbcTemplate;
 
+    @Autowired
+    private HuiFuService huiFuService;
+
+    @Autowired
+    private IFsStoreOrderScrmService orderService;
+
+
+    // 订单银行回调数据丢失补偿
+    public void recoveryBankOrder() {
+        // 查询出来最近三十分钟的订单 待支付 未退款
+        List<LiveOrder> list = liveOrderService.selectBankOrder();
+        if(list == null || list.isEmpty()) return;
+        for (LiveOrder liveOrder : list) {
+            List<LiveOrderPayment> liveOrderPayments = liveOrderPaymentMapper.selectLiveOrderPaymentByOrderId(liveOrder.getOrderId());
+            if(liveOrderPayments == null || liveOrderPayments.isEmpty()) continue;
+            for (LiveOrderPayment payment : liveOrderPayments) {
+                V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
+                request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
+                request.setOrgHfSeqId(payment.getTradeNo());
+                HuiFuQueryOrderResult o = null;
+                try {
+                    o = huiFuService.queryOrder(request);
+                } catch (Exception e) {
+                    log.error("查询失败:"+e.getMessage());
+                    continue;
+                }
+                log.info("汇付返回"+o);
+                if ("00000000".equals(o.getResp_code()) && "S".equals(o.getTrans_stat())) {
+                    String[] order=o.getOrg_req_seq_id().split("-");
+                    if ("live".equals(order[0])) {
+                        liveOrderService.payConfirm(1, null, order[1], o.getOrg_hf_seq_id(), o.getOut_trans_id(), o.getParty_order_id());
+                    }
+                }
+            }
+        }
+    }
+
+
     public void PushErp() throws ParseException {
-//        List<Long> ids = liveOrderMapper.selectOrderIdByNoErp();
-//        for (Long id : ids) {
-//            liveOrderService.createOmsOrder(id);
-//        }
+        List<Long> ids = liveOrderMapper.selectOrderIdByNoErp();
+        if(ids == null) return;
+        if (ids.size() > 50) {
+            ids = ids.subList(0, 50);
+        }
+        for (Long id : ids) {
+            liveOrderService.createOmsOrder(id);
+        }
     }
 
     public void redPacketSubMoney() throws Exception {
@@ -182,9 +229,14 @@ public class LiveTask {
     public void deliveryOp() {
         List<LiveOrder> list = liveOrderService.selectUpdateExpress();
         if(list == null || list.isEmpty()) return;
-        if (list.size() > 100) {
-            list = list.subList(0, 100);
+        if (list.size() > 50) {
+            list = list.subList(0, 50);
+        }
+        Date nowDate = DateUtils.getNowDate();
+        for (LiveOrder order : list) {
+            order.setUpdateTime(nowDate);
         }
+        liveOrderService.batchUpdateTime(list);
         for (LiveOrder order : list) {
             ErpOrderQueryRequert request = new ErpOrderQueryRequert();
             request.setCode(order.getExtendOrderId());

+ 6 - 0
fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java

@@ -165,6 +165,9 @@ public class MallStoreTask
         } else {
             ids = fsStoreOrderMapper.selectFsStoreOrderNoCreateOms();
         }
+        if (!ids.isEmpty() && ids.size() > 50) {
+            ids = ids.subList(0, 50);
+        }
         for (Long id : ids) {
             fsStoreOrderService.createOmsOrder(id);
         }
@@ -204,6 +207,9 @@ public class MallStoreTask
     public void deliveryOp()
     {
         List<FsStoreOrderScrm> list = fsStoreOrderMapper.selectUpdateExpress();
+        if (list != null && list.size() > 50) {
+            list = list.subList(0, 50);
+        }
         for (FsStoreOrderScrm order : list){
             ErpOrderQueryRequert request = new ErpOrderQueryRequert();
             request.setCode(order.getExtendOrderId());

+ 19 - 6
fs-admin/src/main/java/com/fs/live/controller/LiveAfterSalesController.java

@@ -1,5 +1,6 @@
 package com.fs.live.controller;
 
+import cn.hutool.core.date.DateTime;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -11,7 +12,9 @@ import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.framework.web.service.TokenService;
+import com.fs.his.domain.FsStoreAfterSalesLogs;
 import com.fs.his.domain.FsUser;
+import com.fs.his.enums.FsStoreAfterSalesStatusEnum;
 import com.fs.his.service.IFsUserService;
 import com.fs.live.domain.LiveAfterSales;
 import com.fs.live.domain.LiveAfterSalesItem;
@@ -61,7 +64,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 获取售后记录详细信息
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:query')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:query')")
     @GetMapping(value = "/{id}")
     public R getInfo(@PathVariable("id") Long id)
     {
@@ -79,7 +82,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 查询售后记录列表
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:list')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:list')")
     @GetMapping("/list")
     public TableDataInfo list(LiveAfterSalesVo liveAfterSales)
     {
@@ -94,7 +97,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 导出售后记录列表
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:export')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:export')")
     @Log(title = "售后记录", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
     public AjaxResult export(LiveAfterSalesVo liveAfterSales)
@@ -114,7 +117,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 新增售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:add')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:add')")
     @Log(title = "售后记录", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody LiveAfterSales liveAfterSales)
@@ -125,18 +128,28 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 修改售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:edit')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:edit')")
     @Log(title = "售后记录", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody LiveAfterSales liveAfterSales)
     {
+
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        //操作记录
+        LiveAfterSalesLogs logs = new LiveAfterSalesLogs();
+        logs.setChangeTime(new DateTime());
+        logs.setChangeType(2);
+        logs.setOperator(loginUser.getUser().getNickName());
+        logs.setStoreAfterSalesId(liveAfterSales.getId());
+        logs.setChangeMessage(FsStoreAfterSalesStatusEnum.STATUS_2.getDesc());
+        liveAfterSalesLogsService.insertLiveAfterSalesLogs(logs);
         return toAjax(liveAfterSalesService.updateLiveAfterSales(liveAfterSales));
     }
 
     /**
      * 删除售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:remove')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:remove')")
     @Log(title = "售后记录", businessType = BusinessType.DELETE)
 	@DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)

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

@@ -93,7 +93,7 @@ public class LiveController extends BaseController {
     @Log(title = "直播", businessType = BusinessType.DELETE)
     @DeleteMapping("/{liveIds}")
     public AjaxResult remove(@PathVariable Long[] liveIds) {
-        return toAjax(liveService.deleteLiveByLiveIds(liveIds));
+        return toAjax(liveService.deleteLiveByLiveIds(liveIds, new Live()));
     }
 
     @PreAuthorize("@ss.hasPermi('live:live:query')")

+ 43 - 0
fs-admin/src/main/java/com/fs/live/controller/LiveDataController.java

@@ -109,5 +109,48 @@ public class LiveDataController extends BaseController {
         return R.ok(liveViewData);
     }
 
+    /**
+     * 查询直播间详情数据(SQL方式)
+     * @param liveId 直播间ID
+     * @return 详情数据
+     */
+    @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
+    @GetMapping("/getLiveDataDetailBySql")
+    public R getLiveDataDetailBySql(@RequestParam Long liveId) {
+        return liveDataService.getLiveDataDetailBySql(liveId);
+    }
+
+    /**
+     * 查询直播间用户详情列表(SQL方式)
+     * @param liveId 直播间ID
+     * @return 用户详情列表
+     */
+    @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
+    @GetMapping("/getLiveUserDetailListBySql")
+    public R getLiveUserDetailListBySql(@RequestParam Long liveId) {
+        return liveDataService.getLiveUserDetailListBySql(liveId);
+    }
+
+    /**
+     * 查询直播间详情数据(查询数据服务器处理方式)
+     * @param liveId 直播间ID
+     * @return 详情数据
+     */
+    @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
+    @GetMapping("/getLiveDataDetailByServer")
+    public R getLiveDataDetailByServer(@RequestParam Long liveId) {
+        return liveDataService.getLiveDataDetailByServer(liveId);
+    }
+
+    /**
+     * 查询直播间用户详情列表(查询数据服务器处理方式)
+     * @param liveId 直播间ID
+     * @return 用户详情列表
+     */
+    @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
+    @GetMapping("/getLiveUserDetailListByServer")
+    public R getLiveUserDetailListByServer(@RequestParam Long liveId) {
+        return liveDataService.getLiveUserDetailListByServer(liveId);
+    }
 
 }

+ 59 - 13
fs-admin/src/main/java/com/fs/live/controller/LiveOrderController.java

@@ -31,6 +31,8 @@ import com.fs.his.enums.FsStoreOrderLogEnum;
 import com.fs.his.service.IFsDfAccountService;
 import com.fs.his.service.IFsExpressService;
 import com.fs.his.service.IFsUserService;
+import com.fs.his.utils.ConfigUtil;
+import com.fs.hisStore.config.FsErpConfig;
 import com.fs.hisStore.dto.StoreOrderExpressExportDTO;
 import com.fs.hisStore.param.*;
 import com.fs.hisStore.service.IFsExpressScrmService;
@@ -116,6 +118,27 @@ public class LiveOrderController extends BaseController
 
     @Autowired
     private LiveTask liveTask;
+    @Autowired
+    @Qualifier("erpOrderServiceImpl")
+    private IErpOrderService gyOrderService;
+
+    @Autowired
+    @Qualifier("wdtErpOrderServiceImpl")
+    private IErpOrderService wdtOrderService;
+    @Autowired
+    @Qualifier("hzOMSErpOrderServiceImpl")
+    private IErpOrderService hzOMSErpOrderService;
+    @Autowired
+    @Qualifier("dfOrderServiceImpl")
+    private IErpOrderService dfOrderService;
+    @Autowired
+    @Qualifier("k9OrderScrmServiceImpl")
+    private IErpOrderService k9OrderService;
+    @Autowired
+    @Qualifier("JSTErpOrderServiceImpl")
+    private IErpOrderService jSTOrderService;
+    @Autowired
+    private ConfigUtil configUtil;
 
 
 
@@ -181,6 +204,7 @@ public class LiveOrderController extends BaseController
                 vo.setPayDelivery(BigDecimal.ZERO);
                 vo.setBarCode("");
                 vo.setCateName("");
+                vo.setBankTransactionId("");
             }
             vo.setCost(vo.getCostPrice());
 
@@ -205,11 +229,12 @@ public class LiveOrderController extends BaseController
             vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
             // 财务独特字段
             if (loginUser.getPermissions().contains("live:liveOrder:finance") || loginUser.getPermissions().contains("*:*:*")) {
-                vo.setCostPrice(vo.getCostPrice());
+                vo.setCostPrice(vo.getCost());
                 vo.setFPrice(vo.getCostPrice().multiply(BigDecimal.valueOf(Long.parseLong(vo.getTotalNum()))));
             } else {
                 vo.setCostPrice(BigDecimal.ZERO);
                 vo.setFPrice(BigDecimal.ZERO);
+                vo.setBankTransactionId("");
             }
             vo.setCost(vo.getCostPrice());
         }
@@ -435,20 +460,41 @@ public class LiveOrderController extends BaseController
     @PreAuthorize("@ss.hasPermi('live:liveOrder:getEroOrder')")
     @GetMapping("/getEroOrder")
     public R getEroOrder(@RequestParam("extendOrderId") String extendOrderId) {
+        IErpOrderService erpOrderService = getErpService();
         ErpOrderQueryRequert request = new ErpOrderQueryRequert();
         request.setCode(extendOrderId);
-        if(StringUtils.isEmpty(extendOrderId)) return R.error("物流订单ID为空!");
-
-        LiveOrder order = liveOrderService.selectLiveOrderByExtendId(extendOrderId);
-
-        // 根据仓库code找erp
-//        if(com.fs.common.utils.StringUtils.isNotBlank(order.getStoreHouseCode())){
-//            String erp = fsWarehousesMapper.selectErpByCode(order.getStoreHouseCode());
-//            ErpContextHolder.setErpType(erp);
-//        }
-
-//        ErpOrderQueryResponse response = erpOrderService.getOrderLive(request);
-        return R.ok().put("data","123");
+        ErpOrderQueryResponse response = erpOrderService.getLiveOrder(request);
+        return R.ok().put("data",response);
+    }
+    private IErpOrderService getErpService(){
+        //判断是否开启erp
+        IErpOrderService erpOrderService = null;
+        FsErpConfig erpConfig = configUtil.getErpConfig();
+        Integer erpOpen = erpConfig.getErpOpen();
+        if (erpOpen != null && erpOpen == 1) {
+            //判断erp类型
+            Integer erpType = erpConfig.getErpType();
+            if (erpType != null) {
+                if (erpType == 1){
+                    //管易
+                    erpOrderService =  gyOrderService;
+                } else if (erpType == 2){
+                    //旺店通
+                    erpOrderService =  wdtOrderService;
+                } else if (erpType == 3){
+                    //
+                    erpOrderService =  hzOMSErpOrderService;
+                } else if (erpType == 4){
+                    //代服
+                    erpOrderService =  dfOrderService;
+                }else if(erpType == 5){
+                    erpOrderService=jSTOrderService;
+                }else if(erpType == 6){
+                    erpOrderService=k9OrderService;
+                }
+            }
+        }
+        return erpOrderService;
     }
 
     @Log(title = "冻结、解冻佣金", businessType = BusinessType.UPDATE)

+ 6 - 6
fs-company/src/main/java/com/fs/company/controller/live/LiveAfterSalesController.java

@@ -52,7 +52,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 查询售后记录列表
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:list')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:list')")
     @GetMapping("/list")
     public TableDataInfo list(LiveAfterSalesVo liveAfterSales)
     {
@@ -69,7 +69,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 导出售后记录列表
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:export')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:export')")
     @Log(title = "售后记录", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
     public AjaxResult export(LiveAfterSalesVo liveAfterSales)
@@ -89,7 +89,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 获取售后记录详细信息
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:query')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:query')")
     @GetMapping(value = "/{id}")
     public R getInfo(@PathVariable("id") Long id)
     {
@@ -107,7 +107,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 新增售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:add')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:add')")
     @Log(title = "售后记录", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody LiveAfterSales liveAfterSales)
@@ -118,7 +118,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 修改售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:edit')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:edit')")
     @Log(title = "售后记录", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody LiveAfterSales liveAfterSales)
@@ -129,7 +129,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 删除售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:remove')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:remove')")
     @Log(title = "售后记录", businessType = BusinessType.DELETE)
 	@DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)

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

@@ -181,6 +181,9 @@ public class LiveController extends BaseController
     @PutMapping
     public AjaxResult edit(@RequestBody Live live)
     {
+        CompanyUser user = SecurityUtils.getLoginUser().getUser();
+        live.setCompanyUserId(user.getUserId());
+        live.setCompanyId(user.getCompanyId());
         return toAjax(liveService.updateLive(live));
     }
 
@@ -192,7 +195,11 @@ public class LiveController extends BaseController
 	@DeleteMapping("/{liveIds}")
     public AjaxResult remove(@PathVariable Long[] liveIds)
     {
-        return toAjax(liveService.deleteLiveByLiveIds(liveIds));
+        Live live = new Live();
+        CompanyUser user = SecurityUtils.getLoginUser().getUser();
+        live.setCompanyUserId(user.getUserId());
+        live.setCompanyId(user.getCompanyId());
+        return toAjax(liveService.deleteLiveByLiveIds(liveIds, live));
     }
 
     @PreAuthorize("@ss.hasPermi('live:live:query')")

+ 8 - 8
fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java

@@ -924,14 +924,14 @@ public class QwUserController extends BaseController
         return R.ok();
     }
 
-    /**
-     * 重启云主机
-     * @return
-     */
-    @PutMapping("/restartHost")
-    public R restartCloudHost(@RequestParam String serverIp) {
-        return qwUserService.restartCloudHost(serverIp);
-    }
+//    /**
+//     * 重启云主机
+//     * @return
+//     */
+//    @PutMapping("/restartHost")
+//    public R restartCloudHost(@RequestParam String serverIp) {
+//        return qwUserService.restartCloudHost(serverIp);
+//    }
     @PostMapping("/updateSendType")
     public R updateSendType(@RequestBody UpdateSendTypeVo vo) {
         return qwUserService.updateSendType(vo);

+ 45 - 13
fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java

@@ -12,6 +12,8 @@ import com.fs.company.domain.CompanyMiniapp;
 import com.fs.company.service.ICompanyMiniappService;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.course.mapper.FsCoursePlaySourceConfigMapper;
+import com.fs.course.service.IFsCoursePlaySourceConfigService;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.his.domain.FsUser;
 import com.fs.his.mapper.FsUserMapper;
@@ -39,6 +41,7 @@ import java.time.LocalDateTime;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -55,34 +58,63 @@ public class IpadSendServer {
     private final IQwUserVideoService qwUserVideoService;
     private final RedisCache redisCache;
     private final ICompanyMiniappService companyMiniappService;
+    private final IFsCoursePlaySourceConfigService playSourceConfigService;
     private final FsUserMapper fsUserMapper;
     private static final List<String> PROJECT_NAMES = Arrays.asList("济南联志健康", "北京存在文化");
     private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap, Long companyId) {
+        // 发送参数原本的appid
         String appid = content.getMiniprogramAppid();
+        // 判断销售工时ID不为空并且有小程序类型
         if(companyId != null && content.getMiniType() != null){
-            List<CompanyMiniapp> list = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId));
-            List<CompanyMiniapp> collect = list.stream().filter(e -> e.getType().equals(content.getMiniType())).collect(Collectors.toList());
-            if(!collect.isEmpty() && collect.get(0) != null && StringUtils.isNotEmpty(collect.get(0).getAppId())){
-                appid = collect.get(0).getAppId();
+            // 获取销售公司下面绑定的主备小程序,并且根据当前应该发送的主备类型查询出数据
+            List<CompanyMiniapp> list = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId).eq("type", content.getMiniType()));
+            // 判断当前绑定的最新的小程序,并且覆盖以前的值(可以达到实时替换小程序的功能)
+            if(!list.isEmpty() && list.get(0) != null && StringUtils.isNotEmpty(list.get(0).getAppId())){
+                appid = list.get(0).getAppId();
             }
+            // 获取配置文件里面的项目名称
             String signProjectName = SpringUtils.getProperty("cloud_host.company_name");
-            //区分新老用户,新用户发送备用小程序,老用户发送主小程序
+            //区分新老用户,新用户发送备用小程序,老用户发送主小程序,TODO 需要使用的项目在参数里面加上
             if(PROJECT_NAMES.contains(signProjectName)){
                 log.info("ID:{}, qwUserId:{},externalId:{},进入区分发小程序逻辑", vo.getId(), vo.getQwUserId(), vo.getExId());
+                // 判断消息是否是群聊,不是群聊再进去,TODO 当权无法判断群聊是否可以发送对应的小程序
                 if(!vo.isRoom()){
                     log.info("qwUserId:{},externalId:{},不是群聊", vo.getQwUserId(), vo.getExId());
                     try {
+                        // 获取当前外部联系人
                         QwExternalContact qwExternalContact = qwExternalContactMapper.selectOne(new LambdaQueryWrapper<QwExternalContact>().eq(QwExternalContact::getQwUserId,vo.getQwUserId()).eq(QwExternalContact::getExternalUserId,vo.getExId()).last(" limit 1"));
+                        // 当前外部联系是否绑定用户数据,如果数据为空则是没看过小程序的新用户,不用去判断小程序,跳过判断
                         if(qwExternalContact.getFsUserId() != null){
+                            // 根据用户绑定信息去查询用户信息
                             FsUser fsUser = fsUserMapper.selectFsUserByUserId(qwExternalContact.getFsUserId());
-                            LocalDateTime createTime = DateUtil.dateToLocalDateTime(fsUser.getCreateTime());
-                            log.info("ID:{}, qwUserId:{},externalId:{},已绑定小程序,判断时间:{}", vo.getId(), vo.getQwUserId(), vo.getExId(), createTime);
-                            LocalDateTime lastTime = LocalDateTime.of(2025, 11, 6, 23, 59, 59);
-                            int listIndex = createTime.isAfter(lastTime) ? 1 : 0 ;
-                            List<CompanyMiniapp> collect2 = list.stream().filter(e -> e.getType().equals(listIndex)).collect(Collectors.toList());
-                            if(!collect2.isEmpty() && collect2.get(0) != null && StringUtils.isNotEmpty(collect2.get(0).getAppId())){
-                                appid = collect2.get(0).getAppId();
-                                log.info("ID:{}, qwUserId:{},externalId:{},发送小程序:{}", vo.getId(), vo.getQwUserId(), vo.getExId(), appid);
+                            // 判断用户是否有看过其他的小程序多个
+                            if(StringUtils.isNotEmpty(fsUser.getAppId())){
+                                // 获取用户看过的小程序ID集合
+                                List<String> miniAppList = Arrays.asList(fsUser.getAppId().split(","));
+                                // 根据小程序ID查询小程序列表
+                                List<FsCoursePlaySourceConfig> configList = playSourceConfigService.selectByAppIds(miniAppList);
+                                // 筛选出半封禁的小程序数据,得到这个数据然后优先发这个小程序
+                                Optional<FsCoursePlaySourceConfig> optional = configList.stream().filter(e -> e.getStatus() != null && e.getStatus() == 1).findFirst();
+                                // 判断是否找到
+                                if(optional.isPresent()){
+                                    // 找到半封禁并且在appid里面存在证明这个客户是打开过小程序,优先发送这个
+                                    appid = optional.get().getAppid();
+                                }
+                            }else{
+                                // 获取用户的创建时间
+                                LocalDateTime createTime = DateUtil.dateToLocalDateTime(fsUser.getCreateTime());
+                                log.info("ID:{}, qwUserId:{},externalId:{},已绑定小程序,判断时间:{}", vo.getId(), vo.getQwUserId(), vo.getExId(), createTime);
+                                // 这个时间是写死的,目前判断的芳华封禁的时间,可以更具项目调整
+                                LocalDateTime lastTime = LocalDateTime.of(2025, 11, 6, 23, 59, 59);
+                                // 判断客户创建时间是在这个之前还是之后
+                                int listIndex = createTime.isAfter(lastTime) ? 1 : 0 ;
+                                // 获取公司里面的主备小程序,根据用户创建时间来发送主备,如果创建时间大于上看的时间就发送备用小程序,如果小于就发送主要小程序
+                                List<CompanyMiniapp> collect2 = list.stream().filter(e -> e.getType().equals(listIndex)).collect(Collectors.toList());
+                                // 判断是否获取到了配置
+                                if(!collect2.isEmpty() && collect2.get(0) != null && StringUtils.isNotEmpty(collect2.get(0).getAppId())){
+                                    appid = collect2.get(0).getAppId();
+                                    log.info("ID:{}, qwUserId:{},externalId:{},发送小程序:{}", vo.getId(), vo.getQwUserId(), vo.getExId(), appid);
+                                }
                             }
                         }else{
                             log.info("ID:{}, qwUserId:{},externalId:{},未绑定小程序用户", vo.getId(), vo.getQwUserId(), vo.getExId());

+ 5 - 5
fs-service/pom.xml

@@ -285,11 +285,11 @@
         </dependency>
 
         <!-- 移动云ECS SDK -->
-        <dependency>
-            <groupId>com.ecloud.sdk</groupId>
-            <artifactId>ecloud-sdk-ecs</artifactId>
-            <version>1.1.26</version>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>com.ecloud.sdk</groupId>-->
+<!--            <artifactId>ecloud-sdk-ecs</artifactId>-->
+<!--            <version>1.1.26</version>-->
+<!--        </dependency>-->
 
     </dependencies>
 

+ 13 - 13
fs-service/src/main/java/com/cloud/host/CloudHostConfig.java

@@ -1,7 +1,7 @@
 package com.cloud.host;
 
-import com.ecloud.sdk.config.Config;
-import com.ecloud.sdk.ecs.v1.Client;
+//import com.ecloud.sdk.config.Config;
+//import com.ecloud.sdk.ecs.v1.Client;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
@@ -14,16 +14,16 @@ public class CloudHostConfig {
      * @param poolId
      * @return Client
      */
-    public static Client createClient(String accessKey, String secretKey, String poolId) {
-        Config config = new Config();
-        config.setAccessKey(accessKey);
-        config.setSecretKey(secretKey);
-        config.setPoolId(poolId);
-        // 默认连接超时时间为60秒
-        config.setConnectTimeout(60);
-        // 默认响应超时时间为120秒
-        config.setReadTimeout(120);
-        return new Client(config);
-    }
+//    public static Client createClient(String accessKey, String secretKey, String poolId) {
+//        Config config = new Config();
+//        config.setAccessKey(accessKey);
+//        config.setSecretKey(secretKey);
+//        config.setPoolId(poolId);
+//        // 默认连接超时时间为60秒
+//        config.setConnectTimeout(60);
+//        // 默认响应超时时间为120秒
+//        config.setReadTimeout(120);
+//        return new Client(config);
+//    }
 
 }

+ 4 - 1
fs-service/src/main/java/com/fs/company/mapper/CompanyMapper.java

@@ -155,7 +155,10 @@ public interface CompanyMapper
 
 
     @Select({"<script> " +
-            "select c.*,cu.user_name,qu.used_num FROM company c LEFT JOIN company_user cu ON c.user_id =cu.user_id  " +
+            "select c.*,cu.user_name,qu.used_num," +
+            "CASE WHEN JSON_VALID(t1.config_value) THEN t1.config_value->>'$.mchId' ELSE NULL END as mchId" +
+            " FROM company c LEFT JOIN company_user cu ON c.user_id =cu.user_id  " +
+            " left join company_config t1 on t1.config_key = 'redPacket:config' and t1.company_id = c.company_id " +
             "LEFT JOIN (select company_id, count(id) as used_num from qw_user where server_id is not null and server_status = 1 group by company_id) qu ON qu.company_id = c.company_id " +
             "where c.is_del=0 " +
             "            <if test=\"companyName != null  and companyName != ''\"> and c.company_name like concat('%', #{companyName}, '%')</if>\n" +

+ 5 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyVO.java

@@ -99,4 +99,9 @@ public class CompanyVO implements Serializable
     private BigDecimal redPackageMoney;
 
     private Integer liveShow;
+
+    /**
+     * 新增展示商户号(分公司配置时有值)
+     */
+    private String mchId;
 }

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

@@ -13,4 +13,6 @@ public interface IFsCoursePlaySourceConfigService extends IService<FsCoursePlayS
      * 查询点播配置列表
      */
     List<FsCoursePlaySourceConfigVO> selectCoursePlaySourceConfigVOListByMap(Map<String, Object> params);
+
+    List<FsCoursePlaySourceConfig> selectByAppIds(List<String> miniAppList);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCoursePlaySourceConfigServiceImpl.java

@@ -1,5 +1,6 @@
 package com.fs.course.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.mapper.FsCoursePlaySourceConfigMapper;
@@ -24,4 +25,9 @@ public class FsCoursePlaySourceConfigServiceImpl extends ServiceImpl<FsCoursePla
     public List<FsCoursePlaySourceConfigVO> selectCoursePlaySourceConfigVOListByMap(Map<String, Object> params) {
         return baseMapper.selectCoursePlaySourceConfigVOListByMap(params);
     }
+
+    @Override
+    public List<FsCoursePlaySourceConfig> selectByAppIds(List<String> miniAppList) {
+        return baseMapper.selectList(new QueryWrapper<FsCoursePlaySourceConfig>().in("appid", miniAppList));
+    }
 }

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

@@ -458,8 +458,234 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         // 设置 Redis 记录的过期时间(例如 5 分钟)
         redisCache.expire(redisKey, 300, TimeUnit.SECONDS);
     }
-
     @Override
+    public R isAddKf(FsUserCourseVideoAddKfUParam param) {
+
+        logger.info("zyp \n【判断添加客服】:{}",param);
+        //查询用户
+        FsUser fsUser = fsUserMapper.selectFsUserByUserId(param.getUserId());
+
+        //用户不存在唤起重新授权
+        if (fsUser==null){
+            return R.error(401,"会员不存在");
+        }
+        if (fsUser.getStatus()==0){
+            return R.error("会员被停用,无权限,请联系客服!");
+        }
+
+        if (param.getIsRoom()!=null&& param.getIsRoom()== 1) {
+            // 当 isRoom 为 1 时走 handleRoom
+            return handleRoom(param,fsUser);
+        }
+
+
+        if (param.getLinkType()!=null&&param.getLinkType()==1){
+            if (param.getLinkId()!=null){
+                // 从数据库中查找短链对应的真实链接
+                FsCourseLink courseLink = courseLinkMapper.selectFsCourseLinkByLinkId(param.getLinkId());
+                if (courseLink != null) {
+                    // 获取当前时间
+                    Date currentTime = new Date();
+                    // 获取链接的更新时间(假设这个字段代表过期时间)
+                    Date updateTime = courseLink.getUpdateTime();
+
+                    // 判断是否过期
+                    if (currentTime.after(updateTime)) {
+                        // 链接已过期
+                        return R.error("链接已过期");
+                    }else {
+                        return R.ok();
+                    }
+                }
+            }
+            return R.error("链接过期");
+        }
+        FsCourseLink courseLink = courseLinkMapper.selectFsCourseLinkByLink(param.getLink());
+//        if(StringUtils.isNotEmpty(courseLink.getUNo())){
+//            if(!courseLink.getUNo().equals(param.getUNo())){
+//                log.error("看课链接uNO不一致,linkUNo:{}, param-uNo:{}", courseLink.getUNo(), param.getUNo());
+//            }else{
+//                log.info("看课链接uNO一致,uno:{}", courseLink.getUNo());
+//            }
+//        }
+
+        //未注册提示
+        String noRegisterMsg = "由于您还未完成注册,请联系伴学助手完成注册即可观看!";
+        //非独属链接提示
+        String noMemberMsg = "此链接已被绑定,请联系伴学助手领取您的专属链接,专属链接请勿分享哦!";
+
+        // 群聊逻辑
+        if(courseLink != null && StringUtils.isNotEmpty(courseLink.getChatId())){
+            QwGroupChat qwGroupChat = qwGroupChatMapper.selectQwGroupChatByChatId(courseLink.getChatId());
+            if(qwGroupChat == null){
+                return R.error("群参数异常!");
+            }
+            SopUserLogsInfo sopUserLogsInfo =  new SopUserLogsInfo();
+            sopUserLogsInfo.setChatId(courseLink.getChatId());
+            List<QwGroupChatUser> qwGroupChatUsers = qwGroupChatUserMapper.selectByChatId(sopUserLogsInfo);
+            if(qwGroupChatUsers == null || qwGroupChatUsers.isEmpty()){
+                return R.error("群参数异常!");
+            }
+            QwExternalContact qwExternalContact =
+                    qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>()
+                            .eq("user_id", qwGroupChat.getOwner())
+                            .eq("fs_user_id", param.getUserId())
+                            .eq("corp_id", param.getCorpId())
+                            .eq("status",0));
+            if(qwExternalContact==null){
+                return R.error(noRegisterMsg);
+            }
+            if(qwGroupChatUsers.stream().noneMatch(e -> e.getUserId().equals(qwExternalContact.getExternalUserId()))){
+                log.error("客户不在群:{},里面:{}", qwGroupChat.getChatId(), qwExternalContact.getExternalUserId());
+                return R.error(noRegisterMsg);
+            }
+            Long qwExternalId = qwExternalContact.getId();
+            log.info("外部联系人数据:{}", qwExternalContact);
+            addCompanyCompanyFsUser(param);
+            FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(qwExternalId, param.getVideoId(),param.getQwUserId());
+            if (log==null ){
+                return R.error(noRegisterMsg);
+            }
+            boolean isDebug = false;
+            //判断外部联系人有没有绑定userId
+            if (qwExternalContact.getFsUserId()!=null && isDebug){
+                //有客户有小程序id  但 登录的小程序id和根据外部联系人id查出来的小程序id不一致
+                if (!qwExternalContact.getFsUserId().equals(param.getUserId())) {
+                    return R.error(noMemberMsg);
+                }
+                List<QwExternalContact> qwExternalContacts = qwExternalContactMapper.selectQwExternalContactByMiniUserId(param.getUserId());
+                //匹配客户公司id
+                if (qwExternalContacts.stream().noneMatch(contact -> contact.getCorpId().equals(param.getCorpId()))){
+                    return R.error(noRegisterMsg);
+                }
+
+                //看课记录中userId为0绑定userId
+                if (log.getUserId()==null||log.getUserId().equals(0L) || !log.getUserId().equals(param.getUserId())){
+                    log.setUserId(param.getUserId());
+                }
+
+                log.setUpdateTime(new Date());
+                fsUserCompanyBindService.bindFsUser(fsUser.getUserId(), qwExternalId, log.getLogId());
+                courseWatchLogMapper.updateFsCourseWatchLog(log);
+
+            }else {
+                //没绑定fsUser直接绑定fsUser
+                QwExternalContact contact = new QwExternalContact();
+                contact.setId(qwExternalId);
+                contact.setFsUserId(param.getUserId());
+                qwExternalContactMapper.updateQwExternalContact(contact);
+                FsUser user = new FsUser();
+                user.setUserId(fsUser.getUserId());
+                user.setIsAddQw(1);
+                fsUserMapper.updateFsUser(user);
+                //绑定上之后 更新观看记录
+                //看课记录中userId为0绑定userId
+                log.setUserId(param.getUserId());
+                log.setUpdateTime(new Date());
+                courseWatchLogMapper.updateFsCourseWatchLog(log);
+            }
+            fsUserCompanyBindService.bindFsUser(fsUser.getUserId(), qwExternalId, log.getLogId());
+            return R.error(567,"群聊通用链接").put("qwExternalId", qwExternalContact.getId());
+        }
+        else{
+            Long qwExternalId = param.getQwExternalId();
+
+            FsCourseWatchLog log=new FsCourseWatchLog();
+
+            //如果是官方的则查真正的外部联系人id
+            if (param.getLinkType()!=null&&param.getLinkType()==5) {
+
+                log = courseWatchLogMapper.selectFsCourseWatchLogByCourseSopIdAndVideoId(param.getUserId(), param.getVideoId(), param.getQwUserId());
+                if (log == null) {
+                    return R.error(noRegisterMsg);
+                }else {
+                    qwExternalId=log.getQwExternalContactId();
+                }
+
+            }else {
+                log = courseWatchLogMapper.getWatchCourseVideoByExt(qwExternalId, param.getVideoId(),param.getQwUserId());
+                if (log==null ){
+                    return R.error(noRegisterMsg);
+                }
+            }
+
+
+            //查询是否有添加客服
+            QwExternalContact externalContact = qwExternalContactMapper.selectQwExternalContactById(qwExternalId);
+
+            //用小程序id查询外部联系人
+            List<QwExternalContact> qwExternalContacts = qwExternalContactMapper.selectQwExternalContactByMiniUserId(param.getUserId());
+
+
+            //如果查不出来客户信息,加好友
+            if(externalContact==null){
+                return R.error(noRegisterMsg);
+            }
+
+            //2025.04.10 绑定公司码
+            addCompanyCompanyFsUser(param);
+
+            //判断外部联系人有没有绑定userId
+            if (externalContact.getFsUserId()!=null){
+                //有客户有小程序id  但 登录的小程序id和根据外部联系人id查出来的小程序id不一致
+                if (!externalContact.getFsUserId().equals(param.getUserId())) {
+                    return R.error(noMemberMsg);
+                }
+                //匹配客户公司id
+                if (qwExternalContacts.stream().noneMatch(contact -> contact.getCorpId().equals(param.getCorpId()))){
+                    return R.error(noMemberMsg);
+                }
+
+                //看课记录中userId为0绑定userId
+                if (log.getUserId()==null||log.getUserId().equals(0L) || !log.getUserId().equals(param.getUserId())){
+                    log.setUserId(param.getUserId());
+                }
+
+                log.setUpdateTime(new Date());
+                courseWatchLogMapper.updateFsCourseWatchLog(log);
+
+                iSopUserLogsInfoService.updateSopUserInfoByExternalId(qwExternalId,param.getUserId());
+
+                fsUserCompanyBindService.bindFsUser(fsUser.getUserId(), qwExternalId, log.getLogId());
+                if (param.getLinkType()!=null&&param.getLinkType()==5){
+                    FsCourseLink fsCourseLink = fsCourseLinkMapper.selectExpireLinkByQwExternalId(param.getQwUserId(), param.getVideoId(), qwExternalId);
+                    return R.error(566,"官方群发通用链接").put("courseLink",fsCourseLink);
+                }
+                return R.ok();
+
+            }else {
+                //没绑定fsUser直接绑定fsUser
+                QwExternalContact contact = new QwExternalContact();
+                contact.setId(qwExternalId);
+                contact.setFsUserId(param.getUserId());
+//            adHtmlClickLogService.upload(contact.getState(), AdUploadType.REGISTERED, e -> contact.setUploadRegisterStatus(1));
+                qwExternalContactMapper.updateQwExternalContact(contact);
+
+                iSopUserLogsInfoService.updateSopUserInfoByExternalId(qwExternalId,param.getUserId());
+
+                FsUser user = new FsUser();
+                user.setUserId(fsUser.getUserId());
+                user.setIsAddQw(1);
+                fsUserMapper.updateFsUser(user);
+
+                //绑定上之后 更新观看记录
+                //看课记录中userId为0绑定userId
+                if (log.getUserId()==null||log.getUserId().equals(0L) || !log.getUserId().equals(param.getUserId())){
+                    log.setUserId(param.getUserId());
+                }
+
+                log.setUpdateTime(new Date());
+                courseWatchLogMapper.updateFsCourseWatchLog(log);
+                fsUserCompanyBindService.bindFsUser(fsUser.getUserId(), qwExternalId, log.getLogId());
+                if (param.getLinkType()!=null&&param.getLinkType()==5){
+                    FsCourseLink fsCourseLink = fsCourseLinkMapper.selectExpireLinkByQwExternalId(param.getQwUserId(), param.getVideoId(), qwExternalId);
+                    return R.error(566,"官方群发通用链接").put("courseLink",fsCourseLink);
+                }
+                return R.ok();
+            }
+        }
+    }
+  /*  @Override
     public R isAddKf(FsUserCourseVideoAddKfUParam param) {
         logger.info("【判断添加客服】:{}", param);
         //查询用户
@@ -553,7 +779,7 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         }
 
     }
-
+*/
     private R handleQwRoom(FsUserCourseVideoAddKfUParam param, FsUser user) {
         FsCourseLink courseLink = courseLinkMapper.selectFsCourseLinkByLink(param.getLink());
         String msg = "<div style=\"color: red;margin-bottom: 15px;font-weight: bold;\">本课程为群会员独享<br>请长按二维码</div>\n" +

+ 4 - 4
fs-service/src/main/java/com/fs/course/vo/FsCourseProductOrderVO.java

@@ -46,8 +46,8 @@ public class FsCourseProductOrderVO extends BaseEntity {
     private Long isPay;
 
     /** 支付时间 */
-    @JsonFormat(pattern = "yyyy-MM-dd")
-    @Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
     private Date payTime;
 
     /** 支付方式 微信 */
@@ -58,8 +58,8 @@ public class FsCourseProductOrderVO extends BaseEntity {
     private Long status;
 
     /** 申请退款时间 */
-    @JsonFormat(pattern = "yyyy-MM-dd")
-    @Excel(name = "申请退款时间", width = 30, dateFormat = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "申请退款时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
     private Date refundTime;
 
     /** 申请退款理由 */

+ 6 - 4
fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.fs.common.utils.StringUtils;
 import com.fs.erp.constant.AfterSalesOrderStatusEnum;
 import com.fs.erp.constant.ErpQueryOrderStatusEnum;
 import com.fs.erp.constant.OrderStatusEnum;
@@ -44,10 +45,7 @@ import org.springframework.util.CollectionUtils;
 
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -543,6 +541,7 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         // 1. 构建查询请求DTO
         OrderQueryRequestDTO requestDTO = new OrderQueryRequestDTO();
         requestDTO.setOIds(Collections.singletonList(Long.valueOf(param.getCode())));
+        requestDTO.setOrderItemFlds(Arrays.asList("status"));
 
         // 2. 调用ERP服务查询订单
         OrderQueryResponseDTO query = jstErpHttpService.query(requestDTO);
@@ -742,6 +741,9 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
                     .sum();
             erpOrder.setQty(totalQty);
         }
+        if (StringUtils.isNotEmpty(order.getStatus()) && "Cancelled".equals(order.getStatus())) {
+            erpOrder.setCancle(true);
+        }
 
         // 设置金额相关信息
         erpOrder.setAmount(order.getAmount() != null ? order.getAmount().doubleValue() : null);

+ 3 - 0
fs-service/src/main/java/com/fs/his/domain/FsAdv.java

@@ -58,4 +58,7 @@ public class FsAdv extends BaseEntity
     /** 宣传活动ID **/
     private Long activeId;
 
+    /** 跳转小程序原始id **/
+    private String originalId;
+
 }

+ 3 - 0
fs-service/src/main/java/com/fs/his/domain/FsUserInformationCollection.java

@@ -101,8 +101,11 @@ public class FsUserInformationCollection extends BaseEntity{
     private String patientName;
 
     private Long companyId;
+    //药师id
     private Long doctorType2Id;
+    //药师确认
     private Integer doctorType2Confirm;
+    //药师签名
     private String doctorType2Sign;
 
 }

+ 0 - 1
fs-service/src/main/java/com/fs/his/mapper/FsAdvMapper.java

@@ -62,7 +62,6 @@ public interface FsAdvMapper
 
     public int deleteFsAdvByAdvIds(String[] advIds);
 
-    @Select("select * from fs_adv where status = 1 and adv_type = 11 and show_type = 4 ")
     List<FsAdv> selectFsAppAdvList();
     @Select({"<script> " +
             "select * from fs_adv "+

+ 3 - 4
fs-service/src/main/java/com/fs/his/mapper/FsPackageOrderMapper.java

@@ -8,10 +8,7 @@ import com.fs.his.domain.FsStorePayment;
 import com.fs.his.dto.PackageOrderDTO;
 import com.fs.his.param.FsPackageOrderListUParam;
 import com.fs.his.param.FsPackageOrderParam;
-import com.fs.his.vo.FsPackageOrderExcelVO;
-import com.fs.his.vo.FsPackageOrderListUVO;
-import com.fs.his.vo.FsPackageOrderListVO;
-import com.fs.his.vo.FsPackageOrderVO;
+import com.fs.his.vo.*;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
@@ -241,4 +238,6 @@ public interface FsPackageOrderMapper
     List<PackageOrderDTO> getNewOrder();
 
     List<FsPackageOrder> selectOutTimeOrderList(@Param("unPayTime") Integer unPayTime);
+
+    FsUserInfoCollectionAndStoreOrderVo selectInformationCollectionByStoreOrderId(@Param("orderId")Long orderId);
 }

+ 3 - 4
fs-service/src/main/java/com/fs/his/service/IFsPackageOrderService.java

@@ -10,10 +10,7 @@ import com.fs.his.domain.FsPackageOrder;
 import com.fs.his.domain.FsStorePayment;
 import com.fs.his.dto.PackageOrderDTO;
 import com.fs.his.param.*;
-import com.fs.his.vo.FsPackageOrderExcelVO;
-import com.fs.his.vo.FsPackageOrderListUVO;
-import com.fs.his.vo.FsPackageOrderListVO;
-import com.fs.his.vo.FsPackageOrderVO;
+import com.fs.his.vo.*;
 import io.swagger.models.auth.In;
 
 /**
@@ -146,4 +143,6 @@ public interface IFsPackageOrderService
     List<FsPackageOrder> selectOutTimeOrderList(Integer unPayTime);
 
     R editPatientImages(Long orderId, String imagesList);
+
+    FsUserInfoCollectionAndStoreOrderVo selectInformationCollectionByStoreOrderId(Long orderId);
 }

+ 29 - 12
fs-service/src/main/java/com/fs/his/service/impl/FsPackageOrderServiceImpl.java

@@ -46,10 +46,7 @@ import com.fs.his.service.*;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.utils.HttpUtil;
 import com.fs.his.utils.PhoneUtil;
-import com.fs.his.vo.FsPackageOrderExcelVO;
-import com.fs.his.vo.FsPackageOrderListUVO;
-import com.fs.his.vo.FsPackageOrderListVO;
-import com.fs.his.vo.FsPackageOrderVO;
+import com.fs.his.vo.*;
 import com.fs.huifuPay.domain.HuiFuCreateOrder;
 import com.fs.huifuPay.domain.HuifuCreateOrderResult;
 import com.fs.huifuPay.sdk.opps.core.utils.HuiFuUtils;
@@ -627,14 +624,7 @@ public class FsPackageOrderServiceImpl implements IFsPackageOrderService
         order.setCycle(fsPackage.getCycle());
         order.setCostDiscountMoney(fsPackage.getTotalCostPrice().subtract(fsPackage.getTotalPrice()));
         if(fsPackageOrderMapper.insertFsPackageOrder(order)>0){
-            if (param.getIsUserInformation()!=null && param.getIsUserInformation() == 1){
-                FsUserInformationCollection fsUserInformationCollection = fsUserInformationCollectionService.selectFsUserInformationCollectionById(param.getUserInformationId());
-                if (fsUserInformationCollection != null) {
-                    fsUserInformationCollection.setPackageOrderCode(order.getOrderSn());
-                    fsUserInformationCollection.setPackageOrderId(order.getOrderId());
-                    fsUserInformationCollectionService.updatePackageOrderCode(fsUserInformationCollection);
-                }
-            }
+            updateFsUserInformationCollectionByPackageOrder(param, order);
             String redisKey = String.valueOf(StrUtil.format("{}{}", FsConstants.REDIS_PACKAGE_ORDER_UNPAY, order.getOrderId()));
             redisCache.setCacheObject(redisKey,order.getOrderId(),30, TimeUnit.MINUTES);
             return R.ok().put("order",order);
@@ -644,6 +634,23 @@ public class FsPackageOrderServiceImpl implements IFsPackageOrderService
         }
     }
 
+    private void updateFsUserInformationCollectionByPackageOrder(FsPackageOrderCreateParam param, FsPackageOrder order) {
+        if (param.getIsUserInformation()!=null && param.getIsUserInformation() == 1){
+            FsUserInformationCollection fsUserInformationCollection = fsUserInformationCollectionService.selectFsUserInformationCollectionById(param.getUserInformationId());
+            if (fsUserInformationCollection != null) {
+                fsUserInformationCollection.setPackageOrderCode(order.getOrderSn());
+                fsUserInformationCollection.setPackageOrderId(order.getOrderId());
+                fsUserInformationCollectionService.updatePackageOrderCode(fsUserInformationCollection);
+                //更新订单表
+                FsPackageOrder temp = new FsPackageOrder();
+                temp.setOrderId(order.getOrderId());
+                //添加前缀 区分信息采集订单
+                temp.setOrderSn("info"+ order.getOrderSn());
+                fsPackageOrderMapper.updateFsPackageOrder(temp);
+            }
+        }
+    }
+
     @Override
     @Transactional
     public R payOrder(FsPackageOrderPayParam param) {
@@ -1870,4 +1877,14 @@ public class FsPackageOrderServiceImpl implements IFsPackageOrderService
         }
         return R.ok();
     }
+
+    /**
+     * 根据storeOrderId 查询信息采集信息
+     * @param orderId
+     * @return FsUserInformationCollection
+     */
+    @Override
+    public FsUserInfoCollectionAndStoreOrderVo selectInformationCollectionByStoreOrderId(Long orderId) {
+        return fsPackageOrderMapper.selectInformationCollectionByStoreOrderId(orderId);
+    }
 }

+ 6 - 4
fs-service/src/main/java/com/fs/his/service/impl/FsPackageServiceImpl.java

@@ -5,10 +5,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.net.URL;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONUtil;
@@ -336,6 +333,11 @@ public class FsPackageServiceImpl implements IFsPackageService {
         if(list != null && !list.isEmpty()){
             try {
                 for (FsPackage fsPackage : list) {
+                    //重命名
+                    fsPackage.setPackageName(fsPackage.getPackageName() + " - 副本");
+                    fsPackage.setSecondName(fsPackage.getSecondName() + " - 副本");
+                    fsPackage.setCreateTime(new Date());
+                    fsPackage.setUpdateTime(new Date());
                     fsPackageMapper.insertFsPackage(fsPackage);
                 }
             } catch (Exception e) {

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

@@ -21,6 +21,7 @@ import com.fs.his.service.IFsPrescribeDrugService;
 import com.fs.his.service.IFsPrescribeService;
 import com.fs.his.service.IFsStoreOrderService;
 import com.fs.his.utils.ConfigUtil;
+import com.fs.his.utils.IdCardUtil;
 import com.fs.his.utils.qrcode.QRCodeUtils;
 import com.fs.his.vo.*;
 import com.fs.im.dto.MsgCustomDTO;
@@ -517,7 +518,12 @@ public class FsPrescribeServiceImpl implements IFsPrescribeService
             return null;
         }
         long currentTimeMillis = System.currentTimeMillis();
-        long ageInMillis = currentTimeMillis - patJson.getBirthday();
+        Long birthday = patJson.getBirthday();
+        if (birthday == null) {
+            birthday = IdCardUtil.getBirthdayFromIdCard(patJson.getIdCard());
+            patJson.setBirthday(birthday);
+        }
+        long ageInMillis = currentTimeMillis - birthday;
         long ageInSeconds = ageInMillis / 1000;
         long ageInYears = ageInSeconds / (365 * 24 * 3600);
         fsPrescribe.setPatientAge(ageInYears+"");
@@ -525,7 +531,7 @@ public class FsPrescribeServiceImpl implements IFsPrescribeService
         fsPrescribe.setPatientName(patJson.getPatientName());
         fsPrescribe.setPatientTel(patJson.getMobile());
         fsPrescribe.setPatientGender(patJson.getSex().toString());
-        fsPrescribe.setPatientBirthday(new SimpleDateFormat("yyyy-MM-dd").format(new Date(patJson.getBirthday())));
+        fsPrescribe.setPatientBirthday(new SimpleDateFormat("yyyy-MM-dd").format(new Date(birthday)));
         fsPrescribe.setDoctorId(packageOrder.getDoctorId());
         FsDoctor fsDoctor = doctorMapper.selectFsDoctorByDoctorId(packageOrder.getDoctorId());
         if (fsDoctor==null){

+ 23 - 6
fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java

@@ -1134,6 +1134,7 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
         try {
             //信息采集 发送药师im
             FsUserInformationCollection fsUserInformationCollectionParam = new FsUserInformationCollection();
+            fsUserInformationCollectionParam.setUserId(packageOrder.getUserId());
             fsUserInformationCollectionParam.setPackageOrderId(packageOrder.getOrderId());
             fsUserInformationCollectionParam.setPackageOrderCode(packageOrder.getOrderSn());
             List<FsUserInformationCollection> fsUserInformationCollections = fsUserInformationCollectionMapper.selectFsUserInformationCollectionList(fsUserInformationCollectionParam);
@@ -1187,6 +1188,11 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
                 if (vo.getDeliverySn() == null || vo.getDeliverySn().isEmpty()) {
                     throw new CustomException("快递单号为空");
                 }
+                //判断是否是信息采集订单 且 药师已经确认
+                FsUserInfoCollectionAndStoreOrderVo info = fsPackageOrderMapper.selectInformationCollectionByStoreOrderId(o.getOrderId());
+                if (info != null&&info.getDoctorType2Confirm()!=1) {
+                    throw new CustomException("订单编号"+ info.getStoreOrderCode() +",手动发货导入发货失败,失败原因-信息采集订单,药师暂未确认!");
+                }
                 FsStoreOrder fsStoreOrder = new FsStoreOrder();
 
                 fsStoreOrder.setDeliverySn(vo.getDeliverySn());
@@ -3956,30 +3962,41 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
                 FsStoreOrder param = new FsStoreOrder(); //修改订单的参数
                 param.setOrderCode(vo.getOrderCode());
                 param.setOrderId(o.getOrderId());
-                if ("6".equals(vo.getStatus())) {
+                String inputStatus = vo.getStatus();
+                if ("6".equals(inputStatus)) {
                     failureNum++;
                     String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 导入失败:";
                     failureMsg.append(msg).append("该状态不支持修改为待推送");
                     continue;
                 }
-                if ("-1".equals(vo.getStatus())) {
+                if ("-1".equals(inputStatus)) {
                     failureNum++;
                     String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 导入失败:";
                     failureMsg.append(msg).append("该状态不支持修改为退款中,需要手动申请退款");
                     continue;
                 }
-                if ("-2".equals(vo.getStatus())) {
+                if ("-2".equals(inputStatus)) {
                     failureNum++;
                     String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 导入失败:";
                     failureMsg.append(msg).append("该状态不支持修改为退款中,需要审核完成退款");
                     continue;
                 }
+                //发货
+                if ("2".equals(inputStatus)){
+                    //判断是否是信息采集订单 且 药师已经确认
+                    FsUserInfoCollectionAndStoreOrderVo info = fsPackageOrderMapper.selectInformationCollectionByStoreOrderId(o.getOrderId());
+                    if (info != null&&info.getDoctorType2Confirm()!=1) {
+                        String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 导入失败:";
+                        failureMsg.append(msg).append("失败原因-信息采集订单,药师暂未确认!");
+                        continue;
+                    }
+                }
 
                 Integer status = o.getStatus();
 
-                if (StringUtils.isNotBlank(vo.getStatus())){
-                    param.setStatus(Integer.valueOf(vo.getStatus()));
-                    status = Integer.valueOf(vo.getStatus());
+                if (StringUtils.isNotBlank(inputStatus)){
+                    param.setStatus(Integer.valueOf(inputStatus));
+                    status = Integer.valueOf(inputStatus);
                 }
                 /**
                  * 地址和电话仅待付款和待推送可以修改

+ 20 - 0
fs-service/src/main/java/com/fs/his/utils/IdCardUtil.java

@@ -140,4 +140,24 @@ public class IdCardUtil {
             throw new RuntimeException("匹配请求异常", e);
         }
     }
+
+    /**
+     * 从身份证号码提取出生日期时间戳
+     * @param idCard 身份证号码
+     * @return 出生日期时间戳(毫秒)
+     */
+    public static Long getBirthdayFromIdCard(String idCard) {
+        if (idCard == null || idCard.length() != 18) {
+            throw new IllegalArgumentException("身份证号码格式不正确");
+        }
+
+        try {
+            String birthdayStr = idCard.substring(6, 14);
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+            Date birthday = sdf.parse(birthdayStr);
+            return birthday.getTime();
+        } catch (Exception e) {
+            throw new IllegalArgumentException("身份证出生日期解析失败", e);
+        }
+    }
 }

+ 12 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserInfoCollectionAndStoreOrderVo.java

@@ -0,0 +1,12 @@
+package com.fs.his.vo;
+
+import com.fs.his.domain.FsUserInformationCollection;
+import lombok.Data;
+
+
+@Data
+public class FsUserInfoCollectionAndStoreOrderVo extends FsUserInformationCollection {
+    private String storeOrderCode;
+
+
+}

+ 3 - 1
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java

@@ -76,7 +76,9 @@ public interface FsStoreOrderItemScrmMapper
 
     @Select({"<script> " +
             "select i.*,o.user_id,psps.cost,o.pay_postage,o.total_num,o.status,fspcs.cate_name, o.real_name,o.user_phone,o.user_address,o.create_time,o.pay_time,o.delivery_sn,o.delivery_name,o.delivery_id, c.company_name ,cu.nick_name as company_user_nick_name ,cu.phonenumber as company_usere_phonenumber,o.upload_time ,CASE WHEN o.certificates IS NULL OR o.certificates = '' THEN 0 ELSE 1 END AS is_upload   " +
-            " ,p.title as package_name,cts.name as scheduleName from fs_store_order_item_scrm i left join fs_store_order_scrm o on o.id=i.order_id left join fs_user u on o.user_id=u.user_id  " +
+            " ,p.title as package_name,cts.name as scheduleName, os.bank_transaction_id as bankTransactionId from fs_store_order_item_scrm i left join fs_store_order_scrm o on o.id=i.order_id" +
+            " left join fs_store_payment_scrm os on os.business_order_id = o.id " +
+            " left join fs_user u on o.user_id=u.user_id  " +
             " left join fs_store_product_package_scrm p on o.package_id=p.package_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 company_tcm_schedule cts on cts.id = o.schedule_id " +
             " left join fs_store_product_scrm psps on i.product_id=psps.product_id left join fs_store_product_category_scrm fspcs on fspcs.cate_id=psps.cate_id " +
             "where 1=1 " +

+ 12 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStorePaymentScrmMapper.java

@@ -9,6 +9,7 @@ import com.fs.hisStore.domain.FsStorePaymentScrm;
 import com.fs.hisStore.param.FsStorePaymentParam;
 import com.fs.hisStore.param.FsStoreStatisticsParam;
 import com.fs.hisStore.vo.FsStorePaymentStatisticsVO;
+import com.fs.hisStore.vo.FsStorePaymentUsetVo;
 import com.fs.hisStore.vo.FsStorePaymentVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
@@ -371,4 +372,15 @@ public interface FsStorePaymentScrmMapper
 
     @Select("select * from fs_store_payment_scrm where pay_code=#{payCode}")
     FsStorePaymentScrm selectFsStorePaymentByPaymentCode(String payCode);
+
+    /**
+     * 获取查询用户支付信息
+     * @return list
+     **/
+    List<FsStorePaymentUsetVo> getPaymentUsetInfoList();
+
+    /**
+     * 批量更新发货状态
+     * **/
+    void batchUpadte(@Param("list") List<String> list);
 }

+ 8 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsUserScrmMapper.java

@@ -20,6 +20,7 @@ import com.fs.hisStore.domain.FsUserWatchStatisticsScrm;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
+import org.springframework.security.core.parameters.P;
 
 import java.math.BigDecimal;
 import java.util.List;
@@ -335,4 +336,11 @@ public interface FsUserScrmMapper
      */
     @Update("update fs_user set order_count = order_count + 1, total_amount = IFNULL(total_amount, 0) + #{amount} where user_id = #{userId}")
     void updateUserOrderCountAndAmount(@Param("userId") Long userId, @Param("amount") BigDecimal amount);
+
+    /**
+     * 累计成交总额
+     * @param payMoney 成交金额
+     */
+    @Update("update fs_user set total_amount = IFNULL(total_amount, 0) + #{payMoney} where user_id = #{userId}")
+    void incPayMoney(@Param("payMoney") BigDecimal payMoney, @Param("userId") Long userId);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderParam.java

@@ -113,4 +113,10 @@ public class FsStoreOrderParam extends BaseEntity implements Serializable
 
     private String appId;
 
+    //银行交易流水号
+    private String bankTransactionId;
+
+    private Integer pageNum;
+    private Integer pageSize;
+
 }

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

@@ -116,4 +116,9 @@ public interface IFsStorePaymentScrmService
     R getWxaCodeByPayment(FsStorePaymentGetWxaCodeParam param);
 
     R paymentByWxaCode(FsStorePaymentPayParam param);
+
+    /**
+     * 批量导入更新微信订单发货状态
+     * **/
+    R oneClickShipping();
 }

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

@@ -28,6 +28,7 @@ import com.fs.hisStore.vo.FsUserTuiVO;
 import com.fs.hisStore.vo.h5.*;
 import com.github.pagehelper.PageInfo;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 
@@ -292,4 +293,5 @@ public interface IFsUserScrmService
 
     void handleFsUserWx(FsUserScrm user, LoginMaWxParam param, WxMaJscode2SessionResult session);
 
+    void incPayMoney(BigDecimal payMoney, Long userId);
 }

+ 5 - 1
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java

@@ -784,7 +784,11 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
                         FsHfpayConfigMapper fsHfpayConfigMapper = SpringUtils.getBean(FsHfpayConfigMapper.class);
                         if (payment.getAppId() != null) {
                             FsHfpayConfig fsHfpayConfig = fsHfpayConfigMapper.selectByAppId(payment.getAppId());
-                            huifuId = fsHfpayConfig.getHuifuId();
+                            if (fsHfpayConfig == null){
+                                huifuId = fsPayConfig.getHuifuId();
+                            }else {
+                                huifuId = fsHfpayConfig.getHuifuId();
+                            }
                         } else {
                             if (("益善缘".equals(cloudHostProper.getCompanyName()))) {
 

+ 9 - 5
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -2406,7 +2406,11 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                         FsHfpayConfigMapper fsHfpayConfigMapper = SpringUtils.getBean(FsHfpayConfigMapper.class);
                         if (payment.getAppId() != null) {
                             FsHfpayConfig fsHfpayConfig = fsHfpayConfigMapper.selectByAppId(payment.getAppId());
-                            huifuId = fsHfpayConfig.getHuifuId();
+                            if (fsHfpayConfig == null){
+                                huifuId = fsPayConfig.getHuifuId();
+                            }else {
+                                huifuId = fsHfpayConfig.getHuifuId();
+                            }
                         } else {
                             if (("益善缘".equals(cloudHostProper.getCompanyName()))) {
 
@@ -3041,10 +3045,10 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                     TemplateDTO templateDTO = TemplateDTO.builder()
                             .number(num)
                             .price(price)
-                            .first(shippingTemplatesRegion.getFirst().doubleValue())
-                            .firstPrice(shippingTemplatesRegion.getFirstPrice())
-                            .continues(shippingTemplatesRegion.getContinues().doubleValue())
-                            .continuePrice(shippingTemplatesRegion.getContinuePrice())
+                            .first(ObjectUtil.isNotEmpty(shippingTemplatesRegion)?shippingTemplatesRegion.getFirst().doubleValue():0.0)
+                            .firstPrice(ObjectUtil.isNotEmpty(shippingTemplatesRegion)?shippingTemplatesRegion.getFirstPrice():new BigDecimal(0.0))
+                            .continues(ObjectUtil.isNotEmpty(shippingTemplatesRegion)?shippingTemplatesRegion.getContinues().doubleValue():0.0)
+                            .continuePrice(ObjectUtil.isNotEmpty(shippingTemplatesRegion)?shippingTemplatesRegion.getContinuePrice():new BigDecimal(0.0))
                             .tempId(tempId)
                             .cityId(cityId)
                             .build();

+ 93 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStorePaymentScrmServiceImpl.java

@@ -3,11 +3,16 @@ package com.fs.hisStore.service.impl;
 
 import java.math.BigDecimal;
 
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
+import java.util.stream.Collectors;
 
 
 import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
+import cn.binarywang.wx.miniapp.bean.shop.request.shipping.*;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
@@ -41,8 +46,10 @@ import com.fs.his.service.IFsUserService;
 import com.fs.his.service.IFsUserWxService;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.utils.HttpUtil;
+import com.fs.hisStore.config.StoreConfig;
 import com.fs.hisStore.enums.SysConfigEnum;
 import com.fs.hisStore.param.*;
+import com.fs.hisStore.vo.FsStorePaymentUsetVo;
 import com.fs.huifuPay.domain.HuiFuCreateOrder;
 import com.fs.huifuPay.domain.HuifuCreateOrderResult;
 import com.fs.huifuPay.sdk.opps.core.utils.HuiFuUtils;
@@ -53,6 +60,7 @@ import com.fs.hisStore.vo.FsStorePaymentStatisticsVO;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 import com.fs.system.service.ISysConfigService;
+import com.fs.utils.TwelveDigitSnowflake;
 import com.fs.wx.miniapp.config.WxMaProperties;
 import com.fs.hisStore.domain.FsUserScrm;
 import com.fs.hisStore.service.IFsUserScrmService;
@@ -71,6 +79,7 @@ import com.github.binarywang.wxpay.service.WxPayService;
 import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
 import com.google.common.reflect.TypeToken;
 import com.google.gson.Gson;
+import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -91,6 +100,7 @@ import javax.servlet.http.HttpServletRequest;
  * @date 2022-06-20
  */
 @Service
+@Slf4j
 public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
 {
     Logger logger = LoggerFactory.getLogger(getClass());
@@ -949,4 +959,87 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
             return R.error(result.getResp_desc());
         }
     }
+
+    @Override
+    public R oneClickShipping() {
+        try {
+            StringBuilder builder = new StringBuilder();
+            //获取商城配置
+            String json = configService.selectConfigByKey("store.config");
+            StoreConfig config = JSONUtil.toBean(json, StoreConfig.class);
+            //验证是否开启微信发货
+            if (config.getIsWeChatShipping() != null && config.getIsWeChatShipping()) {
+                //获取支付信息
+                List<FsStorePaymentUsetVo> paymentList = fsStorePaymentMapper.getPaymentUsetInfoList();
+                if (paymentList.isEmpty()) {
+                    return R.ok("操作成功,暂无同步订单信息!");
+                }
+                String uploadTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"))
+                        .format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+                Map<String, List<FsStorePaymentUsetVo>> paymentUsetVoMap = paymentList.stream().collect(Collectors.groupingBy(FsStorePaymentUsetVo::getAppId));
+                for (Map.Entry<String, List<FsStorePaymentUsetVo>> entry : paymentUsetVoMap.entrySet()) {
+                    List<String> successList = new ArrayList<>();
+                    String appId = entry.getKey();
+                    List<FsStorePaymentUsetVo> userPayments = entry.getValue();
+                    final WxMaService wxService = WxMaConfiguration.getMaService(appId);
+                    if (!userPayments.isEmpty()) {
+                        for (FsStorePaymentUsetVo v : userPayments) {
+                            // 上传物流信息到微信
+                            if (uploadShippingInfoToWechat(wxService, v, uploadTime)) {
+                                successList.add(v.getBankTransactionId());
+                            }else {
+                                successList.add(v.getBankTransactionId());
+                            }
+                        }
+                        //批量更新数据
+                        if (!successList.isEmpty()) {
+                            fsStorePaymentMapper.batchUpadte(successList);
+                        }
+                    }
+                }
+            }
+
+
+            return R.ok(builder.toString().equals("") ? "操作成功!" : builder.toString());
+        } catch (Exception e) {
+            log.error("导入发货单快递信息失败", e);
+            return R.error("导入失败:" + e.getMessage());
+        }
+    }
+
+    private boolean uploadShippingInfoToWechat(WxMaService wxService,
+                                               FsStorePaymentUsetVo dto,
+                                               String uploadTime) {
+        try {
+            WxMaOrderShippingInfoUploadRequest request = new WxMaOrderShippingInfoUploadRequest();
+            OrderKeyBean orderKeyBean = new OrderKeyBean();
+            orderKeyBean.setOrderNumberType(2);
+            orderKeyBean.setTransactionId(dto.getBankTransactionId());//交易订单号ID
+            request.setOrderKey(orderKeyBean);
+            request.setDeliveryMode(1);
+            request.setLogisticsType(4);
+            List<ShippingListBean> shippingList = new ArrayList<>();
+            ShippingListBean shippingListBean = new ShippingListBean();
+            //默认物品信息
+            shippingListBean.setTrackingNo(String.valueOf(new TwelveDigitSnowflake(1).nextId()));
+            shippingListBean.setExpressCompany("FS");
+            shippingListBean.setItemDesc("默认商品");
+            ContactBean contactBean = new ContactBean();
+            contactBean.setReceiverContact(dto.getPhone());
+            shippingListBean.setContact(contactBean);
+
+            shippingList.add(shippingListBean);
+            request.setShippingList(shippingList);
+            request.setUploadTime(uploadTime);
+            // 设置支付者信息
+            PayerBean payerBean = new PayerBean();
+            payerBean.setOpenid(dto.getOpenId());
+            request.setPayer(payerBean);
+            // 上传物流信息
+            return wxService.getWxMaOrderShippingService().upload(request).getErrCode() == 0;
+        } catch (Exception e) {
+            log.error("上传物流信息到微信失败,订单号: {}", dto.getBankTransactionId(), e);
+            return false;
+        }
+    }
 }

+ 285 - 1
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java

@@ -31,7 +31,18 @@ import com.fs.hisStore.domain.*;
 import com.fs.hisStore.enums.CompanyEnum;
 import com.fs.hisStore.mapper.*;
 import com.fs.hisStore.utils.StoreAuditLogUtil;
+import com.fs.live.domain.Live;
 import com.fs.live.domain.LiveGoods;
+import com.fs.live.domain.LiveAutoTask;
+import com.fs.live.domain.LiveLotteryConf;
+import com.fs.live.domain.LiveLotteryProductConf;
+import com.fs.live.mapper.LiveLotteryProductConfMapper;
+import com.fs.live.mapper.LiveMapper;
+import com.fs.live.service.ILiveService;
+import com.fs.live.service.ILiveGoodsService;
+import com.fs.live.service.ILiveAutoTaskService;
+import com.fs.live.service.ILiveLotteryConfService;
+import com.fs.live.vo.LiveGoodsVo;
 import com.fs.statis.dto.ModifyMoreDTO;
 import com.fs.hisStore.dto.ProductArrtDTO;
 import com.fs.hisStore.dto.ProductAttrCountDto;
@@ -40,10 +51,12 @@ import com.fs.hisStore.param.FsStoreProductQueryParam;
 import com.fs.hisStore.service.IFsStoreProductAttrValueScrmService;
 import com.fs.hisStore.vo.*;
 import com.fs.statis.dto.ProductAuditDTO;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import com.fs.hisStore.service.IFsStoreProductScrmService;
 import org.springframework.transaction.annotation.Propagation;
@@ -57,8 +70,10 @@ import org.springframework.transaction.annotation.Transactional;
  */
 @Service
 @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
+@Slf4j
 public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
 {
+
     @Autowired
     private FsStoreProductScrmMapper fsStoreProductMapper;
     @Autowired
@@ -102,6 +117,24 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
     @Autowired
     private FsStoreProductCategoryScrmMapper fsStoreProductCategoryScrmMapper;
 
+    @Autowired
+    private ILiveService liveService;
+
+    @Autowired
+    private LiveMapper liveMapper;
+
+    @Autowired
+    private ILiveGoodsService liveGoodsService;
+
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
+
+    @Autowired
+    private ILiveLotteryConfService liveLotteryConfService;
+
+    @Autowired
+    private LiveLotteryProductConfMapper liveLotteryProductConfMapper;
+
     /**
      * 查询商品
      *
@@ -169,7 +202,258 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
     public int deleteFsStoreProductByIds(Long[] productIds)
     {
         storeAuditLogUtil.addBatchAuditArray(productIds, "", "");
-        return fsStoreProductMapper.deleteFsStoreProductByIds(productIds);
+
+        int result = fsStoreProductMapper.deleteFsStoreProductByIds(productIds);
+
+        // 异步处理商品删除联动逻辑
+        if (result > 0) {
+            try {
+                log.info("批量删除商品:{}", productIds);
+                handleProductDeleteAsync(productIds);
+            } catch (Exception e) {
+                log.error("商品删除异步处理失败:{}", e.getMessage());
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 异步处理商品删除联动逻辑
+     * 删除所有未直播、直播中和直播回放的直播间中关联的商品、定时任务、抽奖等
+     *
+     * @param productIds 被删除的商品ID数组
+     */
+    @Async
+    public void handleProductDeleteAsync(Long[] productIds) {
+        try {
+            log.info("开始异步处理商品删除联动,商品IDs: {}", Arrays.toString(productIds));
+
+            // 查询所有未直播(1)、直播中(2)和直播回放(4)的直播间
+            // 使用 LiveMapper 查询状态为1,2,4的直播间(包括所有类型)
+            List<Live> allLiveList = liveMapper.liveListAll();
+            // 同时查询其他类型的直播间(如果liveListAll只查询类型2,3,我们需要补充查询类型1的)
+            // 为了确保完整性,我们使用 selectLiveList 方法查询所有状态为1,2,4的直播间
+            Live queryLive = new Live();
+            List<Live> allLiveList2 = liveService.selectLiveList(queryLive);
+            // 合并并去重,过滤出状态为1,2,4的直播间
+            Set<Long> liveIdSet = new HashSet<>();
+            List<Live> targetLiveList = new ArrayList<>();
+            for (Live live : allLiveList) {
+                if (live.getLiveId() != null && !liveIdSet.contains(live.getLiveId())) {
+                    liveIdSet.add(live.getLiveId());
+                    targetLiveList.add(live);
+                }
+            }
+            for (Live live : allLiveList2) {
+                if (live.getStatus() != null && (live.getStatus() == 1 || live.getStatus() == 2 || live.getStatus() == 4)) {
+                    if (live.getLiveId() != null && !liveIdSet.contains(live.getLiveId())) {
+                        liveIdSet.add(live.getLiveId());
+                        targetLiveList.add(live);
+                    }
+                }
+            }
+
+            if (targetLiveList.isEmpty()) {
+                log.info("没有找到需要处理的直播间");
+                return;
+            }
+
+            log.info("找到 {} 个需要处理的直播间", targetLiveList.size());
+
+            // 遍历每个被删除的商品
+            for (Long productId : productIds) {
+                processProductDeleteForLives(productId, targetLiveList);
+            }
+
+            log.info("商品删除联动处理完成");
+        } catch (Exception e) {
+            log.error("异步处理商品删除联动失败", e);
+        }
+    }
+
+    /**
+     * 处理单个商品在指定直播间列表中的删除联动
+     *
+     * @param productId 商品ID
+     * @param liveList 直播间列表
+     */
+    private void processProductDeleteForLives(Long productId, List<Live> liveList) {
+        for (Live live : liveList) {
+            try {
+                Long liveId = live.getLiveId();
+                if (liveId == null) {
+                    continue;
+                }
+
+                // 1. 删除直播商品
+                deleteLiveGoodsByProductId(productId, liveId);
+
+                // 2. 删除直播定时任务(直播上下架、直播卡片)
+                deleteLiveAutoTasksByProductId(productId, liveId);
+
+                // 3. 删除直播抽奖(产品关联被删除的商品)
+                deleteLiveLotteryProductConfByProductId(productId, liveId);
+
+                // 4. 删除直播定时任务(抽奖,里面关联了这个商品的)
+                deleteLiveAutoTasksByLotteryProductId(productId, liveId);
+
+            } catch (Exception e) {
+                log.error("处理直播间 {} 的商品 {} 删除联动失败", live.getLiveId(), productId, e);
+            }
+        }
+    }
+
+    /**
+     * 删除直播商品
+     *
+     * @param productId 商品ID
+     * @param liveId 直播间ID
+     */
+    private void deleteLiveGoodsByProductId(Long productId, Long liveId) {
+        try {
+            LiveGoods queryGoods = new LiveGoods();
+            queryGoods.setProductId(productId);
+            queryGoods.setLiveId(liveId);
+            List<LiveGoods> goodsList = liveGoodsService.selectLiveGoodsList(queryGoods);
+
+            if (!goodsList.isEmpty()) {
+                Long[] goodsIds = goodsList.stream()
+                        .map(LiveGoods::getGoodsId)
+                        .toArray(Long[]::new);
+                liveGoodsService.deleteLiveGoodsByGoodsIds(goodsIds);
+                log.info("删除直播间 {} 的商品 {} 相关的直播商品 {} 个", liveId, productId, goodsIds.length);
+            }
+        } catch (Exception e) {
+            log.error("删除直播商品失败,productId: {}, liveId: {}", productId, liveId, e);
+        }
+    }
+
+    /**
+     * 删除直播定时任务(直播上下架、直播卡片)
+     *
+     * @param productId 商品ID
+     * @param liveId 直播间ID
+     */
+    private void deleteLiveAutoTasksByProductId(Long productId, Long liveId) {
+        try {
+            LiveAutoTask queryTask = new LiveAutoTask();
+            queryTask.setLiveId(liveId);
+            List<LiveAutoTask> taskList = liveAutoTaskService.selectLiveAutoTaskList(queryTask);
+
+            List<Long> taskIdsToDelete = new ArrayList<>();
+
+            for (LiveAutoTask task : taskList) {
+                // 任务类型:1-定时推送卡片商品 6-自动上下架
+                if (task.getTaskType() != null && (task.getTaskType() == 1L || task.getTaskType() == 6L)) {
+                    try {
+                        if (task.getTaskType() == 1L) {
+                            // 商品推荐任务
+                            LiveGoodsVo liveGoodsVo = JSON.parseObject(task.getContent(), LiveGoodsVo.class);
+                            if (liveGoodsVo != null && productId.equals(liveGoodsVo.getProductId())) {
+                                taskIdsToDelete.add(task.getId());
+                            }
+                        } else if (task.getTaskType() == 6L) {
+                            // 商品上下架任务
+                            JSONObject jsonObject = JSON.parseObject(task.getContent());
+                            Long taskProductId = jsonObject.getLong("productId");
+                            if (taskProductId != null && productId.equals(taskProductId)) {
+                                taskIdsToDelete.add(task.getId());
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.warn("解析自动化任务content失败,taskId: {}, error: {}", task.getId(), e.getMessage());
+                    }
+                }
+            }
+
+            if (!taskIdsToDelete.isEmpty()) {
+                Long[] ids = taskIdsToDelete.toArray(new Long[0]);
+                liveAutoTaskService.deleteLiveAutoTaskByIds(ids);
+                log.info("删除直播间 {} 的商品 {} 相关的定时任务 {} 个", liveId, productId, taskIdsToDelete.size());
+            }
+        } catch (Exception e) {
+            log.error("删除直播定时任务失败,productId: {}, liveId: {}", productId, liveId, e);
+        }
+    }
+
+    /**
+     * 删除直播抽奖(产品关联被删除的商品)
+     *
+     * @param productId 商品ID
+     * @param liveId 直播间ID
+     */
+    private void deleteLiveLotteryProductConfByProductId(Long productId, Long liveId) {
+        try {
+            LiveLotteryProductConf queryConf = new LiveLotteryProductConf();
+            queryConf.setProductId(productId);
+            queryConf.setLiveId(liveId);
+            List<LiveLotteryProductConf> confList = liveLotteryProductConfMapper.selectLiveLotteryProductConfList(queryConf);
+
+            if (!confList.isEmpty()) {
+                Long[] ids = confList.stream()
+                        .map(LiveLotteryProductConf::getId)
+                        .toArray(Long[]::new);
+                liveLotteryProductConfMapper.deleteLiveLotteryProductConfByIds(ids);
+                log.info("删除直播间 {} 的商品 {} 相关的抽奖商品配置 {} 个", liveId, productId, ids.length);
+            }
+        } catch (Exception e) {
+            log.error("删除直播抽奖商品配置失败,productId: {}, liveId: {}", productId, liveId, e);
+        }
+    }
+
+    /**
+     * 删除直播定时任务(抽奖,里面关联了这个商品的)
+     *
+     * @param productId 商品ID
+     * @param liveId 直播间ID
+     */
+    private void deleteLiveAutoTasksByLotteryProductId(Long productId, Long liveId) {
+        try {
+            // 查询该直播间下所有包含该商品的抽奖配置
+            LiveLotteryProductConf queryConf = new LiveLotteryProductConf();
+            queryConf.setProductId(productId);
+            queryConf.setLiveId(liveId);
+            List<LiveLotteryProductConf> confList = liveLotteryProductConfMapper.selectLiveLotteryProductConfList(queryConf);
+
+            if (confList.isEmpty()) {
+                return;
+            }
+
+            // 获取所有相关的抽奖ID
+            Set<Long> lotteryIds = confList.stream()
+                    .map(LiveLotteryProductConf::getLotteryId)
+                    .collect(Collectors.toSet());
+
+            // 查询该直播间的所有定时任务
+            LiveAutoTask queryTask = new LiveAutoTask();
+            queryTask.setLiveId(liveId);
+            List<LiveAutoTask> taskList = liveAutoTaskService.selectLiveAutoTaskList(queryTask);
+
+            List<Long> taskIdsToDelete = new ArrayList<>();
+
+            for (LiveAutoTask task : taskList) {
+                // 任务类型:4-抽奖
+                if (task.getTaskType() != null && task.getTaskType() == 4L) {
+                    try {
+                        LiveLotteryConf liveLotteryConf = JSON.parseObject(task.getContent(), LiveLotteryConf.class);
+                        if (liveLotteryConf != null && lotteryIds.contains(liveLotteryConf.getLotteryId())) {
+                            taskIdsToDelete.add(task.getId());
+                        }
+                    } catch (Exception e) {
+                        log.warn("解析自动化任务content失败,taskId: {}, error: {}", task.getId(), e.getMessage());
+                    }
+                }
+            }
+
+            if (!taskIdsToDelete.isEmpty()) {
+                Long[] ids = taskIdsToDelete.toArray(new Long[0]);
+                liveAutoTaskService.deleteLiveAutoTaskByIds(ids);
+                log.info("删除直播间 {} 的商品 {} 相关的抽奖定时任务 {} 个", liveId, productId, taskIdsToDelete.size());
+            }
+        } catch (Exception e) {
+            log.error("删除抽奖定时任务失败,productId: {}, liveId: {}", productId, liveId, e);
+        }
     }
 
     /**

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

@@ -1157,4 +1157,9 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
             throw new RuntimeException(e);
         }
     }
+
+    @Override
+    public void incPayMoney(BigDecimal payMoney, Long userId) {
+        fsUserMapper.incPayMoney(payMoney, userId);
+    }
 }

+ 3 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportVO.java

@@ -107,5 +107,8 @@ public class FsStoreOrderItemExportVO implements Serializable
     @Excel(name = "归属档期")
     private String scheduleName;
 
+    //银行交易流水号
+    @Excel(name = "银行交易流水号")
+    private String bankTransactionId;
 
 }

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

@@ -267,5 +267,9 @@ public class FsStoreOrderVO implements Serializable
     //erp推送账号
     private String erpAccount;
 
+    /** 银行交易流水号 */
+    @Excel(name = "银行交易流水号")
+    private String bankTransactionId;
+
 
 }

+ 20 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStorePaymentUsetVo.java

@@ -0,0 +1,20 @@
+package com.fs.hisStore.vo;
+
+import lombok.Data;
+
+@Data
+public class FsStorePaymentUsetVo {
+    //交易订单号
+    private String bankTransactionId;
+
+    //openId
+    private String openId;
+
+    //手机号
+    private String phone;
+
+    /**
+     * appId
+     * **/
+    private String appId;
+}

+ 23 - 23
fs-service/src/main/java/com/fs/im/service/impl/OpenIMServiceImpl.java

@@ -1582,32 +1582,32 @@ public class OpenIMServiceImpl implements OpenIMService {
     public OpenImResponseDTO sendUserInformation(Long userId,Long doctorId,Long userInformationId) {
         try {
             accountCheck("U"+userId,"1");
-        OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
-        ObjectMapper objectMapper = new ObjectMapper();
-        openImMsgDTO.setSendID("U"+userId);
-        openImMsgDTO.setRecvID("D"+doctorId);
-        //110为im的自定义消息类型
-        openImMsgDTO.setContentType(110);
-        openImMsgDTO.setSenderPlatformID(5);
-        openImMsgDTO.setSessionType(1);
-        OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
-        //content.setContent(ext);
-        PayloadDTO payload = new PayloadDTO();
-        payload.setDescription("userInformation");
-        payload.setData("userInformation");
-        PayloadDTO.Extension extension = new PayloadDTO.Extension();
+            OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+            ObjectMapper objectMapper = new ObjectMapper();
+            openImMsgDTO.setSendID("U"+userId);
+            openImMsgDTO.setRecvID("D"+doctorId);
+            //110为im的自定义消息类型
+            openImMsgDTO.setContentType(110);
+            openImMsgDTO.setSenderPlatformID(5);
+            openImMsgDTO.setSessionType(1);
+            OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+            //content.setContent(ext);
+            PayloadDTO payload = new PayloadDTO();
+            payload.setDescription("userInformation");
+            payload.setData("userInformation");
+            PayloadDTO.Extension extension = new PayloadDTO.Extension();
 
-        extension.setDoctorId(doctorId);
-        extension.setUserInformationId(userInformationId);
-        payload.setExtension(extension);
-        OpenImMsgDTO.ImData imData = new OpenImMsgDTO.ImData();
+            extension.setDoctorId(doctorId);
+            extension.setUserInformationId(userInformationId);
+            payload.setExtension(extension);
+            OpenImMsgDTO.ImData imData = new OpenImMsgDTO.ImData();
 
-        imData.setPayload(payload);
+            imData.setPayload(payload);
 
-        String imJson = objectMapper.writeValueAsString(imData);
-        content.setData(imJson);
-        openImMsgDTO.setContent(content);
-        return openIMSendMsg(openImMsgDTO);
+            String imJson = objectMapper.writeValueAsString(imData);
+            content.setData(imJson);
+            openImMsgDTO.setContent(content);
+            return openIMSendMsg(openImMsgDTO);
         } catch (Exception e) {
             e.printStackTrace();
             return null;

+ 26 - 4
fs-service/src/main/java/com/fs/ipad/IpadSendUtils.java

@@ -3,6 +3,7 @@ package com.fs.ipad;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.core.redis.RedisCacheT;
 import com.fs.common.exception.base.BaseException;
@@ -17,6 +18,7 @@ import com.fs.qw.domain.QwGroupChat;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwGroupChatMapper;
+import com.fs.qw.mapper.QwUserMapper;
 import com.fs.sop.domain.QwSop;
 import com.fs.sop.domain.SopUserLogs;
 import com.fs.sop.mapper.QwSopMapper;
@@ -53,8 +55,10 @@ public class IpadSendUtils {
     private final QwSopMapper qwSopMapper;
     private final QwExternalContactMapper qwExternalContactMapper;
     private final SopUserLogsMapper sopUserLogsMapper;
+    private final QwUserMapper qwUserMapper;
     private final RedisCache redisCacheUrl;
     private final String FILE_KEY = "ipad:upload:";
+    private final String USER_KEY = "ipad:user:";
 
     /**
      * 发送卡片消息
@@ -241,11 +245,22 @@ public class IpadSendUtils {
      * @return 返回的userid
      */
     private Long chatIds(BaseVo vo){
+        // 实时查询群聊信息
+        QwGroupChat qwGroupChat = qwGroupChatMapper.selectQwGroupChatByChatId(vo.getExId());
+        if(qwGroupChat == null){
+            throw new BaseException("未找到群聊,检查SOP任务数据");
+        }
+        if(qwGroupChat.getRoomid() != null){
+            return qwGroupChat.getRoomid();
+        }
+        // 找到对应的企业微信
+        QwUser qwUser = qwUserMapper.selectOne(new QueryWrapper<QwUser>().eq("corp_id", qwGroupChat.getCorpId()).eq("ipad_status", 1).last(" limit 1"));
         WxWorkChatIdToRoomIdDTO tdo = new WxWorkChatIdToRoomIdDTO();
-        tdo.setChatid(vo.getExId());
-        tdo.setCorpid(vo.getCorpId());
-        tdo.setUuid(vo.getUuid());
-        WxWorkResponseDTO<WxWorkChatIdToRoomIdResp> result = wxWorkService.ChatIdToRoomId(tdo, vo.getServerId());
+        // 重新组装数据
+        tdo.setChatid(qwGroupChat.getChatId());
+//        tdo.setCorpid(vo.getCorpId());
+        tdo.setUuid(qwUser.getUid());
+        WxWorkResponseDTO<WxWorkChatIdToRoomIdResp> result = wxWorkService.ChatIdToRoomId(tdo, qwUser.getServerId());
         if(result.getErrcode() != 0){
             throw new BaseException(result.getErrmsg());
         }
@@ -254,6 +269,13 @@ public class IpadSendUtils {
             log.error("未找到群聊数据,请求数据:{},返回数据:{}", JSON.toJSONString(tdo), JSON.toJSONString(result));
             throw new BaseException("未找到群聊:" + vo.getId());
         }
+        try {
+            qwGroupChat.setRoomid(data.getRoom_id());
+            qwGroupChatMapper.updateQwGroupChat(qwGroupChat);
+            redisCache.setCacheObject(USER_KEY + vo.getExId(), data.getRoom_id().toString());
+        }catch (Exception e){
+            log.error("存储群ID失败", e);
+        }
         return data.getRoom_id();
     }
 

+ 2 - 0
fs-service/src/main/java/com/fs/live/domain/Live.java

@@ -124,5 +124,7 @@ public class   Live extends BaseEntity {
     private Date createTime;
     private String companyName;
     private Long fileSize;
+    private Long videoFileSize;
+    private Long videoDuration;
     private Integer globalVisible;
 }

+ 3 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveCouponMapper.java

@@ -125,4 +125,7 @@ public interface LiveCouponMapper
     @Select("select * from live_coupon_issue_relation where live_id = #{liveId}")
     List<LiveCouponIssueRelation> selectCouponRelationByLiveId(@Param("liveId") Long liveId);
 
+    @Select("select * from live_coupon_issue_relation where coupon_issue_id = #{couponIssueId}")
+    List<LiveCouponIssueRelation> selectCouponRelationByCouponIssueId(@Param("couponIssueId") Long couponIssueId);
+
 }

+ 16 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveDataMapper.java

@@ -3,8 +3,10 @@ package com.fs.live.mapper;
 
 import com.fs.live.domain.LiveData;
 import com.fs.live.vo.LiveDashBoardDataVo;
+import com.fs.live.vo.LiveDataDetailVo;
 import com.fs.live.vo.LiveDataListVo;
 import com.fs.live.vo.LiveDataStatisticsVo;
+import com.fs.live.vo.LiveUserDetailVo;
 import com.fs.live.vo.RecentLiveDataVo;
 import com.fs.live.vo.TrendDataVO;
 import org.apache.ibatis.annotations.Param;
@@ -153,4 +155,18 @@ public interface LiveDataMapper {
      * @return 列表数据
      */
     List<LiveDataListVo> selectLiveDataListByLiveIds(@Param("liveIds") List<Long> liveIds);
+
+    /**
+     * 查询直播间详情数据(SQL方式)
+     * @param liveId 直播间ID
+     * @return 详情数据
+     */
+    LiveDataDetailVo selectLiveDataDetailBySql(@Param("liveId") Long liveId);
+
+    /**
+     * 查询直播间用户详情列表(SQL方式)
+     * @param liveId 直播间ID
+     * @return 用户详情列表
+     */
+    List<LiveUserDetailVo> selectLiveUserDetailListBySql(@Param("liveId") Long liveId);
 }

+ 8 - 5
fs-service/src/main/java/com/fs/live/mapper/LiveMapper.java

@@ -2,6 +2,7 @@ package com.fs.live.mapper;
 
 
 import com.fs.live.domain.Live;
+import com.fs.live.param.LiveDataParam;
 import com.fs.live.vo.LiveListVo;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
@@ -75,7 +76,7 @@ public interface LiveMapper
      * @param liveIds 需要删除的数据主键集合
      * @return 结果
      */
-    public int deleteLiveByLiveIds(Long[] liveIds);
+    public int deleteLiveByLiveIds(@Param("liveIds") Long[] liveIds,@Param("live") Live live);
 
     List<Live> liveList();
 
@@ -135,15 +136,17 @@ public interface LiveMapper
 
     @Select({"<script>" +
             "select * from live where 1=1 " +
-            " <if test='companyId!=null' > and company_id = #{companyId} </if> and live_type IN (1,2, 3) AND status IN (3, 4) AND is_del = 0 and is_audit=1" +
+            " <if test='param.companyId!=null' > and company_id = #{param.companyId} </if> and live_type IN (1,2, 3) AND status IN (3, 4) AND is_del = 0 and is_audit=1 " +
+            " <if test='param.liveName!=null' > and live_name like concat('%' ,#{param.liveName},'%') </if>" +
             " </script>"})
-    List<Live> listLiveData(@Param("companyId")Long companyId);
+    List<Live> listLiveData(@Param("param") LiveDataParam param);
 
     @Select({"<script>" +
             "select count(1) from live where 1=1 " +
-            " <if test='companyId!=null' > and company_id = #{companyId} </if> and live_type IN (1,2, 3) AND status IN (3, 4) AND is_del = 0 and is_audit=1" +
+            " <if test='param.companyId!=null' > and company_id = #{param.companyId} </if> and live_type IN (1,2, 3) AND status IN (3, 4) AND is_del = 0 and is_audit=1" +
+            " <if test='param.liveName!=null' > and live_name like concat('%' ,#{param.liveName},'%') </if>" +
             " </script>"})
-    int listLiveDataCount(@Param("companyId") Long companyId);
+    int listLiveDataCount(@Param("param") LiveDataParam param);
 
 
     List<Live> liveShowList(@Param("companyIds") List<Long> companyIds);

+ 18 - 3
fs-service/src/main/java/com/fs/live/mapper/LiveOrderMapper.java

@@ -104,10 +104,10 @@ public interface LiveOrderMapper {
     @Select("select * from live_order where `status` = 3 AND TIMESTAMPDIFF(HOUR, start_time, NOW()) >= 48  ")
     List<LiveOrder> selectLiveOrderByFinish();
 
-    @Select("select * from live_order where `status` = 1 and extend_order_id is not null and delivery_sn is null ")
+    @Select("select * from live_order where `status` = 1 and extend_order_id is not null and (delivery_sn is null or delivery_code = '') and refund_status = 0 and is_pay = 1 order by update_time ")
     List<LiveOrder> selectUpdateExpress();
 
-    @Select("select order_id from live_order where `status` = 2")
+    @Select("select order_id from live_order where `status` = 2 and delivery_code is not null and delivery_sn is not null and is_pay = 1")
     List<Long> selectSyncExpressIds();
 
     @Select("select order_id from live_order where `status` = 1 and extend_order_id is null and is_pay='1'")
@@ -369,7 +369,7 @@ public interface LiveOrderMapper {
     int batchUpdateErpByOrderIds(@Param("maps")ArrayList<Map<String, String>> maps);
 
     @Select({"<script> " +
-            "select o.order_id,o.total_num ,o.order_code,o.item_json,o.pay_price,o.status,o.delivery_sn as delivery_id,o.finish_time  from live_order o  " +
+            "select o.order_id,o.total_num,o.create_time, o.discount_money ,o.live_id,o.order_code,o.item_json,o.pay_price,o.status,o.delivery_sn as delivery_id,o.finish_time  from live_order o  " +
             "where o.is_del=0 " +
             "<if test = 'maps.status != null and maps.status != \"\"     '> " +
             "and o.status =#{maps.status} " +
@@ -444,4 +444,19 @@ public interface LiveOrderMapper {
      * @param dtoList 物流信息列表
      */
     void batchUpdateInOrderCode(@Param("list") List<LiveOrderDeliveryNoteDTO> dtoList);
+
+    @Select("SELECT * FROM live_order WHERE user_id=#{userId} LIMIT 1")
+    LiveOrder selectOrderByUserIdLimit1(@Param("userId") Long userId);
+
+    @Select("SELECT * FROM live_order WHERE live_id= #{liveId}")
+    List<LiveOrder> selectOrderByLiveId(@Param("liveId") Long liveId);
+
+    /*
+    * 查询订单创建时间为最近30分钟的订单
+    * */
+    @Select("SELECT * FROM live_order WHERE create_time >= DATE_SUB(NOW(), INTERVAL 15 MINUTE) and status = 0 and refund_status = 0")
+    List<LiveOrder> selectBankOrder();
+
+
+    void batchUpdateTime(@Param("list") List<LiveOrder> list);
 }

+ 3 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveWatchUserMapper.java

@@ -140,4 +140,7 @@ public interface LiveWatchUserMapper {
 
     @Update("update live_watch_user set single_visible = #{status} where live_id = #{liveId} and user_id=#{userId}")
     void updateSingleVisible(@Param("liveId") long liveId,@Param("status") int status,@Param("userId") long userId);
+
+    @Select("select * from live_watch_user where live_id = #{liveId}")
+    List<LiveWatchUser> selectLiveWatchUserListByLiveId(@Param("liveId") Long liveId);
 }

+ 1 - 0
fs-service/src/main/java/com/fs/live/param/LiveDataParam.java

@@ -93,6 +93,7 @@ public class LiveDataParam {
 
     private Integer pageNum;
     private Integer pageSize;
+    private String watchType;
 
 
 }

+ 21 - 0
fs-service/src/main/java/com/fs/live/param/LiveNotifyParam.java

@@ -0,0 +1,21 @@
+package com.fs.live.param;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.HashMap;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class LiveNotifyParam {
+
+    private String liveId;
+    private String templateId;
+    private String maOpenId;
+    private Long userId;
+    private String appId;
+
+    HashMap<String,String> data;
+}

+ 3 - 0
fs-service/src/main/java/com/fs/live/param/LiveOrderParam.java

@@ -111,4 +111,7 @@ public class LiveOrderParam extends BaseEntity implements Serializable
     //导出字段
     private String filter;
 
+    //银行交易流水号
+    private String bankTransactionId;
+
 }

+ 7 - 0
fs-service/src/main/java/com/fs/live/service/ILiveAutoTaskService.java

@@ -89,4 +89,11 @@ public interface ILiveAutoTaskService {
     List<LiveAutoTask> selectLiveAutoTaskConsoleList(LiveAutoTask liveAutoTask);
 
     List<LiveAutoTask> consoleList(Long liveId);
+
+    void deleteAutoTasksByCouponId(Long couponId, Long liveId);
+    void deleteAutoTasksByRedId(Long redId, Long liveId);
+    void deleteAutoTasksByLotteryId(Long lotteryId, Long liveId);
+    void deleteAutoTasksByGoodsId(Long goodsId, Long liveId);
+    void deleteLotteryCache(Long lotteryId, Long liveId);
+    void deleteRedCache(Long redId, Long liveId);
 }

+ 28 - 0
fs-service/src/main/java/com/fs/live/service/ILiveDataService.java

@@ -135,4 +135,32 @@ public interface ILiveDataService {
     List<LiveUserFirstVo> inviteList(Long liveId);
 
     R listLiveData(LiveDataParam param);
+
+    /**
+     * 查询直播间详情数据(SQL方式)
+     * @param liveId 直播间ID
+     * @return 详情数据
+     */
+    R getLiveDataDetailBySql(Long liveId);
+
+    /**
+     * 查询直播间用户详情列表(SQL方式)
+     * @param liveId 直播间ID
+     * @return 用户详情列表
+     */
+    R getLiveUserDetailListBySql(Long liveId);
+
+    /**
+     * 查询直播间详情数据(查询数据服务器处理方式)
+     * @param liveId 直播间ID
+     * @return 详情数据
+     */
+    R getLiveDataDetailByServer(Long liveId);
+
+    /**
+     * 查询直播间用户详情列表(查询数据服务器处理方式)
+     * @param liveId 直播间ID
+     * @return 用户详情列表
+     */
+    R getLiveUserDetailListByServer(Long liveId);
 }

+ 7 - 1
fs-service/src/main/java/com/fs/live/service/ILiveOrderService.java

@@ -100,7 +100,7 @@ public interface ILiveOrderService {
      * @param orderId
      * @return
      */
-    int getGoods(Long orderId);
+    int finishishOrder(Long orderId);
 
     /**
      * 修改物流信息
@@ -255,4 +255,10 @@ public interface ILiveOrderService {
      * @return list
      * **/
     List<LiveOrderDeliveryNoteExportVO> getDeliveryNote(LiveOrderParam param);
+
+    LiveOrder selectOrderByUserIdLimit1(Long userId);
+
+    List<LiveOrder> selectBankOrder();
+
+    void batchUpdateTime(List<LiveOrder> list);
 }

+ 3 - 2
fs-service/src/main/java/com/fs/live/service/ILiveService.java

@@ -2,6 +2,7 @@ package com.fs.live.service;
 
 
 import com.fs.common.core.page.PageRequest;
+import com.fs.live.param.LiveNotifyParam;
 import com.fs.live.vo.LiveVo;
 import com.fs.common.core.domain.R;
 import com.fs.live.domain.Live;
@@ -75,7 +76,7 @@ public interface ILiveService
      * @param liveIds 需要删除的直播主键集合
      * @return 结果
      */
-    public int deleteLiveByLiveIds(Long[] liveIds);
+    public int deleteLiveByLiveIds(Long[] liveIds,Live live);
 
     /**
      * 删除直播信息
@@ -181,7 +182,7 @@ public interface ILiveService
 
     List<Live> liveCompanyList(Long companyId);
 
-    R subNotifyLive(HashMap<String, Object> param);
+    R subNotifyLive(LiveNotifyParam liveNotifyParam);
 
     Integer updateLiveIsAudit(Live live);
 

+ 15 - 4
fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java

@@ -25,6 +25,8 @@ import com.fs.config.cloud.CloudHostProper;
 import com.fs.erp.constant.AfterSalesOrderStatusEnum;
 import com.fs.erp.domain.FsJstAftersalePush;
 import com.fs.erp.dto.BaseResponse;
+import com.fs.erp.dto.ErpOrderQueryRequert;
+import com.fs.erp.dto.ErpOrderQueryResponse;
 import com.fs.erp.dto.ErpRefundUpdateRequest;
 import com.fs.erp.mapper.FsJstAftersalePushMapper;
 import com.fs.erp.service.IErpOrderService;
@@ -377,6 +379,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
         order.setRefundStatus(String.valueOf(OrderInfoEnum.REFUND_STATUS_1.getValue()));
         order.setRefundExplain(param.getReasons());
         order.setCancelReason(param.getExplains());
+        order.setIsAfterSales(1);
         order.setRefundTime(new Date());
         liveOrderService.updateLiveOrder(order);
         //生成售后订单
@@ -424,9 +427,16 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
         request.setRefund_state(1);
         request.setStoreAfterSalesId(storeAfterSales.getId());
         if (StringUtils.isNotBlank(order.getExtendOrderId())){
-            BaseResponse response=erpOrderService.refundUpdateLive(request);
-            if(response.getSuccess()){
-                return R.ok();
+            ErpOrderQueryRequert queryRequest = new ErpOrderQueryRequert();
+            queryRequest.setCode(order.getExtendOrderId());
+            ErpOrderQueryResponse response = erpOrderService.getLiveOrder(queryRequest);
+            if (response.getOrders() != null && response.getOrders().size() > 0) {
+                if (response.getOrders().get(0).getCancle() != null && !response.getOrders().get(0).getCancle()) {
+                    BaseResponse res = erpOrderService.refundUpdateLive(request);
+                    if(res.getSuccess()){
+                        return R.ok();
+                    }
+                }
             }
             else{
                 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
@@ -542,7 +552,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
     @Override
     public int updateLiveAfterSales(LiveAfterSales liveAfterSales)
     {
-        if (!"".equals(liveAfterSales.getDeliveryName()) && !"".equals(liveAfterSales.getDeliverySn())){
+        if (StringUtils.isNotEmpty(liveAfterSales.getDeliveryName()) && StringUtils.isNotEmpty(liveAfterSales.getDeliverySn()) && StringUtils.isNotEmpty(liveAfterSales.getDeliveryCode())) {
             liveAfterSales.setStatus(2);
         }
 
@@ -860,6 +870,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
         LiveOrder order = liveOrderService.selectLiveOrderByOrderId(String.valueOf(storeAfterSales.getOrderId()));
         order.setStatus(storeAfterSales.getOrderStatus());
         order.setRefundStatus(OrderInfoEnum.REFUND_STATUS_0.getValue().toString());
+        order.setIsAfterSales(0);
         liveOrderService.updateLiveOrder(order);
         //操作记录
         LiveAfterSalesLogs logs = new LiveAfterSalesLogs();

+ 242 - 4
fs-service/src/main/java/com/fs/live/service/impl/LiveAutoTaskServiceImpl.java

@@ -3,10 +3,7 @@ package com.fs.live.service.impl;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
@@ -333,6 +330,13 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
     @Override
     public int deleteLiveAutoTaskByIds(Long[] ids)
     {
+        // 先从缓存中删除所有任务
+        for (Long id : ids) {
+            LiveAutoTask task = baseMapper.selectLiveAutoTaskById(id);
+            if (task != null) {
+                removeTaskFromCache(task);
+            }
+        }
         return baseMapper.deleteLiveAutoTaskByIds(ids);
     }
 
@@ -345,6 +349,12 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
     @Override
     public int deleteLiveAutoTaskById(Long id)
     {
+        // 先查询任务信息
+        LiveAutoTask task = baseMapper.selectLiveAutoTaskById(id);
+        if (task != null) {
+            // 从缓存中删除
+            removeTaskFromCache(task);
+        }
         return baseMapper.deleteLiveAutoTaskById(id);
     }
 
@@ -414,4 +424,232 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
     public List<LiveAutoTask> consoleList(Long liveId) {
         return baseMapper.consoleList(liveId, DateUtils.getNowDate());
     }
+
+    /**
+     * 删除与优惠券相关的自动化任务(taskType=5)
+     * @param couponId 优惠券ID
+     * @param liveId 直播间ID(可为null,null表示所有直播间)
+     */
+    @Override
+    public void deleteAutoTasksByCouponId(Long couponId, Long liveId) {
+        try {
+            List<LiveAutoTask> allTasks;
+            if (liveId != null) {
+                allTasks = baseMapper.selectLiveAutoTaskByLiveId(liveId);
+            } else {
+                // 查询所有直播间的任务
+                LiveAutoTask queryTask = new LiveAutoTask();
+                queryTask.setTaskType(5L); // 优惠券任务
+                allTasks = baseMapper.selectLiveAutoTaskList(queryTask);
+            }
+
+            List<Long> taskIdsToDelete = new ArrayList<>();
+            for (LiveAutoTask task : allTasks) {
+                if (task.getTaskType() != null && task.getTaskType() == 5L) {
+                    try {
+                        LiveCoupon liveCoupon = JSON.parseObject(task.getContent(), LiveCoupon.class);
+                        if (liveCoupon != null && couponId.equals(liveCoupon.getCouponId())) {
+                            taskIdsToDelete.add(task.getId());
+                            // 从缓存中删除
+                            removeTaskFromCache(task);
+                        }
+                    } catch (Exception e) {
+                        log.warn("解析自动化任务content失败,taskId: {}, error: {}", task.getId(), e.getMessage());
+                    }
+                }
+            }
+
+            if (!taskIdsToDelete.isEmpty()) {
+                Long[] ids = taskIdsToDelete.toArray(new Long[0]);
+                baseMapper.deleteLiveAutoTaskByIds(ids);
+                log.info("删除与优惠券ID {} 相关的自动化任务 {} 个", couponId, taskIdsToDelete.size());
+            }
+        } catch (Exception e) {
+            log.error("删除优惠券相关自动化任务失败,couponId: {}", couponId, e);
+        }
+    }
+
+    /**
+     * 删除与红包相关的自动化任务(taskType=2)
+     * @param redId 红包ID
+     * @param liveId 直播间ID
+     */
+    @Override
+    public void deleteAutoTasksByRedId(Long redId, Long liveId) {
+        try {
+            List<LiveAutoTask> allTasks = baseMapper.selectLiveAutoTaskByLiveId(liveId);
+            List<Long> taskIdsToDelete = new ArrayList<>();
+
+            for (LiveAutoTask task : allTasks) {
+                if (task.getTaskType() != null && task.getTaskType() == 2L) {
+                    try {
+                        LiveRedConf liveRedConf = JSON.parseObject(task.getContent(), LiveRedConf.class);
+                        if (liveRedConf != null && redId.equals(liveRedConf.getRedId())) {
+                            taskIdsToDelete.add(task.getId());
+                            // 从缓存中删除
+                            removeTaskFromCache(task);
+                        }
+                    } catch (Exception e) {
+                        log.warn("解析自动化任务content失败,taskId: {}, error: {}", task.getId(), e.getMessage());
+                    }
+                }
+            }
+
+            if (!taskIdsToDelete.isEmpty()) {
+                Long[] ids = taskIdsToDelete.toArray(new Long[0]);
+                baseMapper.deleteLiveAutoTaskByIds(ids);
+                log.info("删除与红包ID {} 相关的自动化任务 {} 个", redId, taskIdsToDelete.size());
+            }
+        } catch (Exception e) {
+            log.error("删除红包相关自动化任务失败,redId: {}, liveId: {}", redId, liveId, e);
+        }
+    }
+
+    /**
+     * 删除与抽奖相关的自动化任务(taskType=4)
+     * @param lotteryId 抽奖ID
+     * @param liveId 直播间ID
+     */
+    @Override
+    public void deleteAutoTasksByLotteryId(Long lotteryId, Long liveId) {
+        try {
+            List<LiveAutoTask> allTasks = baseMapper.selectLiveAutoTaskByLiveId(liveId);
+            List<Long> taskIdsToDelete = new ArrayList<>();
+
+            for (LiveAutoTask task : allTasks) {
+                if (task.getTaskType() != null && task.getTaskType() == 4L) {
+                    try {
+                        LiveLotteryConf liveLotteryConf = JSON.parseObject(task.getContent(), LiveLotteryConf.class);
+                        if (liveLotteryConf != null && lotteryId.equals(liveLotteryConf.getLotteryId())) {
+                            taskIdsToDelete.add(task.getId());
+                            // 从缓存中删除
+                            removeTaskFromCache(task);
+                        }
+                    } catch (Exception e) {
+                        log.warn("解析自动化任务content失败,taskId: {}, error: {}", task.getId(), e.getMessage());
+                    }
+                }
+            }
+
+            if (!taskIdsToDelete.isEmpty()) {
+                Long[] ids = taskIdsToDelete.toArray(new Long[0]);
+                baseMapper.deleteLiveAutoTaskByIds(ids);
+                log.info("删除与抽奖ID {} 相关的自动化任务 {} 个", lotteryId, taskIdsToDelete.size());
+            }
+        } catch (Exception e) {
+            log.error("删除抽奖相关自动化任务失败,lotteryId: {}, liveId: {}", lotteryId, liveId, e);
+        }
+    }
+
+    /**
+     * 删除与商品相关的自动化任务(taskType=1或6)
+     * @param goodsId 商品ID
+     * @param liveId 直播间ID
+     */
+    @Override
+    public void deleteAutoTasksByGoodsId(Long goodsId, Long liveId) {
+        try {
+            List<LiveAutoTask> allTasks = baseMapper.selectLiveAutoTaskByLiveId(liveId);
+            List<Long> taskIdsToDelete = new ArrayList<>();
+
+            for (LiveAutoTask task : allTasks) {
+                if (task.getTaskType() != null && (task.getTaskType() == 1L || task.getTaskType() == 6L)) {
+                    try {
+                        if (task.getTaskType() == 1L) {
+                            // 商品推荐任务
+                            LiveGoodsVo liveGoodsVo = JSON.parseObject(task.getContent(), LiveGoodsVo.class);
+                            if (liveGoodsVo != null && goodsId.equals(liveGoodsVo.getGoodsId())) {
+                                taskIdsToDelete.add(task.getId());
+                                removeTaskFromCache(task);
+                            }
+                        } else if (task.getTaskType() == 6L) {
+                            // 商品上下架任务
+                            com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(task.getContent());
+                            Long taskGoodsId = jsonObject.getLong("goodsId");
+                            if (taskGoodsId != null && goodsId.equals(taskGoodsId)) {
+                                taskIdsToDelete.add(task.getId());
+                                removeTaskFromCache(task);
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.warn("解析自动化任务content失败,taskId: {}, error: {}", task.getId(), e.getMessage());
+                    }
+                }
+            }
+
+            if (!taskIdsToDelete.isEmpty()) {
+                Long[] ids = taskIdsToDelete.toArray(new Long[0]);
+                baseMapper.deleteLiveAutoTaskByIds(ids);
+                log.info("删除与商品ID {} 相关的自动化任务 {} 个", goodsId, taskIdsToDelete.size());
+            }
+        } catch (Exception e) {
+            log.error("删除商品相关自动化任务失败,goodsId: {}, liveId: {}", goodsId, liveId, e);
+        }
+    }
+
+    /**
+     * 从缓存中删除自动化任务
+     * @param task 自动化任务
+     */
+    private void removeTaskFromCache(LiveAutoTask task) {
+        try {
+            if (task.getLiveId() != null && task.getId() != null) {
+                String cacheKey = "live:auto_task:" + task.getLiveId();
+                // 获取ZSet中的所有任务
+                Set<String> allTasks = redisCache.redisTemplate.opsForZSet().range(cacheKey, 0, -1);
+                if (allTasks != null && !allTasks.isEmpty()) {
+                    // 遍历找到匹配的任务ID
+                    for (String taskJson : allTasks) {
+                        try {
+                            LiveAutoTask cachedTask = JSON.parseObject(taskJson, LiveAutoTask.class);
+                            if (cachedTask != null && task.getId().equals(cachedTask.getId())) {
+                                // 找到匹配的任务,从ZSet中删除
+                                redisCache.redisTemplate.opsForZSet().remove(cacheKey, taskJson);
+                                log.debug("从缓存中删除自动化任务,liveId: {}, taskId: {}", task.getLiveId(), task.getId());
+                                break;
+                            }
+                        } catch (Exception e) {
+                            log.warn("解析缓存中的任务失败,taskJson: {}, error: {}", taskJson, e.getMessage());
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.warn("从缓存删除自动化任务失败,taskId: {}, error: {}", task.getId(), e.getMessage());
+        }
+    }
+
+    /**
+     * 删除抽奖缓存
+     * @param lotteryId 抽奖ID
+     * @param liveId 直播间ID
+     */
+    public void deleteLotteryCache(Long lotteryId, Long liveId) {
+        try {
+            String cacheKey = "live:lottery_task:" + liveId;
+            // 从ZSet中删除指定的抽奖ID
+            redisCache.redisTemplate.opsForZSet().remove(cacheKey, String.valueOf(lotteryId));
+            log.info("删除抽奖缓存,lotteryId: {}, liveId: {}", lotteryId, liveId);
+        } catch (Exception e) {
+            log.error("删除抽奖缓存失败,lotteryId: {}, liveId: {}", lotteryId, liveId, e);
+        }
+    }
+
+    /**
+     * 删除红包缓存
+     * @param redId 红包ID
+     * @param liveId 直播间ID
+     */
+    public void deleteRedCache(Long redId, Long liveId) {
+        try {
+            String cacheKey = "live:red_task:" + liveId;
+            // 从ZSet中删除指定的红包ID
+            redisCache.redisTemplate.opsForZSet().remove(cacheKey, String.valueOf(redId));
+            // 同时删除剩余红包数的缓存
+            redisCache.deleteObject("live:red:remainingLots:" + redId);
+            log.info("删除红包缓存,redId: {}, liveId: {}", redId, liveId);
+        } catch (Exception e) {
+            log.error("删除红包缓存失败,redId: {}, liveId: {}", redId, liveId, e);
+        }
+    }
 }

+ 52 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveCouponIssueServiceImpl.java

@@ -3,7 +3,11 @@ package com.fs.live.service.impl;
 import java.util.Collections;
 import java.util.List;
 import com.fs.common.utils.DateUtils;
+import com.fs.live.domain.LiveCouponIssueRelation;
+import com.fs.live.mapper.LiveCouponMapper;
 import com.fs.live.param.CouponPO;
+import com.fs.live.service.ILiveAutoTaskService;
+import com.fs.live.vo.LiveCouponListVo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.live.mapper.LiveCouponIssueMapper;
@@ -21,6 +25,10 @@ public class LiveCouponIssueServiceImpl implements ILiveCouponIssueService
 {
     @Autowired
     private LiveCouponIssueMapper liveCouponIssueMapper;
+    @Autowired
+    private LiveCouponMapper liveCouponMapper;
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
 
     /**
      * 查询优惠券领取
@@ -79,8 +87,31 @@ public class LiveCouponIssueServiceImpl implements ILiveCouponIssueService
      * @return 结果
      */
     @Override
+    @org.springframework.transaction.annotation.Transactional
     public int deleteLiveCouponIssueByIds(Long[] ids)
     {
+        // 联动删除:删除所有直播间存在的优惠券、定时任务和缓存
+        for (Long couponIssueId : ids) {
+            // 查询优惠券发布信息,获取couponId
+            LiveCouponIssue issue = liveCouponIssueMapper.selectLiveCouponIssueById(couponIssueId);
+            if (issue != null && issue.getCouponId() != null) {
+                // 删除所有直播间相关的自动化任务(liveId为null表示所有直播间)
+                liveAutoTaskService
+                        .deleteAutoTasksByCouponId(issue.getCouponId(), null);
+
+                // 查询所有使用该优惠券发布的直播间关联关系
+                List<LiveCouponIssueRelation> relations =
+                        liveCouponMapper.selectCouponRelationByCouponIssueId(couponIssueId);
+
+                // 删除所有直播间的关联关系
+                for (LiveCouponIssueRelation relation : relations) {
+                    LiveCouponListVo listVo = new LiveCouponListVo();
+                    listVo.setCouponIds(java.util.Arrays.asList(couponIssueId));
+                    listVo.setLiveId(relation.getLiveId().intValue());
+                    liveCouponMapper.handleDeleteSelectedAdmin(listVo);
+                }
+            }
+        }
         return liveCouponIssueMapper.deleteLiveCouponIssueByIds(ids);
     }
 
@@ -91,8 +122,29 @@ public class LiveCouponIssueServiceImpl implements ILiveCouponIssueService
      * @return 结果
      */
     @Override
+    @org.springframework.transaction.annotation.Transactional
     public int deleteLiveCouponIssueById(Long id)
     {
+        // 联动删除:删除所有直播间存在的优惠券、定时任务和缓存
+        // 查询优惠券发布信息,获取couponId
+        LiveCouponIssue issue = liveCouponIssueMapper.selectLiveCouponIssueById(id);
+        if (issue != null && issue.getCouponId() != null) {
+            // 删除所有直播间相关的自动化任务(liveId为null表示所有直播间)
+            liveAutoTaskService
+                    .deleteAutoTasksByCouponId(issue.getCouponId(), null);
+
+            // 查询所有使用该优惠券发布的直播间关联关系
+            List<LiveCouponIssueRelation> relations =
+                    liveCouponMapper.selectCouponRelationByCouponIssueId(id);
+
+            // 删除所有直播间的关联关系
+            for (LiveCouponIssueRelation relation : relations) {
+                LiveCouponListVo listVo = new LiveCouponListVo();
+                listVo.setCouponIds(java.util.Arrays.asList(id));
+                listVo.setLiveId(relation.getLiveId().intValue());
+                liveCouponMapper.handleDeleteSelectedAdmin(listVo);
+            }
+        }
         return liveCouponIssueMapper.deleteLiveCouponIssueById(id);
     }
 

+ 84 - 4
fs-service/src/main/java/com/fs/live/service/impl/LiveCouponServiceImpl.java

@@ -11,19 +11,17 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.live.domain.*;
+import com.fs.live.mapper.LiveCouponIssueMapper;
 import com.fs.live.mapper.LiveCouponIssueUserMapper;
 import com.fs.live.mapper.LiveMapper;
 import com.fs.live.param.CouponPO;
-import com.fs.live.service.ILiveCouponIssueService;
-import com.fs.live.service.ILiveCouponIssueUserService;
-import com.fs.live.service.ILiveCouponUserService;
+import com.fs.live.service.*;
 import com.fs.live.vo.LiveCouponListVo;
 
 import org.checkerframework.checker.units.qual.A;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.live.mapper.LiveCouponMapper;
-import com.fs.live.service.ILiveCouponService;
 import org.springframework.transaction.annotation.Transactional;
 
 /**
@@ -51,6 +49,10 @@ public class LiveCouponServiceImpl implements ILiveCouponService
     private ILiveCouponIssueService liveCouponIssueService;
     @Autowired
     private LiveCouponIssueUserMapper liveCouponIssueUserMapper;
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
+    @Autowired
+    private LiveCouponIssueMapper liveCouponIssueMapper;
 
     /**
      * 查询优惠券
@@ -109,8 +111,34 @@ public class LiveCouponServiceImpl implements ILiveCouponService
      * @return 结果
      */
     @Override
+    @Transactional
     public int deleteLiveCouponByIds(Long[] couponIds)
     {
+        // 联动删除:删除所有直播间存在的优惠券、定时任务和缓存
+        for (Long couponId : couponIds) {
+            // 删除所有直播间相关的自动化任务(liveId为null表示所有直播间)
+            liveAutoTaskService
+                    .deleteAutoTasksByCouponId(couponId, null);
+
+            // 查询所有使用该优惠券的优惠券发布记录
+            LiveCouponIssue queryIssue = new LiveCouponIssue();
+            queryIssue.setCouponId(couponId);
+            List<LiveCouponIssue> couponIssues = liveCouponIssueMapper.selectLiveCouponIssueList(queryIssue);
+
+            // 删除所有直播间的关联关系
+            for (LiveCouponIssue issue : couponIssues) {
+                if (issue.getId() != null) {
+                    // 查询所有使用该优惠券发布的直播间关联关系
+                    List<LiveCouponIssueRelation> relations = liveCouponMapper.selectCouponRelationByCouponIssueId(issue.getId());
+                    for (LiveCouponIssueRelation relation : relations) {
+                        LiveCouponListVo listVo = new LiveCouponListVo();
+                        listVo.setCouponIds(java.util.Arrays.asList(issue.getId()));
+                        listVo.setLiveId(relation.getLiveId().intValue());
+                        liveCouponMapper.handleDeleteSelectedAdmin(listVo);
+                    }
+                }
+            }
+        }
         return liveCouponMapper.deleteLiveCouponByIds(couponIds);
     }
 
@@ -121,8 +149,32 @@ public class LiveCouponServiceImpl implements ILiveCouponService
      * @return 结果
      */
     @Override
+    @Transactional
     public int deleteLiveCouponById(Long couponId)
     {
+        // 联动删除:删除所有直播间存在的优惠券、定时任务和缓存
+        // 删除所有直播间相关的自动化任务(liveId为null表示所有直播间)
+        liveAutoTaskService
+                .deleteAutoTasksByCouponId(couponId, null);
+
+        // 查询所有使用该优惠券的优惠券发布记录
+        LiveCouponIssue queryIssue = new LiveCouponIssue();
+        queryIssue.setCouponId(couponId);
+        List<LiveCouponIssue> couponIssues = liveCouponIssueMapper.selectLiveCouponIssueList(queryIssue);
+
+        // 删除所有直播间的关联关系
+        for (LiveCouponIssue issue : couponIssues) {
+            if (issue.getId() != null) {
+                // 查询所有使用该优惠券发布的直播间关联关系
+                List<LiveCouponIssueRelation> relations = liveCouponMapper.selectCouponRelationByCouponIssueId(issue.getId());
+                for (LiveCouponIssueRelation relation : relations) {
+                    LiveCouponListVo listVo = new LiveCouponListVo();
+                    listVo.setCouponIds(java.util.Arrays.asList(issue.getId()));
+                    listVo.setLiveId(relation.getLiveId().intValue());
+                    liveCouponMapper.handleDeleteSelectedAdmin(listVo);
+                }
+            }
+        }
         return liveCouponMapper.deleteLiveCouponById(couponId);
     }
 
@@ -185,13 +237,41 @@ public class LiveCouponServiceImpl implements ILiveCouponService
     }
 
     @Override
+    @Transactional
     public R handleDeleteSelectedAdmin(LiveCouponListVo listVo) {
+        // 在删除前,查询关联关系,获取couponId和liveId,用于联动删除
+        if (listVo.getCouponIds() != null && !listVo.getCouponIds().isEmpty() && listVo.getLiveId() != null) {
+            Long liveId = Long.valueOf(listVo.getLiveId());
+            for (Long couponIssueId : listVo.getCouponIds()) {
+                // 查询优惠券发布信息
+                LiveCouponIssue liveCouponIssue = liveCouponIssueMapper.selectLiveCouponIssueById(couponIssueId);
+                if (liveCouponIssue != null && liveCouponIssue.getCouponId() != null) {
+                    // 删除相关的自动化任务
+                    liveAutoTaskService
+                            .deleteAutoTasksByCouponId(liveCouponIssue.getCouponId(), liveId);
+                }
+            }
+        }
         liveCouponMapper.handleDeleteSelectedAdmin(listVo);
         return R.ok();
     }
 
     @Override
+    @Transactional
     public int delLiveCoupon(LiveCouponListVo vo) {
+        // 在删除前,查询关联关系,获取couponId和liveId,用于联动删除
+        if (vo.getCouponIds() != null && !vo.getCouponIds().isEmpty() && vo.getLiveId() != null) {
+            Long liveId = Long.valueOf(vo.getLiveId());
+            for (Long couponIssueId : vo.getCouponIds()) {
+                // 查询优惠券发布信息
+                LiveCouponIssue liveCouponIssue = liveCouponIssueMapper.selectLiveCouponIssueById(couponIssueId);
+                if (liveCouponIssue != null && liveCouponIssue.getCouponId() != null) {
+                    // 删除相关的自动化任务
+                    liveAutoTaskService
+                            .deleteAutoTasksByCouponId(liveCouponIssue.getCouponId(), liveId);
+                }
+            }
+        }
         liveCouponMapper.handleDeleteSelectedAdmin(vo);
         return 1;
     }

+ 417 - 3
fs-service/src/main/java/com/fs/live/service/impl/LiveDataServiceImpl.java

@@ -5,6 +5,8 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.spring.SpringUtils;
+import com.fs.hisStore.domain.FsUserScrm;
+import com.fs.hisStore.mapper.FsUserScrmMapper;
 import com.fs.live.domain.*;
 import com.fs.live.mapper.*;
 import com.fs.live.param.LiveDataParam;
@@ -13,6 +15,16 @@ import com.fs.live.service.ILiveUserFavoriteService;
 import com.fs.live.service.ILiveUserFollowService;
 import com.fs.live.service.ILiveUserLikeService;
 import com.fs.live.vo.*;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyMapper;
+import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.course.domain.FsUserCompanyUser;
+import com.fs.course.mapper.FsUserCompanyUserMapper;
+import com.fs.his.domain.FsUser;
+import com.fs.his.mapper.FsUserMapper;
+import com.fs.hisStore.domain.FsStoreProductScrm;
+import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
 import java.util.stream.Collectors;
 import io.swagger.models.auth.In;
 import org.slf4j.Logger;
@@ -67,7 +79,16 @@ public class LiveDataServiceImpl implements ILiveDataService {
     private LiveMsgMapper liveMsgMapper;
     @Autowired
     private LiveMapper liveMapper;
-
+    @Autowired
+    private FsUserScrmMapper fsUserScrmMapper;
+    @Autowired
+    private FsUserCompanyUserMapper  fsUserCompanyUserMapper;
+    @Autowired
+    private CompanyMapper companyMapper;
+    @Autowired
+    private CompanyUserMapper companyUserMapper;
+    @Autowired
+    private LiveOrderMapper liveOrderMapper;
 
     /* 直播大屏展示 数据接口 */
     @Override
@@ -139,8 +160,8 @@ public class LiveDataServiceImpl implements ILiveDataService {
     public R listLiveData(LiveDataParam param) {
         // 第一步:查询当前公司的直播间数据
         // 直播类型 只展示已结束和直播回放的数据 录播展示直播中和已结束的直播数据
-        List<Live> lives = liveMapper.listLiveData(param.getCompanyId());
-        int total = liveMapper.listLiveDataCount(param.getCompanyId());
+        List<Live> lives = liveMapper.listLiveData(param);
+        int total = liveMapper.listLiveDataCount(param);
 
         if (lives == null || lives.isEmpty()) {
             LiveDataStatisticsVo statistics = new LiveDataStatisticsVo();
@@ -661,4 +682,397 @@ public class LiveDataServiceImpl implements ILiveDataService {
         return column;
     }
 
+    @Override
+    public R getLiveDataDetailBySql(Long liveId) {
+        LiveDataDetailVo detailVo = liveDataMapper.selectLiveDataDetailBySql(liveId);
+        if (detailVo == null) {
+            detailVo = new LiveDataDetailVo();
+        }
+        // 查询单品销量统计
+        List<ProductSalesVo> productSalesList = getProductSalesList(liveId);
+        detailVo.setProductSalesList(productSalesList);
+        return R.ok().put("data", detailVo);
+    }
+
+    @Override
+    public R getLiveUserDetailListBySql(Long liveId) {
+        List<LiveUserDetailVo> userDetailList = liveDataMapper.selectLiveUserDetailListBySql(liveId);
+        return R.ok().put("data", userDetailList);
+    }
+
+    @Override
+    public R getLiveDataDetailByServer(Long liveId) {
+        // 查询数据服务器处理方式:通过查询各个表的数据,在内存中计算统计
+        LiveDataDetailVo detailVo = calculateLiveDataDetailByServer(liveId);
+        // 查询单品销量统计
+        List<ProductSalesVo> productSalesList = getProductSalesList(liveId);
+        detailVo.setProductSalesList(productSalesList);
+        return R.ok().put("data", detailVo);
+    }
+
+    @Override
+    public R getLiveUserDetailListByServer(Long liveId) {
+        // 查询数据服务器处理方式:通过查询各个表的数据,在内存中计算
+        List<LiveUserDetailVo> userDetailList = calculateLiveUserDetailListByServer(liveId);
+        return R.ok().put("data", userDetailList);
+    }
+
+    /**
+     * 通过查询数据服务器处理方式计算直播间详情数据
+     */
+    private LiveDataDetailVo calculateLiveDataDetailByServer(Long liveId) {
+        LiveDataDetailVo detailVo = new LiveDataDetailVo();
+
+        // 查询视频时长
+        LiveVideoMapper liveVideoMapper = SpringUtils.getBean(LiveVideoMapper.class);
+        List<LiveVideo> videos = liveVideoMapper.selectByLiveId(liveId);
+        Long videoDuration = videos.stream()
+                .filter(v -> v.getVideoType() != null && (v.getVideoType() == 1 || v.getVideoType() == 2))
+                .mapToLong(v -> v.getDuration() != null ? v.getDuration() : 0L)
+                .sum();
+        detailVo.setVideoDuration(videoDuration);
+
+        // 查询观看用户数据
+        LiveWatchUser queryParam = new LiveWatchUser();
+        queryParam.setLiveId(liveId);
+        List<LiveWatchUser> watchUsers = liveWatchUserMapper.selectLiveWatchUserList(queryParam);
+
+        // 累计观看人数
+        long totalViewers = watchUsers.stream().map(LiveWatchUser::getUserId).distinct().count();
+        detailVo.setTotalViewers(totalViewers);
+
+        // 直播观看人数
+        long liveViewers = watchUsers.stream()
+                .filter(w -> w.getLiveFlag() != null && w.getLiveFlag() == 1 && (w.getReplayFlag() == null || w.getReplayFlag() == 0))
+                .map(LiveWatchUser::getUserId)
+                .distinct()
+                .count();
+        detailVo.setLiveViewers(liveViewers);
+
+        // 回放观看人数
+        long playbackViewers = watchUsers.stream()
+                .filter(w -> w.getReplayFlag() != null && w.getReplayFlag() == 1 && (w.getLiveFlag() == null || w.getLiveFlag() == 0))
+                .map(LiveWatchUser::getUserId)
+                .distinct()
+                .count();
+        detailVo.setPlaybackViewers(playbackViewers);
+
+        // 累计完课人数(观看时长 >= 视频时长)
+        long totalCompletedCourses = watchUsers.stream()
+                .filter(w -> w.getOnlineSeconds() != null && videoDuration > 0 && w.getOnlineSeconds() >= videoDuration)
+                .map(LiveWatchUser::getUserId)
+                .distinct()
+                .count();
+        detailVo.setTotalCompletedCourses(totalCompletedCourses);
+
+        // 计算到课完课率
+        if (totalViewers > 0) {
+            detailVo.setTotalCompletionRate(BigDecimal.valueOf(totalCompletedCourses * 100.0 / totalViewers).setScale(2, RoundingMode.HALF_UP));
+        }
+
+        // 直播相关统计
+        List<LiveWatchUser> liveWatchUsers = watchUsers.stream()
+                .filter(w -> w.getLiveFlag() != null && w.getLiveFlag() == 1 && (w.getReplayFlag() == null || w.getReplayFlag() == 0))
+                .collect(Collectors.toList());
+
+        long liveOver20Minutes = liveWatchUsers.stream()
+                .filter(w -> w.getOnlineSeconds() != null && w.getOnlineSeconds() >= 1200)
+                .map(LiveWatchUser::getUserId)
+                .distinct()
+                .count();
+        detailVo.setLiveOver20Minutes(liveOver20Minutes);
+
+        long liveOver30Minutes = liveWatchUsers.stream()
+                .filter(w -> w.getOnlineSeconds() != null && w.getOnlineSeconds() >= 1800)
+                .map(LiveWatchUser::getUserId)
+                .distinct()
+                .count();
+        detailVo.setLiveOver30Minutes(liveOver30Minutes);
+
+        if (liveViewers > 0) {
+            detailVo.setLiveCompletionRate20(BigDecimal.valueOf(liveOver20Minutes * 100.0 / liveViewers).setScale(2, RoundingMode.HALF_UP));
+            detailVo.setLiveCompletionRate30(BigDecimal.valueOf(liveOver30Minutes * 100.0 / liveViewers).setScale(2, RoundingMode.HALF_UP));
+        }
+
+        // 直播平均时长
+        double liveAvgDuration = liveWatchUsers.stream()
+                .filter(w -> w.getOnlineSeconds() != null)
+                .mapToLong(LiveWatchUser::getOnlineSeconds)
+                .average()
+                .orElse(0.0);
+        detailVo.setLiveAvgDuration((long) liveAvgDuration);
+
+        // 回放相关统计
+        List<LiveWatchUser> playbackWatchUsers = watchUsers.stream()
+                .filter(w -> w.getReplayFlag() != null && w.getReplayFlag() == 1 && (w.getLiveFlag() == null || w.getLiveFlag() == 0))
+                .collect(Collectors.toList());
+
+        long playbackOver20Minutes = playbackWatchUsers.stream()
+                .filter(w -> w.getOnlineSeconds() != null && w.getOnlineSeconds() >= 1200)
+                .map(LiveWatchUser::getUserId)
+                .distinct()
+                .count();
+        detailVo.setPlaybackOver20Minutes(playbackOver20Minutes);
+
+        long playbackOver30Minutes = playbackWatchUsers.stream()
+                .filter(w -> w.getOnlineSeconds() != null && w.getOnlineSeconds() >= 1800)
+                .map(LiveWatchUser::getUserId)
+                .distinct()
+                .count();
+        detailVo.setPlaybackOver30Minutes(playbackOver30Minutes);
+
+        if (playbackViewers > 0) {
+            detailVo.setPlaybackCompletionRate20(BigDecimal.valueOf(playbackOver20Minutes * 100.0 / playbackViewers).setScale(2, RoundingMode.HALF_UP));
+            detailVo.setPlaybackCompletionRate30(BigDecimal.valueOf(playbackOver30Minutes * 100.0 / playbackViewers).setScale(2, RoundingMode.HALF_UP));
+        }
+
+        // 回放平均时长
+        double playbackAvgDuration = playbackWatchUsers.stream()
+                .filter(w -> w.getOnlineSeconds() != null)
+                .mapToLong(LiveWatchUser::getOnlineSeconds)
+                .average()
+                .orElse(0.0);
+        detailVo.setPlaybackAvgDuration((long) playbackAvgDuration);
+
+        // 回放完播率
+        if (videoDuration > 0) {
+            detailVo.setPlaybackFinishRate(BigDecimal.valueOf(playbackAvgDuration * 100.0 / videoDuration).setScale(2, RoundingMode.HALF_UP));
+        }
+
+        // 查询直播峰值
+        LiveData liveData = liveDataMapper.selectLiveDataByLiveId(liveId);
+        if (liveData != null && liveData.getPeakConcurrentViewers() != null) {
+            detailVo.setLivePeak(liveData.getPeakConcurrentViewers());
+        }
+
+        // 查询订单数据
+        LiveOrderMapper liveOrderMapper = SpringUtils.getBean(LiveOrderMapper.class);
+        LiveOrder orderQuery = new LiveOrder();
+        orderQuery.setLiveId(liveId);
+        List<LiveOrder> orders = liveOrderMapper.selectLiveOrderList(orderQuery);
+
+        BigDecimal gmv = orders.stream()
+                .filter(o -> "1".equals(o.getIsPay()))
+                .map(LiveOrder::getPayPrice)
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        detailVo.setGmv(gmv);
+
+        long paidUsers = orders.stream()
+                .filter(o -> "1".equals(o.getIsPay()))
+                .filter(o -> o.getUserId() != null && !o.getUserId().isEmpty())
+                .map(o -> {
+                    try {
+                        return Long.parseLong(o.getUserId());
+                    } catch (NumberFormatException e) {
+                        return null;
+                    }
+                })
+                .filter(Objects::nonNull)
+                .distinct()
+                .count();
+        detailVo.setPaidUsers(paidUsers);
+
+        long paidOrders = orders.stream()
+                .filter(o -> "1".equals(o.getIsPay()))
+                .count();
+        detailVo.setPaidOrders(paidOrders);
+
+        // 计算转化率
+        if (detailVo.getLivePeak() > 0) {
+            detailVo.setPeakConversionRate(BigDecimal.valueOf(paidUsers * 100.0 / detailVo.getLivePeak()).setScale(2, RoundingMode.HALF_UP));
+            detailVo.setPeakRValue(gmv.divide(BigDecimal.valueOf(detailVo.getLivePeak()), 2, RoundingMode.HALF_UP));
+        }
+
+        if (totalViewers > 0) {
+            detailVo.setTotalViewerConversionRate(BigDecimal.valueOf(paidUsers * 100.0 / totalViewers).setScale(2, RoundingMode.HALF_UP));
+        }
+
+        if (liveOver30Minutes > 0) {
+            detailVo.setCompletion30MinConversionRate(BigDecimal.valueOf(paidUsers * 100.0 / liveOver30Minutes).setScale(2, RoundingMode.HALF_UP));
+        }
+
+        if (totalCompletedCourses > 0) {
+            detailVo.setCompletionRValue(gmv.divide(BigDecimal.valueOf(totalCompletedCourses), 2, RoundingMode.HALF_UP));
+        }
+
+        return detailVo;
+    }
+
+    /**
+     * 通过查询数据服务器处理方式计算用户详情列表
+     */
+    private List<LiveUserDetailVo> calculateLiveUserDetailListByServer(Long liveId) {
+        // 查询观看用户
+        List<LiveWatchUser> watchUsers = liveWatchUserMapper.selectLiveWatchUserListByLiveId(liveId);
+
+        LiveOrder orderQuery = new LiveOrder();
+        orderQuery.setLiveId(liveId);
+        List<LiveOrder> orders = liveOrderMapper.selectLiveOrderList(orderQuery);
+
+
+        // 按用户ID分组统计
+        Map<Long, LiveUserDetailVo> userDetailMap = new HashMap<>();
+
+        for (LiveWatchUser watchUser : watchUsers) {
+            Long userId = watchUser.getUserId();
+            if (userId == null) continue;
+
+            LiveUserDetailVo userDetail = userDetailMap.computeIfAbsent(userId, k -> {
+                LiveUserDetailVo vo = new LiveUserDetailVo();
+                vo.setUserId(userId);
+                // 查询用户信息
+                FsUserScrm user = fsUserScrmMapper.selectFsUserByUserId(userId);
+                if (user != null) {
+                    vo.setUserName(user.getNickName() != null ? user.getNickName() : (user.getNickName() != null ? user.getNickName() : "未知用户"));
+                } else {
+                    vo.setUserName("未知用户");
+                }
+                // 查询用户和销售关系
+                FsUserCompanyUser queryUserCompanyUser = new FsUserCompanyUser();
+                queryUserCompanyUser.setUserId(userId);
+                List<FsUserCompanyUser> userCompanyUserList = fsUserCompanyUserMapper.selectFsUserCompanyUserList(queryUserCompanyUser);
+                FsUserCompanyUser userCompanyUser = userCompanyUserList != null && !userCompanyUserList.isEmpty() ? userCompanyUserList.get(0) : null;
+                if (userCompanyUser != null) {
+                    if (userCompanyUser.getCompanyId() != null) {
+                        Company company = companyMapper.selectCompanyById(userCompanyUser.getCompanyId());
+                        if (company != null) {
+                            vo.setCompanyName(company.getCompanyName());
+                        }
+                    }
+                    if (userCompanyUser.getCompanyUserId() != null) {
+                        CompanyUser companyUser = companyUserMapper.selectCompanyUserByUserId(userCompanyUser.getCompanyUserId());
+                        if (companyUser != null) {
+                            vo.setSalesName(companyUser.getUserName());
+                        }
+                    }
+                }
+                return vo;
+            });
+
+            // 累加观看时长
+            if (watchUser.getOnlineSeconds() != null) {
+                if (watchUser.getLiveFlag() != null && watchUser.getLiveFlag() == 1 && (watchUser.getReplayFlag() == null || watchUser.getReplayFlag() == 0)) {
+                    userDetail.setLiveWatchDuration(userDetail.getLiveWatchDuration() + watchUser.getOnlineSeconds());
+                } else if (watchUser.getReplayFlag() != null && watchUser.getReplayFlag() == 1 && (watchUser.getLiveFlag() == null || watchUser.getLiveFlag() == 0)) {
+                    userDetail.setPlaybackWatchDuration(userDetail.getPlaybackWatchDuration() + watchUser.getOnlineSeconds());
+                }
+            }
+        }
+
+        // 统计订单数据
+        Map<Long, List<LiveOrder>> userOrdersMap = orders.stream()
+                .filter(o -> o.getUserId() != null && !o.getUserId().isEmpty())
+                .filter(o -> {
+                    try {
+                        Long.parseLong(o.getUserId());
+                        return true;
+                    } catch (NumberFormatException e) {
+                        return false;
+                    }
+                })
+                .collect(Collectors.groupingBy(o -> {
+                    try {
+                        return Long.parseLong(o.getUserId());
+                    } catch (NumberFormatException e) {
+                        return 0L;
+                    }
+                }));
+
+        for (Map.Entry<Long, List<LiveOrder>> entry : userOrdersMap.entrySet()) {
+            Long userId = entry.getKey();
+            List<LiveOrder> userOrders = entry.getValue();
+
+            LiveUserDetailVo userDetail = userDetailMap.computeIfAbsent(userId, k -> {
+                LiveUserDetailVo vo = new LiveUserDetailVo();
+                vo.setUserId(userId);
+                FsUserScrm user = fsUserScrmMapper.selectFsUserByUserId(userId);
+                if (user != null) {
+                    vo.setUserName(user.getNickName() != null ? user.getNickName() : (user.getNickName() != null ? user.getNickName() : "未知用户"));
+                } else {
+                    vo.setUserName("未知用户");
+                }
+                // 查询用户和销售关系
+                FsUserCompanyUser queryUserCompanyUser = new FsUserCompanyUser();
+                queryUserCompanyUser.setUserId(userId);
+                List<FsUserCompanyUser> userCompanyUserList = fsUserCompanyUserMapper.selectFsUserCompanyUserList(queryUserCompanyUser);
+                if (userCompanyUserList != null && !userCompanyUserList.isEmpty()) {
+                    FsUserCompanyUser userCompanyUser = userCompanyUserList.get(0);
+                    if (userCompanyUser.getCompanyId() != null) {
+                        Company company = companyMapper.selectCompanyById(userCompanyUser.getCompanyId());
+                        if (company != null) {
+                            vo.setCompanyName(company.getCompanyName());
+                        }
+                    }
+                    if (userCompanyUser.getCompanyUserId() != null) {
+                        CompanyUser companyUser = companyUserMapper.selectCompanyUserByUserId(userCompanyUser.getCompanyUserId());
+                        if (companyUser != null) {
+                            vo.setSalesName(companyUser.getUserName());
+                        }
+                    }
+                }
+                return vo;
+            });
+
+            userDetail.setOrderCount((long) userOrders.size());
+            BigDecimal orderAmount = userOrders.stream()
+                    .filter(o -> "1".equals(o.getIsPay()))
+                    .map(LiveOrder::getPayPrice)
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            userDetail.setOrderAmount(orderAmount);
+        }
+
+        // 转换为列表并排序
+        List<LiveUserDetailVo> result = new ArrayList<>(userDetailMap.values());
+        result.sort((a, b) -> {
+            int compare = b.getOrderAmount().compareTo(a.getOrderAmount());
+            if (compare != 0) return compare;
+            return Long.compare(b.getLiveWatchDuration(), a.getLiveWatchDuration());
+        });
+
+        return result;
+    }
+
+    /**
+     * 查询单品销量统计
+     */
+    private List<ProductSalesVo> getProductSalesList(Long liveId) {
+
+        List<LiveOrder> orders = liveOrderMapper.selectOrderByLiveId(liveId);
+
+        // 按商品ID分组统计
+        Map<Long, ProductSalesVo> productSalesMap = new HashMap<>();
+
+        for (LiveOrder order : orders) {
+            if (!"1".equals(order.getIsPay()) || order.getProductId() == null) {
+                continue;
+            }
+
+            ProductSalesVo productSales = productSalesMap.computeIfAbsent(order.getProductId(), k -> {
+                ProductSalesVo vo = new ProductSalesVo();
+                vo.setProductId(order.getProductId());
+                // 查询商品名称
+                FsStoreProductScrmMapper productMapper = SpringUtils.getBean(FsStoreProductScrmMapper.class);
+                FsStoreProductScrm product = productMapper.selectFsStoreProductById(order.getProductId());
+                if (product != null) {
+                    vo.setProductName(product.getProductName());
+                } else {
+                    vo.setProductName("未知商品");
+                }
+                return vo;
+            });
+
+            productSales.setSalesCount(productSales.getSalesCount() + 1);
+            if (order.getPayPrice() != null) {
+                productSales.setSalesAmount(productSales.getSalesAmount().add(order.getPayPrice()));
+            }
+        }
+
+        List<ProductSalesVo> result = new ArrayList<>(productSalesMap.values());
+        result.sort((a, b) -> b.getSalesAmount().compareTo(a.getSalesAmount()));
+
+        return result;
+    }
+
 }

+ 43 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java

@@ -11,6 +11,7 @@ import com.fs.hisStore.domain.FsStoreProductScrm;
 import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
 import com.fs.live.domain.LiveGoods;
 import com.fs.live.mapper.LiveGoodsMapper;
+import com.fs.live.service.ILiveAutoTaskService;
 import com.fs.live.service.ILiveGoodsService;
 import com.fs.live.vo.LiveGoodsListVo;
 import com.fs.live.vo.LiveGoodsVo;
@@ -37,6 +38,8 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
 
     @Autowired
     private LiveGoodsMapper baseMapper;
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
 
     /**
      * 查询直播商品
@@ -105,8 +108,17 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
      * @return 结果
      */
     @Override
+    @org.springframework.transaction.annotation.Transactional
     public int deleteLiveGoodsByGoodsIds(Long[] goodsIds)
     {
+        // 联动删除:删除相关的自动化任务
+        for (Long goodsId : goodsIds) {
+            LiveGoods goods = baseMapper.selectLiveGoodsByGoodsId(goodsId);
+            if (goods != null && goods.getLiveId() != null) {
+                liveAutoTaskService
+                        .deleteAutoTasksByGoodsId(goodsId, goods.getLiveId());
+            }
+        }
         return baseMapper.deleteLiveGoodsByGoodsIds(goodsIds);
     }
 
@@ -117,8 +129,16 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
      * @return 结果
      */
     @Override
+    @org.springframework.transaction.annotation.Transactional
     public int deleteLiveGoodsByGoodsId(Long goodsId)
     {
+        // 先查询商品信息,获取liveId
+        LiveGoods goods = baseMapper.selectLiveGoodsByGoodsId(goodsId);
+        if (goods != null && goods.getLiveId() != null) {
+            // 删除相关的自动化任务
+            liveAutoTaskService
+                    .deleteAutoTasksByGoodsId(goodsId, goods.getLiveId());
+        }
         return baseMapper.deleteLiveGoodsByGoodsId(goodsId);
     }
 
@@ -276,7 +296,18 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
     }
 
     @Override
+    @org.springframework.transaction.annotation.Transactional
     public R handleDeleteSelected(LiveGoodsListVo listVo) {
+        // 联动删除:删除相关的自动化任务
+        if (listVo.getGoodsIds() != null && !listVo.getGoodsIds().isEmpty()) {
+            Long liveId = listVo.getLiveId() != null ? Long.valueOf(listVo.getLiveId()) : null;
+            for (Long goodsId : listVo.getGoodsIds()) {
+                if (liveId != null) {
+                    liveAutoTaskService
+                            .deleteAutoTasksByGoodsId(goodsId, liveId);
+                }
+            }
+        }
         int deleteCount = baseMapper.deleteBatchList(listVo);
         if (deleteCount > 0) {
             return R.ok("操作成功");
@@ -306,7 +337,19 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
     }
 
     @Override
+    @org.springframework.transaction.annotation.Transactional
     public R handleDeleteSelectedAdmin(LiveGoodsListVo listVo) {
+        // 联动删除:删除相关的自动化任务
+        if (listVo.getGoodsIds() != null && !listVo.getGoodsIds().isEmpty()) {
+            // 需要查询每个商品的liveId
+            for (Long goodsId : listVo.getGoodsIds()) {
+                LiveGoods goods = baseMapper.selectLiveGoodsByGoodsId(goodsId);
+                if (goods != null && goods.getLiveId() != null) {
+                    liveAutoTaskService
+                            .deleteAutoTasksByGoodsId(goodsId, goods.getLiveId());
+                }
+            }
+        }
         baseMapper.handleDeleteSelectedAdmin(listVo);
         return R.ok();
     }

+ 36 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveLotteryConfServiceImpl.java

@@ -12,6 +12,7 @@ import com.fs.live.mapper.LiveLotteryRegistrationMapper;
 import com.fs.live.param.LiveLotteryProduct;
 import com.fs.live.param.LiveLotteryProductSaveParam;
 import com.fs.live.param.LotteryPO;
+import com.fs.live.service.ILiveAutoTaskService;
 import com.fs.live.service.ILiveLotteryConfService;
 import com.fs.live.vo.LiveLotteryConfVo;
 import com.fs.live.vo.LiveLotteryProductListVo;
@@ -46,6 +47,8 @@ public class LiveLotteryConfServiceImpl implements ILiveLotteryConfService {
 
     @Autowired
     private LiveLotteryConfMapper baseMapper;
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
     /**
      * 查询直播抽奖配置
      *
@@ -117,8 +120,21 @@ public class LiveLotteryConfServiceImpl implements ILiveLotteryConfService {
      * @return 结果
      */
     @Override
+    @Transactional
     public int deleteLiveLotteryConfByLotteryIds(Long[] lotteryIds)
     {
+        // 联动删除:删除相关的自动化任务和缓存
+        for (Long lotteryId : lotteryIds) {
+            LiveLotteryConf conf = baseMapper.selectLiveLotteryConfByLotteryId(lotteryId);
+            if (conf != null && conf.getLiveId() != null) {
+                // 删除相关的自动化任务
+                liveAutoTaskService
+                        .deleteAutoTasksByLotteryId(lotteryId, conf.getLiveId());
+                // 删除抽奖缓存
+                liveAutoTaskService
+                        .deleteLotteryCache(lotteryId, conf.getLiveId());
+            }
+        }
         return baseMapper.deleteLiveLotteryConfByLotteryIds(lotteryIds);
     }
 
@@ -132,6 +148,16 @@ public class LiveLotteryConfServiceImpl implements ILiveLotteryConfService {
     @Transactional
     public int deleteLiveLotteryConfByLotteryId(Long lotteryId)
     {
+        // 先查询抽奖配置,获取liveId
+        LiveLotteryConf conf = baseMapper.selectLiveLotteryConfByLotteryId(lotteryId);
+        if (conf != null && conf.getLiveId() != null) {
+            // 删除相关的自动化任务
+            liveAutoTaskService
+                    .deleteAutoTasksByLotteryId(lotteryId, conf.getLiveId());
+            // 删除抽奖缓存
+            liveAutoTaskService
+                    .deleteLotteryCache(lotteryId, conf.getLiveId());
+        }
         return baseMapper.deleteLiveLotteryConfByLotteryId(lotteryId);
     }
 
@@ -162,6 +188,16 @@ public class LiveLotteryConfServiceImpl implements ILiveLotteryConfService {
     @Override
     @Transactional
     public void delete(Long lotteryId) {
+        // 先查询抽奖配置,获取liveId
+        LiveLotteryConf conf = mapper.selectById(lotteryId);
+        if (conf != null && conf.getLiveId() != null) {
+            // 删除相关的自动化任务
+            liveAutoTaskService
+                    .deleteAutoTasksByLotteryId(lotteryId, conf.getLiveId());
+            // 删除抽奖缓存
+            liveAutoTaskService
+                    .deleteLotteryCache(lotteryId, conf.getLiveId());
+        }
         mapper.deleteById(lotteryId);
     }
 

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

@@ -91,6 +91,7 @@ import com.fs.live.dto.LiveOrderComputeDTO;
 import com.fs.live.dto.LiveOrderCustomerExportDTO;
 import com.fs.live.dto.LiveOrderDeliveryNoteDTO;
 import com.fs.live.dto.LiveOrderItemDTO;
+import com.fs.live.enums.LiveAfterSalesStatusEnum;
 import com.fs.live.mapper.*;
 import com.fs.live.param.*;
 import com.fs.live.service.*;
@@ -656,7 +657,10 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
             log.info("组合码为空,订单ID:" + order.getOrderCode());
             return ;
         }
-
+        // 上面getErpOrder(order) 会更新掉订单信息
+        order.setDeliverySn(null);
+        order.setDeliveryCode(null);
+        order.setDeliveryName(null);
         if (erpOrderService == jSTOrderService) {
             erpOrder.setShop_code(erpConfig.getErpJstShopCode());
         }
@@ -728,6 +732,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
             //增加用户购买次数
             userService.incPayCount(Long.valueOf(order.getUserId()));
+            userService.incPayMoney(order.getPayMoney(), Long.valueOf(order.getUserId()));
 
             order.setStatus(OrderInfoEnum.STATUS_1.getValue());
             order.setPayTime(LocalDateTime.now());
@@ -1133,6 +1138,15 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         IErpOrderService erpOrderService = getErpService();
         FsErpConfig erpConfig = configUtil.generateStructConfigByKey("his.config", FsErpConfig.class);
         LiveOrder order = baseMapper.selectLiveOrderByOrderId(String.valueOf(orderId));
+        LiveAfterSales updateSales = liveAfterSalesMapper.getLiveAfterSalesByOrderId(orderId);
+        if (updateSales == null) {
+            return R.error("售后单不存在");
+        }
+        LiveAfterSales liveMp = new LiveAfterSales();
+        liveMp.setId(updateSales.getId());
+        liveMp.setStatus(LiveAfterSalesStatusEnum.STATUS_4.getValue());
+        liveMp.setSalesStatus(3);
+        liveAfterSalesMapper.updateLiveAfterSales(liveMp);
         if (order == null) {
             return R.error("订单不存在");
         }
@@ -1171,15 +1185,23 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                 }
                 return liveAfterSalesService.applyForAfterSales(order.getUserId(), param);
             } else {
-                jSTOrderService.refundUpdateLive(request);
+                ErpOrderQueryRequert queryRequest = new ErpOrderQueryRequert();
+                queryRequest.setCode(order.getExtendOrderId());
+                ErpOrderQueryResponse response = erpOrderService.getLiveOrder(queryRequest);
+                if (response.getOrders() != null && response.getOrders().size() > 0) {
+                    if (response.getOrders().get(0).getCancle() != null && !response.getOrders().get(0).getCancle()) {
+                        jSTOrderService.refundUpdateLive(request);
+                    }
+                }
             }
         }
-        order.setStatus(OrderInfoEnum.STATUS_2.getValue());
+        order.setStatus(OrderInfoEnum.STATUS_NE2.getValue());
         order.setRefundMoney(order.getPayMoney());
         order.setRefundStatus(String.valueOf(OrderInfoEnum.REFUND_STATUS_2.getValue()));
         liveUserLotteryRecordMapper.updateOrderStatusByOrderId(order.getOrderId(), -2);
         baseMapper.updateLiveOrder(order);
 
+
         //退库存
         //获取订单下的商品
         List<LiveOrderItem> orderItemVOS = liveOrderItemMapper.selectLiveOrderItemByOrderId(order.getOrderId());
@@ -1294,6 +1316,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                 userService.subTuiMoney(storeOrder);
             }
         }
+
         return R.ok();
     }
 
@@ -1345,7 +1368,14 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                 }
                 return liveAfterSalesService.applyForAfterSales(order.getUserId(), param);
             } else {
-                jSTOrderService.refundUpdateLive(request);
+                ErpOrderQueryRequert queryRequest = new ErpOrderQueryRequert();
+                queryRequest.setCode(order.getExtendOrderId());
+                ErpOrderQueryResponse response = erpOrderService.getLiveOrder(queryRequest);
+                if (response.getOrders() != null && response.getOrders().size() > 0) {
+                    if (response.getOrders().get(0).getCancle() != null && !response.getOrders().get(0).getCancle()) {
+                        jSTOrderService.refundUpdateLive(request);
+                    }
+                }
             }
         }
         order.setStatus(OrderInfoEnum.STATUS_NE2.getValue());
@@ -1353,6 +1383,8 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         order.setRefundStatus(String.valueOf(OrderInfoEnum.REFUND_STATUS_2.getValue()));
         liveUserLotteryRecordMapper.updateOrderStatusByOrderId(order.getOrderId(), -2);
         baseMapper.updateLiveOrder(order);
+        //删除用户购买总额
+        userService.incPayMoney(order.getPayMoney().negate(), Long.valueOf(order.getUserId()));
 
         //退库存
         //获取订单下的商品
@@ -1468,6 +1500,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                 userService.subLiveTuiMoney(liveOrder);
             }
         }
+
         return R.ok();
     }
 
@@ -1581,7 +1614,10 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
             erpOrder.setShop_code(erpConfig.getErpJstShopCode());
         }
         ErpOrderResponse response = erpOrderService.addLiveOrder(erpOrder);
-
+        // 上面getErpOrder(order) 会更新掉订单信息
+        order.setDeliverySn(null);
+        order.setDeliveryCode(null);
+        order.setDeliveryName(null);
         //写入日志
         log.info("ErpCreate:" + order.getOrderCode() + ":" + JSONUtil.toJsonStr(response));
         //支付成功后 将订单号写入待发货的REDIS中
@@ -1754,7 +1790,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
             TemplateBean templateBean = TemplateBean.builder()
                     .orderId(order.getOrderId().toString())
-                    .orderCode(order.getOrderCode().toString())
+                    .orderCode(order.getOrderCode())
                     .deliveryId(order.getDeliverySn())
                     .deliveryName(order.getDeliveryName())
                     .userId(Long.valueOf(order.getUserId()))
@@ -2661,17 +2697,19 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         baseMapper.updateLiveOrder(updateEntity);
         //如果是正常签收,更新订单状态
         if(dto.getState().equals("3")&&(dto.getStateEx().equals("301")||dto.getStateEx().equals("302")||dto.getStateEx().equals("304")||dto.getStateEx().equals("311"))){
-            this.getGoods(order.getOrderId());
+            this.finishishOrder(order.getOrderId());
         }
         return R.ok();
     }
 
     @Override
-    public int getGoods(Long orderId) {
+    public int finishishOrder(Long orderId) {
 
         LiveOrder order = baseMapper.selectLiveOrderByOrderId(String.valueOf(orderId));
         if (order==null)throw new CustomException("订单不存在");
-        if (order.getStatus()!= OrderInfoEnum.STATUS_3.getValue())throw new CustomException("非法更改");
+        if (!Objects.equals(order.getStatus(), OrderInfoEnum.STATUS_2.getValue()))throw new CustomException("非法更改");
+        liveOrderLogsService.create(order.getOrderId(), OrderLogEnum.FINISH_ORDER.getValue(),
+                OrderLogEnum.FINISH_ORDER.getDesc());
         LiveOrder updateEntity = new LiveOrder();
         updateEntity.setOrderId(order.getOrderId());
         updateEntity.setUpdateTime(new DateTime());
@@ -2690,8 +2728,16 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 //                OrderLogEnum.FINISH_ORDER.getDesc());
 
         int i = baseMapper.updateLiveOrder(updateEntity);
-        liveUserLotteryRecordMapper.updateOrderStatusByOrderId(order.getOrderId(), OrderInfoEnum.STATUS_3.getValue());
-
+        //模板消息支付成功发布事件
+        TemplateBean templateBean = TemplateBean.builder()
+                .orderId(order.getOrderId().toString())
+                .orderCode(order.getOrderCode().toString())
+                .remark("您的订单已签收成功")
+                .finishTime(order.getFinishTime())
+                .userId(Long.valueOf(order.getUserId()))
+                .templateType(TemplateListenEnum.TYPE_3.getValue())
+                .build();
+        publisher.publishEvent(new TemplateEvent(this, templateBean));
         return i;
     }
 
@@ -3264,6 +3310,21 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         return baseMapper.getDeliveryNote(param);
     }
 
+    @Override
+    public LiveOrder selectOrderByUserIdLimit1(Long userId) {
+        return baseMapper.selectOrderByUserIdLimit1(userId);
+    }
+
+    @Override
+    public List<LiveOrder> selectBankOrder() {
+        return baseMapper.selectBankOrder();
+    }
+
+    @Override
+    public void batchUpdateTime(List<LiveOrder> list) {
+        baseMapper.batchUpdateTime(list);
+    }
+
 
     @Override
     @Transactional(rollbackFor = Throwable.class,propagation = Propagation.REQUIRED)
@@ -3296,7 +3357,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
         // 更改店铺库存
         fsStoreProduct.setStock(fsStoreProduct.getStock()-Integer.parseInt(liveOrder.getTotalNum()));
-        fsStoreProduct.setSales(fsStoreProduct.getSales()+Long.parseLong(liveOrder.getTotalNum()));
+        fsStoreProduct.setSales(fsStoreProduct.getSales()+Integer.parseInt(liveOrder.getTotalNum()));
         fsStoreProductService.updateFsStoreProduct(fsStoreProduct);
         // 更新直播间库存
         goods.setStock(goods.getStock()-Integer.parseInt(liveOrder.getTotalNum()));
@@ -3515,7 +3576,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
     @Override
     @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
     public R cancelOrder(LiveOrder order) {
-        if(order.getStatus() == 1){
+        if(order.getStatus() == OrderInfoEnum.STATUS_0.getValue()){
             LiveOrder liveOrder = baseMapper.selectLiveOrderByOrderId(String.valueOf(order.getOrderId()));
             if(liveOrder == null) return R.error("订单不存在");
             // 更新用户抽奖记录
@@ -3536,6 +3597,14 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
             liveGoodsMapper.updateLiveGoods(goods);
             // 退券
             this.refundCoupon(order);
+            TemplateBean templateBean = TemplateBean.builder()
+                    .orderId(order.getOrderId().toString())
+                    .orderCode(order.getOrderCode().toString())
+                    .remark("您的订单已取消")
+                    .userId(Long.valueOf(order.getUserId()))
+                    .templateType(TemplateListenEnum.TYPE_1.getValue())
+                    .build();
+            publisher.publishEvent(new TemplateEvent(this, templateBean));
 
             return R.ok("操作成功");
         }else {

+ 36 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java

@@ -16,6 +16,7 @@ import com.fs.live.mapper.LiveRedConfMapper;
 import com.fs.live.mapper.LiveRewardRecordMapper;
 import com.fs.live.mapper.LiveUserRedRecordMapper;
 import com.fs.live.param.RedPO;
+import com.fs.live.service.ILiveAutoTaskService;
 import com.fs.live.service.ILiveRedConfService;
 
 import org.slf4j.Logger;
@@ -54,6 +55,8 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
     private LiveMapper liveMapper;
     @Autowired
     private LiveRewardRecordMapper liveRewardRecordMapper;
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
 
     private static final String REDPACKET_REMAININGLOTS_KEY = "live:red:remainingLots:";
     private static final String REDPACKET_REMAININGNUM_KEY = "live:red:remainingNum:";
@@ -133,8 +136,20 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
      * @return 结果
      */
     @Override
+    @Transactional
     public int deleteLiveRedConfByRedIds(Long[] redIds)
     {
+        // 联动删除:删除相关的自动化任务和缓存
+        for (Long redId : redIds) {
+            LiveRedConf conf = baseMapper.selectLiveRedConfByRedId(redId);
+            if (conf != null && conf.getLiveId() != null) {
+                // 删除相关的自动化任务
+                liveAutoTaskService.deleteAutoTasksByRedId(redId, conf.getLiveId());
+                // 删除红包缓存
+                liveAutoTaskService
+                        .deleteRedCache(redId, conf.getLiveId());
+            }
+        }
         return baseMapper.deleteLiveRedConfByRedIds(redIds);
     }
 
@@ -145,8 +160,19 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
      * @return 结果
      */
     @Override
+    @Transactional
     public int deleteLiveRedConfByRedId(Long redId)
     {
+        // 先查询红包配置,获取liveId
+        LiveRedConf conf = baseMapper.selectLiveRedConfByRedId(redId);
+        if (conf != null && conf.getLiveId() != null) {
+            // 删除相关的自动化任务
+            liveAutoTaskService
+                    .deleteAutoTasksByRedId(redId, conf.getLiveId());
+            // 删除红包缓存
+            liveAutoTaskService
+                    .deleteRedCache(redId, conf.getLiveId());
+        }
         return baseMapper.deleteLiveRedConfByRedId(redId);
     }
 
@@ -177,6 +203,16 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
     @Override
     @Transactional
     public void delete(Long redId) {
+        // 先查询红包配置,获取liveId
+        LiveRedConf conf = baseMapper.selectById(redId);
+        if (conf != null && conf.getLiveId() != null) {
+            // 删除相关的自动化任务
+            liveAutoTaskService
+                    .deleteAutoTasksByRedId(redId, conf.getLiveId());
+            // 删除红包缓存
+            liveAutoTaskService
+                    .deleteRedCache(redId, conf.getLiveId());
+        }
         baseMapper.deleteById(redId);
     }
 

+ 36 - 21
fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java

@@ -22,6 +22,7 @@ import com.fs.hisStore.domain.FsStoreProductScrm;
 import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
 import com.fs.live.dto.TemplateMessageSendRequestDTO;
 import com.fs.live.enums.MiniAppNotifyTaskStatusEnum;
+import com.fs.live.param.LiveNotifyParam;
 import com.fs.live.vo.LiveVo;
 import com.fs.common.constant.LiveKeysConstant;
 import com.fs.common.core.domain.R;
@@ -144,6 +145,8 @@ public class LiveServiceImpl implements ILiveService
             byId.setDuration(liveVideo.getDuration());
             byId.setVideoId(liveVideo.getVideoId());
             byId.setVideoType(liveVideo.getVideoType());
+            byId.setVideoFileSize(liveVideo.getFileSize());
+            byId.setVideoDuration(liveVideo.getDuration());
         }
         return byId;
     }
@@ -182,7 +185,6 @@ public class LiveServiceImpl implements ILiveService
     public List<Live> asyncToCache() {
         // 同步直播间数据到缓存
         List<Live> list = liveList();
-        log.info("开始同步直播间数据到缓存,共{}条数据", list.size());
         ThreadUtil.execute(()->{
             // 清空原有的 ZSet 数据
             redisCache.deleteObject(LiveKeysConstant.LIVE_HOME_PAGE_LIST);
@@ -190,7 +192,6 @@ public class LiveServiceImpl implements ILiveService
                 redisCache.zSetAdd(LiveKeysConstant.LIVE_HOME_PAGE_LIST, JSON.toJSONString(live), live.getCreateTime().getTime());
                 redisCache.expire(LiveKeysConstant.LIVE_HOME_PAGE_LIST, LiveKeysConstant.LIVE_HOME_PAGE_LIST_EXPIRE, TimeUnit.SECONDS);
             }
-            log.info("直播间数据同步到缓存完成");
         });
         return list;
     }
@@ -218,10 +219,8 @@ public class LiveServiceImpl implements ILiveService
 			liveVo.setNowDuration(seconds);
 		}
         ThreadUtil.execute(()->{
-            log.info("同步直播间详情数据到缓存{}", id);
             redisCache.deleteObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_DETAIL, live.getLiveId()));
             redisCache.setCacheObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_DETAIL, live.getLiveId()), liveVo,LiveKeysConstant.LIVE_HOME_PAGE_DETAIL_EXPIRE, TimeUnit.SECONDS);
-            log.info("直播间数据同步到缓存完成");
         });
 
         return liveVo;
@@ -231,10 +230,8 @@ public class LiveServiceImpl implements ILiveService
     public LiveConfigVo asyncToCacheLiveConfig(Long liveId) {
         LiveConfigVo liveConfigVo = currentActivities(liveId);
         ThreadUtil.execute(()->{
-            log.info("同步配置信息到缓存{}", liveConfigVo);
             redisCache.deleteObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG, liveId,liveId));
             redisCache.setCacheObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_DETAIL, liveId), liveConfigVo,LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_EXPIRE, TimeUnit.SECONDS);
-            log.info("直播间数据同步到缓存完成");
         });
         return liveConfigVo;
     }
@@ -245,20 +242,20 @@ public class LiveServiceImpl implements ILiveService
     }
 
     @Override
-    public R subNotifyLive(HashMap<String, Object> param) {
+    public R subNotifyLive(LiveNotifyParam param) {
         LiveMiniprogramSubNotifyTask notifyTask = new LiveMiniprogramSubNotifyTask();
-        notifyTask.setPage("/pages_course/living?liveId=" + param.get("liveId"));
+        notifyTask.setPage("/pages_course/living?liveId=" + param.getLiveId());
         notifyTask.setTaskName("直播间预约提醒");
-        notifyTask.setTemplateId((String) param.get("templateId"));
-        Long userId = Long.valueOf((Integer) param.get("userId"));
+        notifyTask.setTemplateId(param.getTemplateId());
+        Long userId = param.getUserId();
         Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
                 .eq(FsUserWx::getFsUserId, userId)
-                .eq(FsUserWx::getAppId, param.getOrDefault("appid", "wx44beed5640bcb1ba"));
+                .eq(FsUserWx::getAppId, StringUtils.isEmpty(param.getAppId()) ? "wx44beed5640bcb1ba" : param.getAppId()); // 卓美小程序
         FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
         String maOpenId = fsUserWx.getOpenId();
-//        if (StringUtils.isEmpty(maOpenId)) {
-//            maOpenId = (String) param.get("maOpenId");
-//        }
+        if (StringUtils.isEmpty(maOpenId)) {
+            maOpenId = param.getMaOpenId();
+        }
         notifyTask.setTouser(maOpenId);
         notifyTask.setPage(String.valueOf(1));
 
@@ -268,7 +265,7 @@ public class LiveServiceImpl implements ILiveService
         notifyTask.setRetryCount(0);
         notifyTask.setMaxRetries(3);
         Map<String, TemplateMessageSendRequestDTO.TemplateDataValue> data = new HashMap<>();
-        HashMap<String,String> mapData = (HashMap<String, String>) param.get("data");
+        HashMap<String,String> mapData =  param.getData();
         // 定义日期时间格式器,注意与字符串格式完全匹配
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
 
@@ -280,7 +277,7 @@ public class LiveServiceImpl implements ILiveService
         });
         notifyTask.setData(JSON.toJSONString(data));
 //        return R.ok("success");
-        liveMiniprogramSubNotifyTaskMapper.insert(notifyTask);
+//        liveMiniprogramSubNotifyTaskMapper.insert(notifyTask);
 
         return R.ok("success");
     }
@@ -387,6 +384,7 @@ public class LiveServiceImpl implements ILiveService
     @Override
     @Transactional
     public int updateLiveEntity(Live live) {
+        log.error("updateLiveEntity:"+ live.getLiveId());
         return baseMapper.updateLive( live);
     }
 
@@ -496,6 +494,11 @@ public class LiveServiceImpl implements ILiveService
      */
     @Override
     public int updateLive(Live live){
+        Live exist = baseMapper.selectLiveByLiveId(live.getLiveId());
+        if (live.getCompanyId() != null && exist.getCompanyId() != null && !Objects.equals(exist.getCompanyId(), live.getCompanyId())) {
+            log.error("updateLive:"+ live.getLiveId());
+            return -1;
+        }
         live.setUpdateTime(DateUtils.getNowDate());
         List<LiveVideo> videos = liveVideoService.listByLiveId(live.getLiveId(), 1);
         if(!videos.isEmpty()){
@@ -524,6 +527,7 @@ public class LiveServiceImpl implements ILiveService
             live.setLiveConfig(JSON.toJSONString(liveReplayParam));
 
         }
+        log.error("updateLive:" + live.getLiveId());
         int result = baseMapper.updateLive(live);
         liveAutoTaskService.recalcLiveAutoTask(live);
 
@@ -537,8 +541,8 @@ public class LiveServiceImpl implements ILiveService
      * @return 结果
      */
     @Override
-    public int deleteLiveByLiveIds(Long[] liveIds){
-        return baseMapper.deleteLiveByLiveIds(liveIds);
+    public int deleteLiveByLiveIds(Long[] liveIds,Live live){
+        return baseMapper.deleteLiveByLiveIds(liveIds, live);
     }
 
     /**
@@ -659,6 +663,7 @@ public class LiveServiceImpl implements ILiveService
         live.setStatus(1);
         live.setFinishTime(LocalDateTime.now());
         live.setLiveType(2);
+        log.error("closeLiving:" + live.getLiveId());
         baseMapper.updateLive(live);
         return R.ok();
     }
@@ -694,7 +699,7 @@ public class LiveServiceImpl implements ILiveService
             curLive.setRtmpUrl("rtmp://your-srs-server/live/" + streamKey);
             curLive.setUpdateTime(now);
 
-
+            log.error("startLoopPlay:" + live.getLiveId());
             baseMapper.updateLive(curLive);
 
             return R.ok();
@@ -722,6 +727,7 @@ public class LiveServiceImpl implements ILiveService
             // 更新流状态
             curLive.setStatus(2);
             curLive.setUpdateTime(new Date());
+            log.error("stopLoopPlay:" + live.getLiveId());
             baseMapper.updateLive(curLive);
 
             return R.ok();
@@ -733,6 +739,7 @@ public class LiveServiceImpl implements ILiveService
     @Override
     public R handleShelfOrUn(LiveListVo listVo) {
         int updatedCount = baseMapper.updateBatchLiveList(listVo);
+        log.error("有人下架了视频:" + listVo.getLiveIds());
         if (updatedCount > 0) {
             return R.ok("操作成功");
         }
@@ -757,7 +764,10 @@ public class LiveServiceImpl implements ILiveService
         if (exist.getStatus() == 3) {
             return R.error("直播已结束");
         }
-
+        if (live.getCompanyId() != null && exist.getCompanyId() != null && !Objects.equals(exist.getCompanyId(), live.getCompanyId())) {
+            return R.error("您没有权限操作此直播间");
+        }
+        log.error("finishLive:" + live.getLiveId());
         exist.setStatus(3);
         exist.setFinishTime(LocalDateTime.now());
         exist.setUpdateTime(new Date());
@@ -782,12 +792,15 @@ public class LiveServiceImpl implements ILiveService
         if (exist.getIsShow() != 1) {
             return R.error("直播已下架");
         }
+        if (live.getCompanyId() != null && exist.getCompanyId() != null && !Objects.equals(exist.getCompanyId(), live.getCompanyId())) {
+            return R.error("您没有权限操作此直播间");
+        }
         String rtmpPushUrl = generateRtmpPushUrl("rtmp://200149.push.tlivecloud.com", "live", exist.getLiveId().toString());
         String hlvPlayUrl = generateHlvPlayUrl("https://live.test.ifeiyu100.com", "live", exist.getLiveId().toString());
         Date now = new Date();
         exist.setRtmpUrl(rtmpPushUrl);
         exist.setFlvHlsUrl(hlvPlayUrl);
-
+        log.error("startLive:" + live.getLiveId());
         exist.setStatus(2);
         exist.setUpdateTime(now);
         exist.setFinishTime( null);
@@ -808,6 +821,7 @@ public class LiveServiceImpl implements ILiveService
     @Override
     public R handleShelfOrUnAdmin(LiveListVo listVo) {
         baseMapper.handleShelfOrUnAdmin(listVo);
+        log.error("有人下架了视频:" + listVo.getLiveIds());
         return R.ok();
     }
 
@@ -1208,6 +1222,7 @@ public class LiveServiceImpl implements ILiveService
             JSONObject jsonObject = JSONObject.parseObject(result);
             if (200 == jsonObject.getIntValue("code") && jsonObject.getBooleanValue("success")) {
                 live.setIdCardUrl(payload.get("idCardUrl"));
+                log.error("verifyIdInfo:" + live.getLiveId());
                 baseMapper.updateLive(live);
                 return R.ok();
             }

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

@@ -314,7 +314,7 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
         try {
             Long onlineSeconds = liveWatchUser.getOnlineSeconds();
             if(onlineSeconds == null) onlineSeconds = 0L;
-            liveWatchUser.setOnlineSeconds(onlineSeconds + (System.currentTimeMillis() - liveWatchUser.getUpdateTime().getTime()));
+            liveWatchUser.setOnlineSeconds(onlineSeconds + (System.currentTimeMillis() - liveWatchUser.getUpdateTime().getTime()) / 1000);
         } catch (Exception e) {
             log.error("设置在线时长异常:{}", e.getMessage());
         }
@@ -442,7 +442,6 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
                                 JSON::toJSONString
                         ));
                 redisCache.hashPut(hashKey,collect);
-                log.info("同步直播在线人数到缓存完成");
             });
         }
         return liveWatchUserVOS;

+ 5 - 0
fs-service/src/main/java/com/fs/live/vo/FsMyLiveOrderListQueryVO.java

@@ -25,6 +25,7 @@ public class FsMyLiveOrderListQueryVO implements Serializable
     /** 订单ID */
     private Long id;
     private Long orderId;
+    private Long liveId;
 
     /** 订单号 */
     private String orderCode;
@@ -45,10 +46,14 @@ public class FsMyLiveOrderListQueryVO implements Serializable
     private String deliveryId;
 
     private Integer isAfterSales;
+    private Integer discountMoney;
 
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date finishTime;
 
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
     private List<LiveOrderItem> items;
 
 

+ 98 - 0
fs-service/src/main/java/com/fs/live/vo/LiveDataDetailVo.java

@@ -0,0 +1,98 @@
+package com.fs.live.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 直播数据详情VO
+ *
+ * @author fs
+ * @date 2025-01-18
+ */
+@Data
+public class LiveDataDetailVo {
+    /** 视频时长(秒) */
+    private Long videoDuration = 0L;
+
+    /** 累计观看人数 */
+    private Long totalViewers = 0L;
+
+    /** 累计完课人数 */
+    private Long totalCompletedCourses = 0L;
+
+    /** 到课完课率(累计完课人数/累计观看人数) */
+    private BigDecimal totalCompletionRate = BigDecimal.ZERO;
+
+    /** 直播观看人数 */
+    private Long liveViewers = 0L;
+
+    /** >20分钟人数(直播) */
+    private Long liveOver20Minutes = 0L;
+
+    /** >30分钟人数(直播) */
+    private Long liveOver30Minutes = 0L;
+
+    /** 到课完课率直播(>20分钟人数(直播)/直播观看人数) */
+    private BigDecimal liveCompletionRate20 = BigDecimal.ZERO;
+
+    /** 到课完课率直播(>30分钟人数(直播)/直播观看人数) */
+    private BigDecimal liveCompletionRate30 = BigDecimal.ZERO;
+
+    /** 回放观看人数 */
+    private Long playbackViewers = 0L;
+
+    /** >20分钟人数(回放) */
+    private Long playbackOver20Minutes = 0L;
+
+    /** >30分钟人数(回放) */
+    private Long playbackOver30Minutes = 0L;
+
+    /** 到课完课率回放(>20分钟人数(回放)/回放观看人数) */
+    private BigDecimal playbackCompletionRate20 = BigDecimal.ZERO;
+
+    /** 到课完课率回放(>30分钟人数(回放)/回放观看人数) */
+    private BigDecimal playbackCompletionRate30 = BigDecimal.ZERO;
+
+    /** 直播峰值 */
+    private Long livePeak = 0L;
+
+    /** 直播平均时长(秒) */
+    private Long liveAvgDuration = 0L;
+
+    /** 回放平均时长(秒) */
+    private Long playbackAvgDuration = 0L;
+
+    /** 回放完播率(回放平均时长/视频时长) */
+    private BigDecimal playbackFinishRate = BigDecimal.ZERO;
+
+    /** GMV */
+    private BigDecimal gmv = BigDecimal.ZERO;
+
+    /** 付费人数 */
+    private Long paidUsers = 0L;
+
+    /** 付费单数 */
+    private Long paidOrders = 0L;
+
+    /** 峰值转化率 */
+    private BigDecimal peakConversionRate = BigDecimal.ZERO;
+
+    /** 总到课转化率 */
+    private BigDecimal totalViewerConversionRate = BigDecimal.ZERO;
+
+    /** 30min完课转化率 */
+    private BigDecimal completion30MinConversionRate = BigDecimal.ZERO;
+
+    /** 峰值R值 */
+    private BigDecimal peakRValue = BigDecimal.ZERO;
+
+    /** 完课R值 */
+    private BigDecimal completionRValue = BigDecimal.ZERO;
+
+    /** 单品销量统计 */
+    private List<ProductSalesVo> productSalesList;
+}
+
+

+ 4 - 0
fs-service/src/main/java/com/fs/live/vo/LiveOrderVO.java

@@ -255,5 +255,9 @@ public class LiveOrderVO implements Serializable
     //erp推送账号
     private String erpAccount;
 
+    /** 银行交易流水号 */
+    @Excel(name = "银行交易流水号")
+    private String bankTransactionId;
+
 
 }

+ 3 - 0
fs-service/src/main/java/com/fs/live/vo/LiveOrderVoZm.java

@@ -476,6 +476,9 @@ public class LiveOrderVoZm{
     private String createTimeStart;
     private String createTimeEnd;
 
+    /** 银行交易流水号 */
+    @Excel(name = "银行交易流水号")
+    private String bankTransactionId;
 
 
 }

+ 40 - 0
fs-service/src/main/java/com/fs/live/vo/LiveUserDetailVo.java

@@ -0,0 +1,40 @@
+package com.fs.live.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 直播间用户详情VO
+ *
+ * @author fs
+ * @date 2025-01-18
+ */
+@Data
+public class LiveUserDetailVo {
+    /** 用户ID */
+    private Long userId;
+
+    /** 用户名称 */
+    private String userName;
+
+    /** 今天看了直播多长时间(秒) */
+    private Long liveWatchDuration = 0L;
+
+    /** 看了回放多长时间(秒) */
+    private Long playbackWatchDuration = 0L;
+
+    /** 下了几笔单 */
+    private Long orderCount = 0L;
+
+    /** 金额是多少 */
+    private BigDecimal orderAmount = BigDecimal.ZERO;
+
+    /** 对应的分公司 */
+    private String companyName;
+
+    /** 分公司的销售是谁 */
+    private String salesName;
+}
+
+

+ 2 - 0
fs-service/src/main/java/com/fs/live/vo/LiveVo.java

@@ -37,6 +37,8 @@ public class LiveVo {
     private String videoUrl;
     private Long videoId;
     private Integer videoType;
+    private Long videoFileSize;
+    private Long videoDuration;
 
     private String flvHlsUrl;
 

+ 28 - 0
fs-service/src/main/java/com/fs/live/vo/ProductSalesVo.java

@@ -0,0 +1,28 @@
+package com.fs.live.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 单品销量统计VO
+ *
+ * @author fs
+ * @date 2025-01-18
+ */
+@Data
+public class ProductSalesVo {
+    /** 商品ID */
+    private Long productId;
+
+    /** 商品名称 */
+    private String productName;
+
+    /** 销量 */
+    private Long salesCount = 0L;
+
+    /** 销售额 */
+    private BigDecimal salesAmount = BigDecimal.ZERO;
+}
+
+

+ 6 - 6
fs-service/src/main/java/com/fs/qw/service/IQwUserService.java

@@ -168,12 +168,12 @@ public interface IQwUserService
     List<Long> selectQwUserListByCuDeptIdList(QwSop qwSop);
 
 
-    /**
-     * 重启云主机
-     * @param IP 服务器ip
-     * @return
-     */
-    R restartCloudHost(String IP);
+//    /**
+//     * 重启云主机
+//     * @param IP 服务器ip
+//     * @return
+//     */
+//    R restartCloudHost(String IP);
 
     /**
      * 获取企微用户信息

+ 48 - 48
fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java

@@ -6,12 +6,12 @@ import cn.hutool.http.HttpRequest;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.cloud.host.CloudHostConfig;
-import com.cloud.host.PoolInfoEnum;
-import com.ecloud.sdk.ecs.v1.Client;
-import com.ecloud.sdk.ecs.v1.model.VmRebootPath;
-import com.ecloud.sdk.ecs.v1.model.VmRebootRequest;
-import com.ecloud.sdk.ecs.v1.model.VmRebootResponse;
+//import com.cloud.host.CloudHostConfig;
+//import com.cloud.host.PoolInfoEnum;
+//import com.ecloud.sdk.ecs.v1.Client;
+//import com.ecloud.sdk.ecs.v1.model.VmRebootPath;
+//import com.ecloud.sdk.ecs.v1.model.VmRebootRequest;
+//import com.ecloud.sdk.ecs.v1.model.VmRebootResponse;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.CloudHostUtils;
@@ -1381,48 +1381,48 @@ public class QwUserServiceImpl implements IQwUserService
         return R.ok(status.toString());
     }
 
-    @Override
-    public R restartCloudHost(String IP) {
-        // 调用SDK必须要的参数
-        String serverId = "";
-        String poolId = "";
-        String accessKey= "";
-        String secretKey ="";
-        try {
-            String bodyData = HttpRequest.get("http://watch.ylrzcloud.com/prod-api/server/getServerId?ipAddress="+IP)
-                    .execute().body();
-            QwFsServerBindResult qwFsServerBindResult = JSON.parseObject(bodyData, QwFsServerBindResult.class);
-            if(qwFsServerBindResult.getData() != null){
-                QwFsServerBindResult.Data data = qwFsServerBindResult.getData();
-                serverId = data.getServerId();
-                poolId = PoolInfoEnum.getPoolIdByCityName(data.getCity() != null ? data.getCity().substring(0, data.getCity().length() - 1):"");
-                accessKey = data.getAccesskey();
-                secretKey = data.getSecretKey();
-            }
-        } catch (Exception e){
-            e.printStackTrace();
-            logger.error("获取主机信息异常,服务器IP:{}", IP);
-        }
-
-        if(StringUtils.isNotEmpty(serverId) && StringUtils.isNotEmpty(poolId) && StringUtils.isNotEmpty(accessKey) && StringUtils.isNotEmpty(secretKey)){
-            Client client = CloudHostConfig.createClient(accessKey, secretKey, poolId);
-            VmRebootRequest.Builder requestBuilder = VmRebootRequest.builder();
-            VmRebootPath vmRebootPath = VmRebootPath.builder().serverId(serverId).build();
-            requestBuilder.vmRebootPath(vmRebootPath);
-            VmRebootRequest request = requestBuilder.build();
-            VmRebootResponse result = client.vmReboot(request);
-            System.out.println(result);
-
-            if(!VmRebootResponse.StateEnum.OK.equals(result.getState())){
-                return R.error(501,"重启主机异常," + result.getErrorMessage()).put("errMsg", result);
-            }
-
-            return R.ok();
-        } else {
-            logger.error("重启云主机异常,获取主机信息异常,serverId:{},poolId:{},accessKey:{},secretKey:{}", serverId, poolId, accessKey, secretKey);
-            return R.error(400,"重启云主机异常,获取主机信息异常");
-        }
-    }
+//    @Override
+//    public R restartCloudHost(String IP) {
+//        // 调用SDK必须要的参数
+//        String serverId = "";
+//        String poolId = "";
+//        String accessKey= "";
+//        String secretKey ="";
+//        try {
+//            String bodyData = HttpRequest.get("http://watch.ylrzcloud.com/prod-api/server/getServerId?ipAddress="+IP)
+//                    .execute().body();
+//            QwFsServerBindResult qwFsServerBindResult = JSON.parseObject(bodyData, QwFsServerBindResult.class);
+//            if(qwFsServerBindResult.getData() != null){
+//                QwFsServerBindResult.Data data = qwFsServerBindResult.getData();
+//                serverId = data.getServerId();
+//                poolId = PoolInfoEnum.getPoolIdByCityName(data.getCity() != null ? data.getCity().substring(0, data.getCity().length() - 1):"");
+//                accessKey = data.getAccesskey();
+//                secretKey = data.getSecretKey();
+//            }
+//        } catch (Exception e){
+//            e.printStackTrace();
+//            logger.error("获取主机信息异常,服务器IP:{}", IP);
+//        }
+//
+//        if(StringUtils.isNotEmpty(serverId) && StringUtils.isNotEmpty(poolId) && StringUtils.isNotEmpty(accessKey) && StringUtils.isNotEmpty(secretKey)){
+//            Client client = CloudHostConfig.createClient(accessKey, secretKey, poolId);
+//            VmRebootRequest.Builder requestBuilder = VmRebootRequest.builder();
+//            VmRebootPath vmRebootPath = VmRebootPath.builder().serverId(serverId).build();
+//            requestBuilder.vmRebootPath(vmRebootPath);
+//            VmRebootRequest request = requestBuilder.build();
+//            VmRebootResponse result = client.vmReboot(request);
+//            System.out.println(result);
+//
+//            if(!VmRebootResponse.StateEnum.OK.equals(result.getState())){
+//                return R.error(501,"重启主机异常," + result.getErrorMessage()).put("errMsg", result);
+//            }
+//
+//            return R.ok();
+//        } else {
+//            logger.error("重启云主机异常,获取主机信息异常,serverId:{},poolId:{},accessKey:{},secretKey:{}", serverId, poolId, accessKey, secretKey);
+//            return R.error(400,"重启云主机异常,获取主机信息异常");
+//        }
+//    }
 
     @Override
     public List<QwUser> getQwUserInfo(QwFsUserParam param) {

+ 89 - 0
fs-service/src/main/java/com/fs/utils/TwelveDigitSnowflake.java

@@ -0,0 +1,89 @@
+package com.fs.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TwelveDigitSnowflake {
+    private final long START_TIMESTAMP = 1672502400000L;
+    private final long MACHINE_BIT = 5;
+    private final long SEQUENCE_BIT = 7;
+    private final long TIMESTAMP_BIT = 30;
+    private final long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
+    private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
+
+    private final long MACHINE_LEFT = SEQUENCE_BIT;
+    private final long TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT;
+
+    private long machineId;
+    private long sequence = 0L;
+    private long lastTimeStamp = -1L;
+
+    // 构造函数,传入机器ID
+    public TwelveDigitSnowflake(long machineId) {
+        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
+            throw new IllegalArgumentException("机器ID超出范围");
+        }
+        this.machineId = machineId;
+    }
+
+    // 生成下一个ID
+    public synchronized long nextId() {
+        long currentTimeStamp = getCurrentTimeStamp();
+
+        // 处理时钟回拨
+        if (currentTimeStamp < lastTimeStamp) {
+            long offset = lastTimeStamp - currentTimeStamp;
+            if (offset <= 5) {
+                try {
+                    wait(offset);
+                    currentTimeStamp = getCurrentTimeStamp();
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            } else { // 超过5毫秒回拨,抛出异常
+                throw new RuntimeException("时钟回拨异常,无法生成ID");
+            }
+        }
+
+        if (currentTimeStamp == lastTimeStamp) {
+            sequence = (sequence + 1) & MAX_SEQUENCE;
+            if (sequence == 0) {
+                currentTimeStamp = getNextMill();
+            }
+        } else {
+            sequence = 0L;
+        }
+
+        lastTimeStamp = currentTimeStamp;
+
+        // 组合ID:时间戳 + 机器ID + 序列号
+        long id = ((currentTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT)
+                | (machineId << MACHINE_LEFT)
+                | sequence;
+        return id;
+    }
+
+    private long getNextMill() {
+        long mill = getCurrentTimeStamp();
+        while (mill <= lastTimeStamp) {
+            mill = getCurrentTimeStamp();
+        }
+        return mill;
+    }
+
+    private long getCurrentTimeStamp() {
+        return System.currentTimeMillis();
+    }
+
+    public static void main(String[] args) {
+        TwelveDigitSnowflake snowflake = new TwelveDigitSnowflake(1);
+        Map<String,Long> check=new HashMap<>();
+        for (int i = 0; i < 10000000; i++) {
+            long id = snowflake.nextId();
+            if(check.containsKey(String.valueOf(id))){
+                throw new RuntimeException("雪花ID重复啦!!!!!!!");
+            }
+            check.put(String.valueOf(id),0L);
+        }
+    }
+}

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

@@ -64,7 +64,7 @@ fs :
   h5CommonApi: http://172.16.16.7:7771
   jwt:
     # 加密秘钥
-    secret: f4e2e52034348f86b67cde581c0f9eb5
+    secret: f4e2e52034348f86b6d81e581c0f9eb5
     # token有效时长,7天,单位秒
     expire: 31536000
     header: AppToken

+ 6 - 0
fs-service/src/main/resources/application-config-druid-hzyy.yml

@@ -62,6 +62,12 @@ watch:
 fs :
   commonApi: http://127.0.0.1:7771
   h5CommonApi: http://127.0.0.1:7771
+  jwt:
+    # 加密秘钥
+    secret: f4h2z52034348y86y67cde581c0f9eb5
+    # token有效时长,7天,单位秒
+    expire: 31536000
+    header: AppToken
 nuonuo:
   key: 10924508
   secret: A2EB20764D304D16

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

@@ -53,7 +53,7 @@ fs :
   commonApi: http://172.27.0.17:8010
   jwt:
     # 加密秘钥
-    secret: f4e2e52034348f86b67cde581c0f9eb5
+    secret: fzbt-zxqzbl
     # token有效时长,7天,单位秒
     expire: 31536000
     header: AppToken

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

@@ -67,7 +67,7 @@ fs :
   commonApi: http://172.27.0.7:8010
   jwt:
     # 加密秘钥
-    secret: f4e2e52034348f86b67cde581c0f9eb5
+    secret: myhk-zxqzbl
     # token有效时长,7天,单位秒
     expire: 31536000
     header: AppToken

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

@@ -39,16 +39,16 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                  url: jdbc:mysql://gz-cdb-ofgnuz1n.sql.tencentcdb.com:26872/fs_his?allowMultiQueries=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                  url: jdbc:mysql://172.16.16.40:3306/fs_his?allowMultiQueries=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                   username: root
                   password: Ylrz_1q2w3e4r5t6y
                 # 从库数据源
                 slave:
                     # 从数据源开关/默认关闭
-                    enabled: false
-                    url:
-                    username:
-                    password:
+                    enabled: true
+                    url: jdbc:mysql://172.16.0.24:3306/fs_his?allowMultiQueries=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrz_1q2w3e4r5t6y
                 # 初始连接数
                 initialSize: 5
                 # 最小连接池数量

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

@@ -18,7 +18,7 @@
             left join fs_course_watch_log cu on cu.period_id = ctp.period_id
         </if>
         <where>
-            ctc.del_flag ='0' and ctp.del_flag ='0'
+            ctc.del_flag ='0'  and (ctp.del_flag != '1' or ctp.del_flag is null)
             <if test="params.trainingCampName != null and params.trainingCampName != ''">
                 and ctc.training_camp_name like concat('%',#{params.trainingCampName},'%')
             </if>

+ 10 - 1
fs-service/src/main/resources/mapper/his/FsAdvMapper.xml

@@ -18,10 +18,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="advType"    column="adv_type"    />
         <result property="showType"    column="show_type"    />
         <result property="activeId"    column="active_id"    />
+        <result property="originalId"    column="original_id"    />
     </resultMap>
 
     <sql id="selectFsAdvVo">
-        select adv_id, adv_title, image_url, adv_url,app_adv_url, content, create_time, update_time, status, sort, adv_type, show_type, active_id from fs_adv
+        select adv_id, adv_title, image_url, adv_url,app_adv_url, content, create_time, update_time, status, sort,
+               adv_type, show_type, active_id,original_id from fs_adv
     </sql>
 
     <select id="selectFsAdvList" parameterType="FsAdv" resultMap="FsAdvResult">
@@ -35,6 +37,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="sort != null  and sort != ''"> and sort = #{sort}</if>
             <if test="advType != null "> and adv_type = #{advType}</if>
             <if test="showType != null "> and show_type = #{showType}</if>
+            <if test="originalId != null and originalId != ''"> and original_id = #{originalId}</if>
         </where>
         order by status desc,sort,adv_id desc
     </select>
@@ -43,6 +46,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectFsAdvVo"/>
         where adv_id = #{advId}
     </select>
+    <select id="selectFsAppAdvList" resultType="com.fs.his.domain.FsAdv">
+        select * from fs_adv where status = 1 and adv_type = 11 and show_type = 4 order by sort asc
+    </select>
 
     <insert id="insertFsAdv" parameterType="FsAdv" useGeneratedKeys="true" keyProperty="advId">
         insert into fs_adv
@@ -59,6 +65,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="advType != null">adv_type,</if>
             <if test="showType != null">show_type,</if>
             <if test="activeId != null">active_id,</if>
+            <if test="originalId != null">original_id,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="advTitle != null and advTitle != ''">#{advTitle},</if>
@@ -73,6 +80,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="advType != null">#{advType},</if>
             <if test="showType != null">#{showType},</if>
             <if test="activeId != null">#{activeId},</if>
+            <if test="originalId != null">#{originalId},</if>
          </trim>
     </insert>
 
@@ -91,6 +99,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="advType != null">adv_type = #{advType},</if>
             <if test="showType != null">show_type = #{showType},</if>
             <if test="activeId != null">active_id = #{activeId},</if>
+            <if test="originalId != null and originalId != ''">original_id = #{originalId},</if>
         </trim>
         where adv_id = #{advId}
     </update>

+ 6 - 0
fs-service/src/main/resources/mapper/his/FsPackageOrderMapper.xml

@@ -320,4 +320,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectOutTimeOrderList" resultType="com.fs.his.domain.FsPackageOrder">
         select * from fs_package_order  where status = 1 AND NOW() &gt; DATE_ADD(create_time, INTERVAL ${unPayTime} MINUTE)
     </select>
+    <select id="selectInformationCollectionByStoreOrderId" resultType="com.fs.his.vo.FsUserInfoCollectionAndStoreOrderVo">
+        SELECT ic.* FROM `fs_package_order` po
+                             INNER  JOIN fs_store_order so ON po.store_order_id = so.order_id
+                             INNER  JOIN fs_user_information_collection ic ON po.order_id = ic.package_order_id
+        WHERE po.store_order_id = #{orderId} ORDER BY po.order_id desc LIMIT 1
+    </select>
 </mapper>

+ 4 - 0
fs-service/src/main/resources/mapper/his/FsUserInformationCollectionMapper.xml

@@ -54,6 +54,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="jsonInfo != null  and jsonInfo != ''"> and json_info = #{jsonInfo}</if>
             <if test="userConfirm != null "> and user_confirm = #{userConfirm}</if>
             <if test="doctorConfirm != null "> and doctor_confirm = #{doctorConfirm}</if>
+            <if test="packageOrderId != null "> and package_order_id = #{packageOrderId}</if>
+            <if test="packageOrderCode != null and packageOrderCode !=''"> and package_order_code = #{packageOrderCode}</if>
         </where>
         order by id desc
     </select>
@@ -96,6 +98,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 <if test="maps.doctorType2Id != null and maps.doctorType2Id != ''"> and fui.doctor_type2_id = #{maps.doctorType2Id}</if>
                 <if test="maps.doctorType2Confirm != null"> and fui.doctor_type2_confirm = #{maps.doctorType2Confirm}</if>
                 <if test="maps.packageOrderCode != null  and maps.packageOrderCode != ''"> and fui.package_order_code = #{maps.packageOrderCode}</if>
+                <if test="maps.patientName != null  and maps.patientName != ''"> and fp.patient_name like concat(#{maps.patientName},"%")</if>
             </where>
         order by id desc
     </select>
@@ -106,6 +109,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="maps.doctorId != null and maps.doctorId != ''"> and fui.doctor_id = #{maps.doctorId}</if>
             <if test="maps.doctorConfirm != null"> and fui.doctor_confirm = #{maps.doctorConfirm}</if>
             <if test="maps.packageOrderCode != null  and maps.packageOrderCode != ''"> and fui.package_order_code = #{maps.packageOrderCode}</if>
+            <if test="maps.patientName != null  and maps.patientName != ''"> and fp.patient_name like concat(#{maps.patientName},"%")</if>
         </where>
         order by id desc
     </select>

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

@@ -985,7 +985,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </foreach>
     </select>
     <select id="selectFsStoreOrderListVOByErpAccount" resultType="com.fs.hisStore.vo.FsStoreOrderVO">
-        select o.*,u.phone,u.register_code,u.register_date,u.source, c.company_name ,cu.nick_name as company_user_nick_name ,cu.phonenumber as company_usere_phonenumber,,df.login_account as erp_account,
+        select o.*,u.phone,u.register_code,u.register_date,u.source, c.company_name ,cu.nick_name as company_user_nick_name ,cu.phonenumber as company_usere_phonenumber,df.login_account as erp_account,
+        sp_latest.bank_transaction_id as bankTransactionId
         from fs_store_order_scrm o
             left join fs_user u on o.user_id=u.user_id
             left join company c on c.company_id=o.company_id
@@ -1000,6 +1001,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         sp.*,
         ROW_NUMBER() OVER (PARTITION BY sp.business_code ORDER BY sp.create_time DESC) as rn
         FROM fs_store_payment_scrm sp
+        WHERE sp.business_code IS NOT NULL
         ) 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>
@@ -1015,6 +1017,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="maps.orderCode != null and  maps.orderCode !=''">
                 and o.order_code like CONCAT('%',#{maps.orderCode},'%')
             </if>
+            <if test="maps.bankTransactionId != null and  maps.bankTransactionId !=''">
+                and sp_latest.bank_transaction_id like CONCAT('%',#{maps.bankTransactionId},'%')
+            </if>
             <if test="maps.isPayRemain != null">
                 and o.is_pay_remain =#{maps.isPayRemain}
             </if>
@@ -1181,6 +1186,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="maps.orderCode != null and  maps.orderCode !=''">
                 and o.order_code like CONCAT('%',#{maps.orderCode},'%')
             </if>
+            <if test="maps.bankTransactionId != null and  maps.bankTransactionId !=''">
+                and sp_latest.bank_transaction_id like CONCAT('%',#{maps.bankTransactionId},'%')
+            </if>
             <if test="maps.isPayRemain != null">
                 and o.is_pay_remain =#{maps.isPayRemain}
             </if>
@@ -1324,6 +1332,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="maps.orderCode != null and  maps.orderCode !=''">
                 and o.order_code like CONCAT('%',#{maps.orderCode},'%')
             </if>
+            <if test="maps.bankTransactionId != null and  maps.bankTransactionId !=''">
+                and sp_latest.bank_transaction_id like CONCAT('%',#{maps.bankTransactionId},'%')
+            </if>
             <if test="maps.isPayRemain != null">
                 and o.is_pay_remain =#{maps.isPayRemain}
             </if>
@@ -1468,6 +1479,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="maps.orderCode != null and  maps.orderCode !=''">
                 and o.order_code like CONCAT('%',#{maps.orderCode},'%')
             </if>
+            <if test="maps.bankTransactionId != null and  maps.bankTransactionId !=''">
+                and sp_latest.bank_transaction_id like CONCAT('%',#{maps.bankTransactionId},'%')
+            </if>
             <if test="maps.isPayRemain != null">
                 and o.is_pay_remain =#{maps.isPayRemain}
             </if>
@@ -1576,7 +1590,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectFsStoreOrderListVO" resultType="com.fs.hisStore.vo.FsStoreOrderVO">
         select o.id,o.order_code,o.extend_order_id,o.pay_order_id,o.bank_order_id,o.user_id,o.real_name,o.user_phone,o.user_address,o.cart_id,o.freight_price,o.total_num,o.total_price,o.total_postage,o.pay_price,o.pay_postage,o.pay_delivery,o.pay_money,o.deduction_price,o.coupon_id,o.coupon_price,o.paid,o.pay_time,o.pay_type,o.create_time,o.update_time,o.status,o.refund_status,o.refund_reason_wap_img,o.refund_reason_wap_explain,o.refund_reason_time,o.refund_reason_wap,o.refund_reason,o.refund_price,o.delivery_sn,o.delivery_name,o.delivery_type,o.delivery_id,o.gain_integral,o.use_integral,o.pay_integral,o.back_integral,o.mark,o.is_del,o.remark,o.verify_code,o.store_id,o.shipping_type,o.is_channel,o.is_remind,o.is_sys_del,o.is_prescribe,o.prescribe_id,o.company_id,o.company_user_id,o.is_package,o.package_json,o.order_type,o.package_id,o.finish_time,o.delivery_status,o.delivery_pay_status,o.delivery_time,o.delivery_pay_time,o.delivery_pay_money,o.tui_money,o.tui_money_status,o.delivery_import_time,o.tui_user_id,o.tui_user_money_status,o.order_create_type,o.store_house_code,o.dept_id,o.is_edit_money,o.customer_id,o.is_pay_remain,o.delivery_send_time,o.certificates,o.upload_time,o.item_json,o.schedule_id,o.delivery_pay_type,o.order_visit,o.service_fee,o.cycle,o.prescribe_price,o.follow_doctor_id,o.follow_time,o.user_coupon_id,o.order_medium,o.erp_phone
         ,u.phone,u.register_code,u.register_date,u.source, c.company_name ,cu.nick_name as company_user_nick_name ,cu.phonenumber as company_usere_phonenumber
-        , csc.name miniProgramName,fsp.cost, fspc.cate_name,spavs.bar_code
+        , csc.name miniProgramName,fsp.cost, fspc.cate_name,spavs.bar_code, sp_latest.bank_transaction_id as bankTransactionId
         from fs_store_order_scrm o
         left join fs_user u on o.user_id=u.user_id
         left join company c on c.company_id=o.company_id
@@ -1611,6 +1625,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="maps.orderCode != null and  maps.orderCode !=''">
                 and o.order_code like CONCAT('%',#{maps.orderCode},'%')
             </if>
+            <if test="maps.bankTransactionId != null and  maps.bankTransactionId !=''">
+                and sp_latest.bank_transaction_id like CONCAT('%',#{maps.bankTransactionId},'%')
+            </if>
             <if test="maps.isPayRemain != null">
                 and o.is_pay_remain =#{maps.isPayRemain}
             </if>
@@ -1766,6 +1783,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="maps.orderCode != null and  maps.orderCode !=''">
                 and o.order_code like CONCAT('%',#{maps.orderCode},'%')
             </if>
+            <if test="maps.bankTransactionId != null and  maps.bankTransactionId !=''">
+                and sp_latest.bank_transaction_id like CONCAT('%',#{maps.bankTransactionId},'%')
+            </if>
             <if test="maps.isPayRemain != null">
                 and o.is_pay_remain =#{maps.isPayRemain}
             </if>

+ 24 - 0
fs-service/src/main/resources/mapper/hisStore/FsStorePaymentScrmMapper.xml

@@ -200,5 +200,29 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </if>
     </select>
 
+    <select id="getPaymentUsetInfoList" resultType="com.fs.hisStore.vo.FsStorePaymentUsetVo">
+        SELECT
+            sp.bank_transaction_id,
+            sp.open_id,
+            sp.app_id,
+            CASE
+                WHEN TRIM(fu.phone) IS NOT NULL AND TRIM(fu.phone) != '' THEN fu.phone
+                ELSE CONCAT(
+                        ELT(FLOOR(1 + RAND() * 6), '13', '14', '15', '17', '18', '19'),
+                        LPAD(FLOOR(RAND() * 1000000000), 9, '0')
+                    )
+                END AS phone
+        FROM
+            fs_store_payment_scrm sp
+                LEFT JOIN fs_user fu ON sp.user_id = fu.user_id
+        WHERE
+            sp.status = 1 and sp.is_shipment = 0 and sp.business_type = 1 AND sp.pay_time > '2025-11-27 00:00:00' LIMIT 500
+    </select>
 
+    <update id="batchUpadte">
+        update fs_store_payment_scrm set is_shipment = 1 where bank_transaction_id in
+        <foreach item="bankTransactionId" collection="list" open="(" separator="," close=")">
+            #{bankTransactionId}
+        </foreach>
+    </update>
 </mapper>

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

@@ -407,4 +407,140 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                  order_stats.gmv, order_stats.paidUsers, order_stats.paidOrders, order_stats.salesCount
         ORDER BY l.start_time DESC
     </select>
+
+    <!-- 查询直播间详情数据(SQL方式) -->
+    <select id="selectLiveDataDetailBySql" resultType="com.fs.live.vo.LiveDataDetailVo">
+        SELECT
+            COALESCE(video_duration.total_duration, 0) AS videoDuration,
+            COUNT(DISTINCT lwu.user_id) AS totalViewers,
+            COUNT(DISTINCT CASE
+                WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                THEN lwu.user_id
+            END) AS totalCompletedCourses,
+            CASE
+                WHEN COUNT(DISTINCT lwu.user_id) > 0 THEN
+                    ROUND(COUNT(DISTINCT CASE
+                        WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                        THEN lwu.user_id
+                    END) * 100.0 / COUNT(DISTINCT lwu.user_id), 2)
+                ELSE 0
+            END AS totalCompletionRate,
+            COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END) AS liveViewers,
+            COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1200 THEN lwu.user_id END) AS liveOver20Minutes,
+            COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) AS liveOver30Minutes,
+            CASE
+                WHEN COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END) > 0 THEN
+                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1200 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END), 2)
+                ELSE 0
+            END AS liveCompletionRate20,
+            CASE
+                WHEN COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END) > 0 THEN
+                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END), 2)
+                ELSE 0
+            END AS liveCompletionRate30,
+            COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END) AS playbackViewers,
+            COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 AND lwu.online_seconds >= 1200 THEN lwu.user_id END) AS playbackOver20Minutes,
+            COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) AS playbackOver30Minutes,
+            CASE
+                WHEN COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END) > 0 THEN
+                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 AND lwu.online_seconds >= 1200 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END), 2)
+                ELSE 0
+            END AS playbackCompletionRate20,
+            CASE
+                WHEN COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END) > 0 THEN
+                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END), 2)
+                ELSE 0
+            END AS playbackCompletionRate30,
+            COALESCE(ld.peak_concurrent_viewers, 0) AS livePeak,
+            COALESCE(AVG(CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.online_seconds END), 0) AS liveAvgDuration,
+            COALESCE(AVG(CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.online_seconds END), 0) AS playbackAvgDuration,
+            CASE
+                WHEN COALESCE(video_duration.total_duration, 0) > 0 THEN
+                    ROUND(COALESCE(AVG(CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.online_seconds END), 0) * 100.0 / video_duration.total_duration, 2)
+                ELSE 0
+            END AS playbackFinishRate,
+            COALESCE(order_stats.gmv, 0) AS gmv,
+            COALESCE(order_stats.paidUsers, 0) AS paidUsers,
+            COALESCE(order_stats.paidOrders, 0) AS paidOrders,
+            CASE
+                WHEN COALESCE(ld.peak_concurrent_viewers, 0) > 0 THEN
+                    ROUND(order_stats.paidUsers * 100.0 / ld.peak_concurrent_viewers, 2)
+                ELSE 0
+            END AS peakConversionRate,
+            CASE
+                WHEN COUNT(DISTINCT lwu.user_id) > 0 THEN
+                    ROUND(order_stats.paidUsers * 100.0 / COUNT(DISTINCT lwu.user_id), 2)
+                ELSE 0
+            END AS totalViewerConversionRate,
+            CASE
+                WHEN COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) > 0 THEN
+                    ROUND(order_stats.paidUsers * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1800 THEN lwu.user_id END), 2)
+                ELSE 0
+            END AS completion30MinConversionRate,
+            CASE
+                WHEN COALESCE(ld.peak_concurrent_viewers, 0) > 0 THEN
+                    ROUND(order_stats.gmv / ld.peak_concurrent_viewers, 2)
+                ELSE 0
+            END AS peakRValue,
+            CASE
+                WHEN COUNT(DISTINCT CASE
+                    WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                    THEN lwu.user_id
+                END) > 0 THEN
+                    ROUND(order_stats.gmv / COUNT(DISTINCT CASE
+                        WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                        THEN lwu.user_id
+                    END), 2)
+                ELSE 0
+            END AS completionRValue
+        FROM live l
+        LEFT JOIN live_data ld ON l.live_id = ld.live_id
+        LEFT JOIN live_watch_user lwu ON l.live_id = lwu.live_id
+        LEFT JOIN (
+            SELECT live_id, SUM(COALESCE(duration, 0)) AS total_duration
+            FROM live_video
+            WHERE video_type IN (1, 2)
+            GROUP BY live_id
+        ) video_duration ON l.live_id = video_duration.live_id
+        LEFT JOIN (
+            SELECT
+                live_id,
+                SUM(CASE WHEN is_pay = '1' THEN pay_price ELSE 0 END) AS gmv,
+                COUNT(DISTINCT CASE WHEN is_pay = '1' THEN user_id END) AS paidUsers,
+                SUM(CASE WHEN is_pay = '1' THEN 1 ELSE 0 END) AS paidOrders
+            FROM live_order
+            GROUP BY live_id
+        ) order_stats ON l.live_id = order_stats.live_id
+        WHERE l.live_id = #{liveId}
+    </select>
+
+    <!-- 查询直播间用户详情列表(SQL方式) -->
+    <select id="selectLiveUserDetailListBySql" resultType="com.fs.live.vo.LiveUserDetailVo">
+        SELECT
+            u.user_id AS userId,
+            COALESCE(u.nickname,u.nick_name, '未知用户') AS userName,
+            COALESCE(SUM(CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.online_seconds ELSE 0 END), 0) AS liveWatchDuration,
+            COALESCE(SUM(CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.online_seconds ELSE 0 END), 0) AS playbackWatchDuration,
+            COALESCE(order_info.orderCount, 0) AS orderCount,
+            COALESCE(order_info.orderAmount, 0) AS orderAmount,
+            COALESCE(c.company_name, '') AS companyName,
+            COALESCE(cu.user_name, '') AS salesName
+        FROM live_watch_user lwu
+        LEFT JOIN fs_user u ON lwu.user_id = u.user_id
+        LEFT JOIN (
+            SELECT
+                CAST(user_id AS UNSIGNED) AS user_id,
+                COUNT(DISTINCT order_id) AS orderCount,
+                SUM(CASE WHEN is_pay = '1' THEN pay_price ELSE 0 END) AS orderAmount
+            FROM live_order
+            WHERE live_id = #{liveId} AND user_id IS NOT NULL AND user_id != ''
+            GROUP BY user_id
+        ) order_info ON lwu.user_id = order_info.user_id
+        LEFT JOIN fs_user_company_user fucu ON lwu.user_id = fucu.user_id
+        LEFT JOIN company c ON fucu.company_id = c.company_id
+        LEFT JOIN company_user cu ON fucu.company_user_id = cu.user_id
+        WHERE lwu.live_id = #{liveId}
+        GROUP BY u.user_id, u.nick_name, u.nickname, order_info.orderCount, order_info.orderAmount, c.company_name, cu.user_name
+        ORDER BY order_info.orderAmount DESC, liveWatchDuration DESC
+    </select>
 </mapper>

+ 16 - 0
fs-service/src/main/resources/mapper/live/LiveMapper.xml

@@ -235,6 +235,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <foreach item="liveId" collection="array" open="(" separator="," close=")">
             #{liveId}
         </foreach>
+        <if test="live != null">
+            <if test="live.companyId != null"> and company_id = #{live.companyId}</if>
+        </if>
     </delete>
 
     <update id="updateBatchLiveList" parameterType="com.fs.live.vo.LiveListVo">
@@ -242,6 +245,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <foreach item="liveId" collection="liveVo.liveIds" open="(" separator="," close=")">
             #{liveId}
         </foreach>
+
+        <if test="liveVo.companyId != null">
+            <if test="liveVo.companyId != null"> and company_id = #{liveVo.companyId}</if>
+        </if>
     </update>
 
     <update id="deleteBatchLiveList" parameterType="com.fs.live.vo.LiveListVo">
@@ -250,6 +257,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <foreach item="liveId" collection="liveVo.liveIds" open="(" separator="," close=")">
             #{liveId}
         </foreach>
+        <if test="liveVo.companyId != null">
+            <if test="liveVo.companyId != null"> and company_id = #{liveVo.companyId}</if>
+        </if>
     </update>
 
     <update id="handleShelfOrUnAdmin" parameterType="com.fs.live.vo.LiveListVo">
@@ -257,6 +267,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <foreach item="liveId" collection="liveVo.liveIds" open="(" separator="," close=")">
             #{liveId}
         </foreach>
+        <if test="liveVo.companyId != null">
+            <if test="liveVo.companyId != null"> and company_id = #{liveVo.companyId}</if>
+        </if>
     </update>
 
     <update id="handleDeleteSelectedAdmin" parameterType="com.fs.live.vo.LiveListVo">
@@ -265,6 +278,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <foreach item="liveId" collection="liveVo.liveIds" open="(" separator="," close=")">
             #{liveId}
         </foreach>
+        <if test="liveVo.companyId != null">
+            <if test="liveVo.companyId != null"> and company_id = #{liveVo.companyId}</if>
+        </if>
     </update>
 
     <update id="updateLiveList">

+ 39 - 14
fs-service/src/main/resources/mapper/live/LiveOrderMapper.xml

@@ -482,7 +482,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectLiveOrderListVO" resultType="com.fs.live.vo.LiveOrderVO">
 
     select o.*,u.phone,u.register_code,u.register_date,u.source, c.company_name ,cu.nick_name as company_user_nick_name ,cu.phonenumber as company_usere_phonenumber
-    , csc.name miniProgramName
+    , csc.name miniProgramName, sp_latest.bank_transaction_id as bankTransactionId
     from live_order o
     left join fs_user u on o.user_id=u.user_id
     left join company c on c.company_id=o.company_id
@@ -516,6 +516,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <if test="maps.orderCode != null and  maps.orderCode !=''">
             and o.order_code like CONCAT('%',#{maps.orderCode},'%')
         </if>
+        <if test="maps.bankTransactionId != null and  maps.bankTransactionId !=''">
+            and sp_latest.bank_transaction_id like CONCAT('%',#{maps.bankTransactionId},'%')
+        </if>
         <if test="maps.isPayRemain != null">
             and o.is_pay_remain =#{maps.isPayRemain}
         </if>
@@ -945,7 +948,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             o.total_num,
             o.total_price,
             o.pay_price,
-            o.pay_money,
             o.is_pay,
             o.pay_time,
             o.pay_type,
@@ -973,12 +975,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             o.coupon_price,
             o.cancel_reason,
 
-            <!-- 销售信息 -->
+
             cu.nick_name AS companyUserNickName,
             cu.phonenumber AS companyUserPhone,
             cu.create_time AS companyUserCreateTime,
 
-            <!-- 客户信息 -->
+
             u.user_code AS userCode,
             u.level AS userLevel,
             u.nick_name AS nickName,
@@ -988,7 +990,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             u.status AS userStatus,
             u.update_time AS latestBindTime,
 
-            <!-- 商品信息 -->
+
             p.product_name AS productName,
             p.cost AS costPrice,
             p.price AS price,
@@ -996,13 +998,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             p.prescribe_spec AS productSpec,
             p.prescribe_factory AS supplierName,
 
-            <!-- 门店信息 -->
+
             s.store_name AS storeName,
             s.store_id AS storeId,
 
-            <!-- 门店信息 -->
-            spavs.bar_code,
-            spcs.cate_name
+
+            GROUP_CONCAT(DISTINCT spavs.bar_code SEPARATOR ',') AS bar_codes,
+            spcs.cate_name,
+
+            lop.bank_transaction_id AS bankTransactionId
 
         FROM
             live_order o
@@ -1012,9 +1016,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             LEFT JOIN fs_store_scrm s ON p.store_id = s.store_id
             LEFT JOIN fs_store_product_attr_value_scrm spavs ON p.product_id = spavs.product_id
             LEFT JOIN fs_store_product_category_scrm spcs ON p.cate_id = spcs.cate_id
+            LEFT JOIN (
+                SELECT
+                    sp.*,
+                    ROW_NUMBER() OVER (PARTITION BY sp.business_code ORDER BY sp.create_time DESC) as rn
+                FROM live_order_payment sp
+                WHERE sp.business_code IS NOT NULL
+            ) lop ON lop.business_code = o.order_code AND lop.rn = 1
 
         <where>
-            o.is_del = 0
+            o.is_del = 0 and p.product_id IS NOT NULL
             <if test="orderId != null">
                 AND o.order_id = #{orderId}
             </if>
@@ -1083,6 +1094,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 AND o.user_phone = #{userPhone}
             </if>
         </where>
+        GROUP BY o.order_id
         ORDER BY o.create_time DESC
     </select>
 
@@ -1107,7 +1119,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <select id="getDeliveryNote" resultType="com.fs.live.vo.LiveOrderDeliveryNoteExportVO">
         SELECT
-        CONCAT('AC', o.id) AS orderNumber,
+        CONCAT('AC', o.order_id) AS orderNumber,
         o.user_name AS recipient,
         o.user_phone AS recipientPhone,
         o.user_address AS recipientAddress,
@@ -1133,11 +1145,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         LEFT JOIN company_user cu
         ON cu.user_id = o.company_user_id
         LEFT JOIN (
-        SELECT DISTINCT order_id
+        SELECT DISTINCT business_id as order_id
         FROM live_order_payment
-        WHERE is_pay_remain != 0 AND STATUS != 0
+        WHERE  STATUS != 0
         ) fp
-        ON o.id = fp.order_id
+        ON o.order_id = fp.order_id
         WHERE
         o.is_del = 0 and
         1 = 1
@@ -1281,4 +1293,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{item.orderNumber}
         </foreach>
     </update>
+
+    <update id="batchUpdateTime">
+        UPDATE live_order
+        SET update_time = CASE
+        <foreach collection="list" item="item" separator="">
+            WHEN order_id = #{item.orderId} THEN #{item.updateTime}
+        </foreach>
+        END
+        WHERE order_id IN
+        <foreach collection="list" item="item" open="(" separator="," close=")">
+            #{item.orderId}
+        </foreach>
+    </update>
 </mapper>

+ 1 - 0
fs-service/src/main/resources/mapper/qw/QwGroupChatMapper.xml

@@ -203,6 +203,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="todayJoin != null">today_join = #{todayJoin},</if>
             <if test="todayOut != null">today_out = #{todayOut},</if>
             <if test="allOutGroup != null">all_out_group = #{allOutGroup},</if>
+            <if test="roomid != null">roomid = #{roomid},</if>
         </trim>
         where chat_id = #{chatId}
     </update>

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff