Ver Fonte

Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java

caoliqin há 2 dias atrás
pai
commit
597b1e95aa
57 ficheiros alterados com 1140 adições e 104 exclusões
  1. 38 0
      fs-admin/src/main/java/com/fs/course/controller/FsCourseFinishTempController.java
  2. 30 7
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  3. 5 1
      fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java
  4. 3 3
      fs-admin/src/main/java/com/fs/live/controller/LiveDataController.java
  5. 13 0
      fs-admin/src/main/java/com/fs/qw/controller/QwSopController.java
  6. 35 3
      fs-admin/src/main/java/com/fs/qw/controller/QwSopTempController.java
  7. 111 0
      fs-admin/src/main/java/com/fs/task/MiniProgramSubTask.java
  8. 68 0
      fs-company/src/main/java/com/fs/company/controller/live/LiveDataController.java
  9. 6 3
      fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java
  10. 25 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseFinishTemp.java
  11. 6 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseAnswerLogsMapper.java
  12. 6 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseFinishTempMapper.java
  13. 28 0
      fs-service/src/main/java/com/fs/course/param/FsCourseAnswerLogsParam.java
  14. 26 0
      fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java
  15. 42 37
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  16. 5 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseFinishTempListVO.java
  17. 3 0
      fs-service/src/main/java/com/fs/his/domain/FsUserInformationCollection.java
  18. 8 6
      fs-service/src/main/java/com/fs/his/mapper/FsStorePaymentMapper.java
  19. 20 6
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java
  20. 1 1
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreAfterSalesScrmMapper.java
  21. 3 3
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java
  22. 1 1
      fs-service/src/main/java/com/fs/live/mapper/LiveDataMapper.java
  23. 2 1
      fs-service/src/main/java/com/fs/live/mapper/LiveOrderMapper.java
  24. 2 2
      fs-service/src/main/java/com/fs/live/service/ILiveDataService.java
  25. 8 12
      fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java
  26. 4 4
      fs-service/src/main/java/com/fs/live/service/impl/LiveDataServiceImpl.java
  27. 9 3
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  28. 10 2
      fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java
  29. 1 2
      fs-service/src/main/java/com/fs/live/service/impl/LiveWatchUserServiceImpl.java
  30. 1 0
      fs-service/src/main/java/com/fs/live/vo/FsMyLiveOrderListQueryVO.java
  31. 1 0
      fs-service/src/main/java/com/fs/live/vo/LiveDataDetailVo.java
  32. 1 0
      fs-service/src/main/java/com/fs/live/vo/LiveUserDetailVo.java
  33. 1 0
      fs-service/src/main/java/com/fs/live/vo/ProductSalesVo.java
  34. 11 0
      fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  35. 1 0
      fs-service/src/main/java/com/fs/qw/service/IQwUserService.java
  36. 6 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  37. 28 1
      fs-service/src/main/java/com/fs/sop/domain/QwSop.java
  38. 25 0
      fs-service/src/main/java/com/fs/sop/domain/QwSopTemp.java
  39. 43 0
      fs-service/src/main/java/com/fs/store/dto/ClientCredGrantReqDTO.java
  40. 52 0
      fs-service/src/main/java/com/fs/store/dto/MiniGramSubsMsgResultDTO.java
  41. 68 0
      fs-service/src/main/java/com/fs/store/dto/TemplateMessageSendRequestDTO.java
  42. 36 0
      fs-service/src/main/java/com/fs/store/dto/WeXinAccessTokenDTO.java
  43. 44 0
      fs-service/src/main/java/com/fs/store/enums/MiniAppNotifyTaskStatusEnum.java
  44. 28 0
      fs-service/src/main/java/com/fs/store/service/IWechatMiniProgrService.java
  45. 29 0
      fs-service/src/main/java/com/fs/store/service/impl/IWechatMiniProgrServiceImpl.java
  46. 95 0
      fs-service/src/main/java/com/fs/store/utils/MiniProgramHttp.java
  47. 43 0
      fs-service/src/main/java/com/fs/store/vo/ClientCredGrantReqDTO.java
  48. 36 0
      fs-service/src/main/java/com/fs/store/vo/WeXinAccessTokenDTO.java
  49. 1 1
      fs-service/src/main/resources/application-config-druid-nmgyt.yml
  50. 6 0
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  51. 20 2
      fs-service/src/main/resources/mapper/his/FsStoreOrderMapper.xml
  52. 6 1
      fs-service/src/main/resources/mapper/his/FsUserInformationCollectionMapper.xml
  53. 19 0
      fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml
  54. 1 1
      fs-service/src/main/resources/mapper/hisStore/FsUserScrmMapper.xml
  55. 6 0
      fs-service/src/main/resources/mapper/live/LiveDataMapper.xml
  56. 7 1
      fs-service/src/main/resources/mapper/sop/QwSopMapper.xml
  57. 6 0
      fs-service/src/main/resources/mapper/sop/QwSopTempMapper.xml

+ 38 - 0
fs-admin/src/main/java/com/fs/course/controller/FsCourseFinishTempController.java

@@ -6,14 +6,22 @@ import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.service.impl.CompanyUserServiceImpl;
+import com.fs.company.vo.DocCompanyUserVO;
 import com.fs.course.domain.FsCourseFinishTemp;
 import com.fs.course.service.IFsCourseFinishTempService;
 import com.fs.course.vo.FsCourseFinishTempListVO;
+import com.fs.sop.domain.QwSopTemp;
+import com.fs.voice.utils.StringUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 /**
  * 完课模板Controller
@@ -27,6 +35,8 @@ public class FsCourseFinishTempController extends BaseController
 {
     @Autowired
     private IFsCourseFinishTempService fsCourseFinishTempService;
+    @Autowired
+    private CompanyUserServiceImpl companyUserService;
 
     /**
      * 查询完课模板列表
@@ -37,6 +47,34 @@ public class FsCourseFinishTempController extends BaseController
     {
         startPage();
         List<FsCourseFinishTempListVO> list = fsCourseFinishTempService.selectFsCourseFinishTempListVO(fsCourseFinishTemp);
+        // 收集所有需要查询的用户ID
+        Set<Long> userIds = list.stream()
+                .map(FsCourseFinishTempListVO::getCreateBy)
+                .filter(str -> !StringUtil.strIsNullOrEmpty(str)) // 取反,保留非空值
+                .map(Long::valueOf)
+                .collect(Collectors.toSet());
+
+        if (!userIds.isEmpty()){
+            // 批量查询用户信息
+            Map<Long, DocCompanyUserVO> userMap = companyUserService
+                    .selectDocCompanyUserListByUserIds(userIds)
+                    .stream()
+                    .collect(Collectors.toMap(DocCompanyUserVO::getUserId, Function.identity()));
+
+
+            list.forEach(item->{
+
+                if (!StringUtil.strIsNullOrEmpty(item.getCreateBy())) {
+                    DocCompanyUserVO user = userMap.get(Long.valueOf(item.getCreateBy()));
+                    if (user != null) {
+                        item.setCreateByName(user.getNickName());
+                        item.setCreateByDeptName(user.getDeptName());
+                    }
+                }
+
+            });
+        }
+
         return getDataTable(list);
     }
 

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

@@ -8,6 +8,8 @@ import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.entity.SysRole;
+import com.fs.common.core.domain.entity.SysUser;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
@@ -53,6 +55,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.fs.system.service.ISysRoleService;
 import com.github.pagehelper.PageHelper;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.BeanUtils;
@@ -340,19 +343,19 @@ public class FsStoreOrderScrmController extends BaseController {
         }
         param.setNotHealth(1);
         List<FsStoreOrderErpExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
+
         //对手机号脱敏
         if (list != null) {
-            //获取当前账号角色权限
-            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+            SysRole sysRole = isCheckPermission();
 
             for (FsStoreOrderErpExportVO vo : list) {
-                if (vo.getPhone() != null) {
+                if (vo.getPhone() != null && sysRole.getIsCheckPhone() != 1) {
                     vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
-                if (vo.getUserPhone() != null) {
+                if (vo.getUserPhone() != null && sysRole.getIsCheckPhone() != 1) {
                     vo.setUserPhone(vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
-                if (vo.getUserAddress()!=null){
+                if (vo.getUserAddress()!=null && sysRole.getIsCheckAddress() != 1){
                     vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
                 }
             }
@@ -377,6 +380,25 @@ public class FsStoreOrderScrmController extends BaseController {
         }
     }
 
+    @Autowired
+    private ISysRoleService sysRoleService;
+    private SysRole isCheckPermission() {
+        SysRole sysRole = new SysRole();
+        SysUser user = getLoginUser().getUser();
+        boolean flag = user.isAdmin();
+        if (flag) {
+            sysRole.setIsCheckPhone(1);
+            sysRole.setIsCheckAddress(1);
+        } else {
+            List<SysRole> roles = user.getRoles();
+            if (roles != null && !roles.isEmpty()) {
+                Long[] roleIds = roles.stream().map(SysRole::getRoleId).toArray(Long[]::new);
+                return sysRoleService.getIsCheckPermission(roleIds);
+            }
+        }
+        return sysRole;
+    }
+
 
     /**
      * 导出订单列表(明文)
@@ -453,14 +475,15 @@ public class FsStoreOrderScrmController extends BaseController {
         List<FsStoreOrderItemExportVO> list = orderItemService.selectFsStoreOrderItemListExportVO(param);
         //对手机号脱敏
         if (list != null) {
+            SysRole sysRole = isCheckPermission();
             LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
 
             for (FsStoreOrderItemExportVO vo : list) {
-                if (vo.getUserPhone() != null) {
+                if (vo.getUserPhone() != null && sysRole.getIsCheckPhone() != 1) {
                     String phone = vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{1})", "$1****$2");
                     vo.setUserPhone(phone);
                 }
-                if (vo.getUserAddress()!=null){
+                if (vo.getUserAddress()!=null && sysRole.getIsCheckAddress() != 1){
                     vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
                 }
                 if (!StringUtils.isEmpty(vo.getJsonInfo())) {

+ 5 - 1
fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java

@@ -3,6 +3,7 @@ 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;
@@ -174,7 +175,10 @@ public class MallStoreTask
         // 单个异常影响全部,跳过异常单子
         for (Long id : ids) {
             try {
-                fsStoreOrderService.createOmsOrder(id);
+                R omsOrder = fsStoreOrderService.createOmsOrder(id);
+                if ("500".equals(omsOrder.get("code"))) {
+
+                }
             } catch (Exception e) {
                 log.error("创建商城oms订单失败:"+id);
                 log.error("创建商城oms订单失败:"+e.getMessage());

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

@@ -133,7 +133,7 @@ public class LiveDataController extends BaseController {
     @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
     @GetMapping("/getLiveUserDetailListBySql")
     public R getLiveUserDetailListBySql(@RequestParam Long liveId) {
-        return liveDataService.getLiveUserDetailListBySql(liveId);
+        return liveDataService.getLiveUserDetailListBySql(liveId,null,null);
     }
 
     /**
@@ -167,11 +167,11 @@ public class LiveDataController extends BaseController {
     @Log(title = "直播间用户详情", businessType = BusinessType.EXPORT)
     @GetMapping("/exportLiveUserDetail")
     public AjaxResult exportLiveUserDetail(@RequestParam Long liveId) {
-        List<LiveUserDetailExportVO> list = liveDataService.exportLiveUserDetail(liveId);
+        List<LiveUserDetailExportVO> list = liveDataService.exportLiveUserDetail(liveId,null,null);
         if (list == null || list.isEmpty()) {
             return AjaxResult.error("未找到用户详情数据");
         }
-        
+
         ExcelUtil<LiveUserDetailExportVO> util = new ExcelUtil<>(LiveUserDetailExportVO.class);
         return util.exportExcel(list, "直播间用户详情数据");
     }

+ 13 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwSopController.java

@@ -27,6 +27,7 @@ import org.springframework.web.bind.annotation.*;
 
 import java.io.IOException;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
@@ -53,6 +54,7 @@ public class QwSopController extends BaseController
     @Autowired
     private FsUserCourseVideoMapper fsUserCourseVideoMapper;
 
+
     /**
      * 查询企微sop列表
      */
@@ -60,6 +62,17 @@ public class QwSopController extends BaseController
     @GetMapping("/list")
     public TableDataInfo list(QwSop qwSop)
     {
+        List<String> userIds = qwSop.getUserIds();
+        if (userIds != null && !userIds.isEmpty()) {
+            List<Long> longs = qwUserService.selectQwUserListByCompanyUserIdS(userIds);
+            if (longs != null && !longs.isEmpty()) {
+                qwSop.getQwUserIdList().addAll(longs);
+            }else {
+                return getDataTable(new ArrayList<>());
+            }
+        }
+
+
         startPage();
         List<QwSop> list = qwSopService.selectQwSopList(qwSop);
         return getDataTable(list);

+ 35 - 3
fs-admin/src/main/java/com/fs/qw/controller/QwSopTempController.java

@@ -12,6 +12,8 @@ import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.TimeUtils;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.service.impl.CompanyUserServiceImpl;
+import com.fs.company.vo.DocCompanyUserVO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.qw.vo.SortDayVo;
 import com.fs.sop.domain.QwSopTemp;
@@ -19,14 +21,14 @@ import com.fs.sop.domain.QwSopTempDay;
 import com.fs.sop.params.QwSopShareTempParam;
 import com.fs.sop.service.IQwSopTempService;
 import com.fs.sop.vo.UpdateRedVo;
+import com.fs.voice.utils.StringUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -43,6 +45,8 @@ public class QwSopTempController extends BaseController
     private IQwSopTempService qwSopTempService;
     @Autowired
     private TokenService tokenService;
+    @Autowired
+    private CompanyUserServiceImpl companyUserService;
     /**
      * 查询sop模板列表
      */
@@ -53,6 +57,34 @@ public class QwSopTempController extends BaseController
         startPage();
 //        List<QwSopTemp> list = qwSopTempService.selectQwSopTempList(qwSopTemp);
         List<QwSopTemp> list = qwSopTempService.selectQwSopTempListNew(qwSopTemp);
+        // 收集所有需要查询的用户ID
+        Set<Long> userIds = list.stream()
+                .map(QwSopTemp::getCreateBy)
+                .filter(str -> !StringUtil.strIsNullOrEmpty(str)) // 取反,保留非空值
+                .map(Long::valueOf)
+                .collect(Collectors.toSet());
+
+        if (!userIds.isEmpty()){
+            // 批量查询用户信息
+            Map<Long, DocCompanyUserVO> userMap = companyUserService
+                    .selectDocCompanyUserListByUserIds(userIds)
+                    .stream()
+                    .collect(Collectors.toMap(DocCompanyUserVO::getUserId, Function.identity()));
+
+
+            list.forEach(item->{
+
+                if (!StringUtil.strIsNullOrEmpty(item.getCreateBy())) {
+                    DocCompanyUserVO user = userMap.get(Long.valueOf(item.getCreateBy()));
+                    if (user != null) {
+                        item.setCreateByName(user.getNickName());
+                        item.setCreateByDeptName(user.getDeptName());
+                    }
+                }
+
+            });
+        }
+
         return getDataTable(list);
     }
 

+ 111 - 0
fs-admin/src/main/java/com/fs/task/MiniProgramSubTask.java

@@ -0,0 +1,111 @@
+package com.fs.task;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import com.fs.live.domain.LiveMiniprogramSubNotifyTask;
+import com.fs.live.mapper.LiveMiniprogramSubNotifyTaskMapper;
+import com.fs.store.enums.MiniAppNotifyTaskStatusEnum;
+import com.fs.store.service.IWechatMiniProgrService;
+import com.fs.store.dto.ClientCredGrantReqDTO;
+import com.fs.store.dto.MiniGramSubsMsgResultDTO;
+import com.fs.store.dto.TemplateMessageSendRequestDTO;
+import com.fs.store.dto.WeXinAccessTokenDTO;
+import com.fs.wx.miniapp.config.WxMaProperties;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 小程序订阅通知定时任务
+ */
+@Service("miniProgramSubTask")
+@Slf4j
+@RequiredArgsConstructor
+public class MiniProgramSubTask {
+    private final IWechatMiniProgrService wechatMiniProgrService;
+
+    private final LiveMiniprogramSubNotifyTaskMapper notifyTaskMapper;
+
+    private WxMaProperties.Config config = null;
+
+    @Autowired
+    public void setConfig(WxMaProperties properties) {
+        if(ObjectUtil.isNotNull(properties)){
+            this.config = properties.getConfigs().get(0);
+        }
+    }
+
+
+    /**
+     * 小程序订阅通知
+     */
+    public void notifyMiniLiveAppSub(){
+        log.info("小程序直播订阅通知定时任务");
+        // 先获取所有可用待处理任务
+        List<LiveMiniprogramSubNotifyTask> pendingData = notifyTaskMapper.selectLivePendingData();
+        if(CollectionUtils.isEmpty(pendingData)){
+            log.info("小程序直播阅通知定时任务, 无待处理数据");
+            return;
+        }
+        LocalDateTime now = LocalDateTime.now();
+        for (LiveMiniprogramSubNotifyTask pendingDatum : pendingData) {
+
+            if(pendingDatum.getUpdateTime().isAfter(now)) continue;
+
+            pendingDatum.setUpdateTime(LocalDateTime.now());
+
+            ClientCredGrantReqDTO clientCredGrantReqDTO = new ClientCredGrantReqDTO();
+            clientCredGrantReqDTO.setAppid(config.getAppid());
+            clientCredGrantReqDTO.setSecret(config.getSecret());
+            clientCredGrantReqDTO.setGrant_type("client_credential");
+
+            try{
+                // 获取accessToken
+                WeXinAccessTokenDTO stableToken = wechatMiniProgrService
+                        .getStableToken(clientCredGrantReqDTO);
+
+                String accessToken = stableToken.getAccessToken();
+
+                // 调用微信小程序订阅通知
+                TemplateMessageSendRequestDTO sendRequestDTO = new TemplateMessageSendRequestDTO();
+                sendRequestDTO.setTemplate_id(pendingDatum.getTemplateId());
+                sendRequestDTO.setTouser(pendingDatum.getTouser());
+                sendRequestDTO.setPage(pendingDatum.getPage());
+                TypeReference<Map<String, TemplateMessageSendRequestDTO.TemplateDataValue>> typeReference = new TypeReference<Map<String, TemplateMessageSendRequestDTO.TemplateDataValue>>() {};
+                sendRequestDTO.setData(JSON.parseObject(pendingDatum.getData(),typeReference));
+                MiniGramSubsMsgResultDTO miniGramSubsMsgResultDTO = wechatMiniProgrService.sendSubscribeMsg(accessToken, sendRequestDTO);
+                pendingDatum.setRequestBody(JSON.toJSONString(sendRequestDTO));
+                pendingDatum.setResponseBody(JSON.toJSONString(miniGramSubsMsgResultDTO));
+
+                // 如果推送消息成功
+                if(miniGramSubsMsgResultDTO.getErrcode() == 0){
+                    pendingDatum.setStatus(MiniAppNotifyTaskStatusEnum.SUCCESS.getValue());
+                } else {
+                    // 更新任务状态为执行失败
+                    pendingDatum.setStatus(MiniAppNotifyTaskStatusEnum.FAILED.getValue());
+                    pendingDatum.setErrorMessage(JSON.toJSONString(miniGramSubsMsgResultDTO));
+                    pendingDatum.setRetryCount(pendingDatum.getRetryCount() +1);
+                }
+            }catch (Throwable e){
+                // 更新任务状态为执行失败
+                pendingDatum.setStatus(MiniAppNotifyTaskStatusEnum.FAILED.getValue());
+                pendingDatum.setErrorMessage(ExceptionUtils.getStackTrace(e));
+                pendingDatum.setRetryCount(pendingDatum.getRetryCount() +1);
+                log.error("小程序直播订阅通知定时任务异常: {}", ExceptionUtils.getStackTrace(e));
+            }
+        }
+
+        if(CollectionUtils.isNotEmpty(pendingData)){
+            notifyTaskMapper.updateBatchById(pendingData);
+        }
+
+    }
+}

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

@@ -7,6 +7,7 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CompanyUser;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.SecurityUtils;
 import com.fs.framework.service.TokenService;
@@ -14,6 +15,7 @@ import com.fs.live.domain.LiveData;
 import com.fs.live.param.LiveDataParam;
 import com.fs.live.service.ILiveDataService;
 import com.fs.live.vo.ColumnsConfigVo;
+import com.fs.live.vo.LiveUserDetailExportVO;
 import com.github.pagehelper.PageHelper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -38,6 +40,72 @@ public class LiveDataController extends BaseController
     @Autowired
     private TokenService tokenService;
 
+    /**
+     * 查询直播间详情数据(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, HttpServletRequest request) {
+        CompanyUser user = tokenService.getLoginUser(request).getUser();
+
+        return liveDataService.getLiveUserDetailListBySql(liveId,user.getCompanyId(),user.getUserId());
+    }
+
+    /**
+     * 查询直播间详情数据(查询数据服务器处理方式)
+     * @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);
+    }
+
+
+    /**
+     * 导出直播间用户详情数据
+     * @param liveId 直播间ID
+     * @return Excel文件
+     */
+    @PreAuthorize("@ss.hasPermi('liveData:liveData:export')")
+    @Log(title = "直播间用户详情", businessType = BusinessType.EXPORT)
+    @GetMapping("/exportLiveUserDetail")
+    public AjaxResult exportLiveUserDetail(@RequestParam Long liveId, HttpServletRequest request) {
+        CompanyUser user = tokenService.getLoginUser(request).getUser();
+        List<LiveUserDetailExportVO> list = liveDataService.exportLiveUserDetail(liveId,user.getCompanyId(),user.getUserId());
+        if (list == null || list.isEmpty()) {
+            return AjaxResult.error("未找到用户详情数据");
+        }
+
+        ExcelUtil<LiveUserDetailExportVO> util = new ExcelUtil<>(LiveUserDetailExportVO.class);
+        return util.exportExcel(list, "直播间用户详情数据");
+    }
+
     /**
      * 直播数据页面卡片数据
      */

+ 6 - 3
fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -146,7 +146,7 @@ public class WebSocketServer {
                 redisCache.incr(UNIQUE_VIEWERS_KEY + liveId, 1);
             }
             liveWatchUserVO.setMsgStatus(liveWatchUserVO.getMsgStatus());
-            if (1 == random.nextInt(4)) {
+            if (1 == random.nextInt(10)) {
                 SendMsgVo sendMsgVo = new SendMsgVo();
                 sendMsgVo.setLiveId(liveId);
                 sendMsgVo.setUserId(userId);
@@ -232,7 +232,7 @@ public class WebSocketServer {
 
 
             // 广播离开消息 添加一个概率问题 摇塞子,1-4 当为1的时候广播消息
-            if (1 == new Random().nextInt(4)) {
+            if (1 == new Random().nextInt(10)) {
                 SendMsgVo sendMsgVo = new SendMsgVo();
                 sendMsgVo.setLiveId(liveId);
                 sendMsgVo.setUserId(userId);
@@ -759,7 +759,7 @@ public class WebSocketServer {
         ConcurrentHashMap<Long, Session> room = getRoom(liveId);
         room.forEach((k, v) -> {
             if (v.isOpen()) {
-                sendWithRetry(v,message,7);
+                sendWithRetry(v,message,1);
             }
         });
     }
@@ -855,6 +855,9 @@ public class WebSocketServer {
                 }
                 LiveCouponIssue liveCouponIssue = liveCouponIssueService.selectLiveCouponIssueByCouponId(liveCoupon.getCouponId());
                 LiveCouponIssueRelation relation = liveCouponMapper.selectCouponRelation(task.getLiveId(), liveCouponIssue.getId());
+                if (liveCoupon != null) {
+                    redisCache.setCacheObject(String.format(LiveKeysConstant.LIVE_COUPON_NUM , liveCouponIssue.getId()), liveCouponIssue.getRemainCount().intValue(), 30, TimeUnit.MINUTES);
+                }
                 HashMap<String, Object> data = new HashMap<>();
                 data.put("liveId", task.getLiveId());
                 data.put("couponIssueId", liveCouponIssue.getId());

+ 25 - 0
fs-service/src/main/java/com/fs/course/domain/FsCourseFinishTemp.java

@@ -5,6 +5,7 @@ import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -66,4 +67,28 @@ public class FsCourseFinishTemp extends BaseEntity
      */
     @TableField(exist = false)
     private List<Long> ids;
+
+    @TableField(exist = false)
+    private List<String> userIds = new ArrayList<>();
+
+    public List<String> getUserIds() {
+        if (userIds == null || userIds.isEmpty()) {
+            return userIds;
+        }
+
+        // 直接在原始列表上修改
+        for (int i = 0; i < userIds.size(); i++) {
+            String id = userIds.get(i);
+            if (id != null) {
+                if (id.startsWith("dept_")) {
+                    userIds.set(i, id.substring(5));
+                } else if (id.startsWith("company_")) {
+                    userIds.set(i, id.substring(8));
+                } else if (id.startsWith("user_")) {
+                    userIds.set(i, id.substring(5));
+                }
+            }
+        }
+        return userIds;
+    }
 }

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

@@ -65,6 +65,12 @@ public interface FsCourseAnswerLogsMapper
             "            <if test=\"map.createTime != null \"> and Date(cal.create_time) = #{map.createTime}</if>\n" +
             "<if test=\"map.sTime != null \">  and DATE(cal.create_time) &gt;= DATE(#{map.sTime})</if>\n" +
             "<if test=\"map.eTime != null \">  and DATE(cal.create_time) &lt;= DATE(#{map.eTime})</if>\n" +
+            "            <if test=\"map.companyUserIds != null and !map.companyUserIds.isEmpty()\">\n" +
+            "                AND cal.company_user_id IN\n" +
+            "                <foreach collection='map.companyUserIds' item='item' open='(' separator=',' close=')'>\n" +
+            "                    #{item}\n" +
+            "                </foreach>\n" +
+            "            </if>" +
             "        </where>  " +
             "order by cal.log_id desc  " +
             " </script>")

+ 6 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseFinishTempMapper.java

@@ -87,6 +87,12 @@ public interface FsCourseFinishTempMapper
             "<if test = ' maps.status !=null '> " +
             "and t.status = #{maps.status} " +
             "</if>" +
+            "            <if test=\"userIds != null and !userIds.isEmpty()\">\n" +
+            "                AND create_by IN\n" +
+            "                <foreach collection='userIds' item='item' open='(' separator=',' close=')'>\n" +
+            "                    #{item}\n" +
+            "                </foreach>\n" +
+            "            </if>" +
             " order by t.id desc  "+
             "</script>"})
     List<FsCourseFinishTempListVO> selectFsCourseFinishTempListVO(@Param("maps") FsCourseFinishTemp fsCourseFinishTemp);

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

@@ -1,10 +1,13 @@
 package com.fs.course.param;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
 
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
 import java.util.Set;
 
 @Data
@@ -45,4 +48,29 @@ public class FsCourseAnswerLogsParam  extends BaseEntity  {
      */
     private Set<Long> userIds;
 
+    @TableField(exist = false)
+    private List<String> companyUserIds=new ArrayList<>();
+
+
+    public List<String> getCompanyUserIds() {
+        if (companyUserIds == null || companyUserIds.isEmpty()) {
+            return companyUserIds;
+        }
+
+        // 直接在原始列表上修改
+        for (int i = 0; i < companyUserIds.size(); i++) {
+            String id = companyUserIds.get(i);
+            if (id != null) {
+                if (id.startsWith("dept_")) {
+                    companyUserIds.set(i, id.substring(5));
+                } else if (id.startsWith("company_")) {
+                    companyUserIds.set(i, id.substring(8));
+                } else if (id.startsWith("user_")) {
+                    companyUserIds.set(i, id.substring(5));
+                }
+            }
+        }
+        return companyUserIds;
+    }
+
 }

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

@@ -1,9 +1,11 @@
 package com.fs.course.param;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
@@ -107,4 +109,28 @@ public class FsCourseWatchLogListParam implements Serializable {
     private Long deptId;
     private List<Long> deptIds;
     private String ids;
+
+    @TableField(exist = false)
+    private List<String> userIds = new ArrayList<>();
+
+    public List<String> getUserIds() {
+        if (userIds == null || userIds.isEmpty()) {
+            return userIds;
+        }
+
+        // 直接在原始列表上修改
+        for (int i = 0; i < userIds.size(); i++) {
+            String id = userIds.get(i);
+            if (id != null) {
+                if (id.startsWith("dept_")) {
+                    userIds.set(i, id.substring(5));
+                } else if (id.startsWith("company_")) {
+                    userIds.set(i, id.substring(8));
+                } else if (id.startsWith("user_")) {
+                    userIds.set(i, id.substring(5));
+                }
+            }
+        }
+        return userIds;
+    }
 }

+ 42 - 37
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -1990,44 +1990,49 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
                     return R.error("服务商余额不足,请联系群主服务器充值!");
                 }
 
-                // 发送红包
-                R sendRedPacket = paymentService.sendRedPacket(packetParam);
-                if (sendRedPacket.get("code").equals(200)) {
-                    FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
-                    TransferBillsResult transferBillsResult;
-                    if (sendRedPacket.get("isNew").equals(1)) {
-                        transferBillsResult = (TransferBillsResult) sendRedPacket.get("data");
-                        redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
-                        redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
-                        redPacketLog.setBatchId(transferBillsResult.getTransferBillNo());
-                    } else {
-                        redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
-                        redPacketLog.setBatchId(sendRedPacket.get("batchId").toString());
-                    }
-                    // 添加红包记录
-                    redPacketLog.setCourseId(param.getCourseId());
-                    redPacketLog.setCompanyId(param.getCompanyId());
-                    redPacketLog.setUserId(param.getUserId());
-                    redPacketLog.setVideoId(param.getVideoId());
-                    redPacketLog.setStatus(0);
-                    redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
-                    redPacketLog.setCompanyUserId(param.getCompanyUserId());
-                    redPacketLog.setCreateTime(new Date());
-                    redPacketLog.setAmount(amount);
-                    redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
-                    redPacketLog.setPeriodId(param.getPeriodId());
-                    redPacketLog.setAppId(param.getAppId());
-
-                    redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+             try{
+                 // 发送红包
+                 R sendRedPacket = paymentService.sendRedPacket(packetParam);
+                 if (sendRedPacket.get("code").equals(200)) {
+                     FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+                     TransferBillsResult transferBillsResult;
+                     if (sendRedPacket.get("isNew").equals(1)) {
+                         transferBillsResult = (TransferBillsResult) sendRedPacket.get("data");
+                         redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
+                         redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+                         redPacketLog.setBatchId(transferBillsResult.getTransferBillNo());
+                     } else {
+                         redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                         redPacketLog.setBatchId(sendRedPacket.get("batchId").toString());
+                     }
+                     // 添加红包记录
+                     redPacketLog.setCourseId(param.getCourseId());
+                     redPacketLog.setCompanyId(param.getCompanyId());
+                     redPacketLog.setUserId(param.getUserId());
+                     redPacketLog.setVideoId(param.getVideoId());
+                     redPacketLog.setStatus(0);
+                     redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+                     redPacketLog.setCompanyUserId(param.getCompanyUserId());
+                     redPacketLog.setCreateTime(new Date());
+                     redPacketLog.setAmount(amount);
+                     redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+                     redPacketLog.setPeriodId(param.getPeriodId());
+                     redPacketLog.setAppId(param.getAppId());
+
+                     redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+
+                     // 更新观看记录的奖励类型
+                     log.setRewardType(config.getRewardType());
+                     courseWatchLogMapper.updateFsCourseWatchLog(log);
+
+                     return sendRedPacket;
+                 } else {
+                     return R.error("奖励发送失败,请联系客服");
+                 }
+             }catch (Exception e){
+                 return R.error("发放奖励失败,请联系客服");
+             }
 
-                    // 更新观看记录的奖励类型
-                    log.setRewardType(config.getRewardType());
-                    courseWatchLogMapper.updateFsCourseWatchLog(log);
-
-                    return sendRedPacket;
-                } else {
-                    return R.error("奖励发送失败,请联系客服");
-                }
             }
         } else {
             FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();

+ 5 - 0
fs-service/src/main/java/com/fs/course/vo/FsCourseFinishTempListVO.java

@@ -42,6 +42,7 @@ public class FsCourseFinishTempListVO implements Serializable
     private Long courseId;
 
     private String courseName;
+    private String createBy;
 
     private String videoName;
 
@@ -64,4 +65,8 @@ public class FsCourseFinishTempListVO implements Serializable
     private Date updateTime;
 
     private Integer isAllCompanyUser;
+
+    private String createByName;
+
+    private String createByDeptName;
 }

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

@@ -108,4 +108,7 @@ public class FsUserInformationCollection extends BaseEntity{
     //药师签名
     private String doctorType2Sign;
 
+    //药品订单id
+    private Long storeOrderId;
+
 }

+ 8 - 6
fs-service/src/main/java/com/fs/his/mapper/FsStorePaymentMapper.java

@@ -199,12 +199,14 @@ public interface FsStorePaymentMapper
             " LEFT JOIN fs_store s ON s.store_id=sp.store_id " +
             " left join fs_store_order fso on fso.order_code = sp.business_code " +
             " left join fs_inquiry_order fio on fio.order_sn = sp.business_code " +
-            "LEFT JOIN company c ON \n" +
-            "    ( c.company_id = fso.company_id) OR\n" +
-            "    ( c.company_id = fio.company_id)\n" +
-            "LEFT JOIN company_user cu ON \n" +
-            "    ( cu.user_id = fso.company_user_id) OR\n" +
-            "    (cu.user_id = fio.company_user_id) " +
+            " left join company c on c.company_id = sp.company_id " +
+            " left join company_user cu on cu.user_id = sp.company_user_id " +
+//            "LEFT JOIN company c ON \n" +
+//            "    ( c.company_id = fso.company_id) OR\n" +
+//            "    ( c.company_id = fio.company_id)\n" +
+//            "LEFT JOIN company_user cu ON \n" +
+//            "    ( cu.user_id = fso.company_user_id) OR\n" +
+//            "    (cu.user_id = fio.company_user_id) " +
             "LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp.app_id" +
             " where 1=1 " +
             "            <if test=\"maps.payCode != null  and maps.payCode != ''\"> and sp.pay_code = #{maps.payCode}</if>\n" +

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

@@ -24,6 +24,7 @@ import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.vo.FsStoreOrderStatisticsVO;
 import com.fs.company.vo.FsStoreProductStatisticsVO;
+import com.fs.config.ai.AiHostProper;
 import com.fs.config.cloud.CloudHostProper;
 import com.fs.core.config.WxPayProperties;
 import com.fs.core.utils.OrderCodeUtils;
@@ -265,6 +266,9 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
     @Autowired
     private ApplicationEventPublisher publisher;
 
+    @Autowired
+    AiHostProper aiHostProper;
+
     @Autowired
     private IFsUserWatchService fsUserWatchService;
     @Autowired
@@ -1051,7 +1055,7 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
         if (fsStoreOrderMapper.insertFsStoreOrder(order) > 0) {
             if(CloudHostUtils.hasCloudHostName("金牛明医")){
                 //信息采集 发送药师im
-                doctorSendIm(packageOrder);
+                doctorSendIm(packageOrder,order.getOrderId());
             }
             if (packageOrder.getCycle() >= followRate) {
                 FsFollow fsFollow = new FsFollow();
@@ -1137,7 +1141,7 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
         return R.ok();
     }
 
-    private void doctorSendIm(FsPackageOrder packageOrder) {
+    private void doctorSendIm(FsPackageOrder packageOrder,Long storeOrderId) {
         try {
             //信息采集 发送药师im
             FsUserInformationCollection fsUserInformationCollectionParam = new FsUserInformationCollection();
@@ -1148,6 +1152,11 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
             if (!fsUserInformationCollections.isEmpty()) {
                 for (FsUserInformationCollection collection : fsUserInformationCollections) {
                     openIMService.sendUserInformation(collection.getUserId(),collection.getDoctorType2Id(),collection.getId());
+                    //保存id 到信息采集表
+                    FsUserInformationCollection saveParam = new FsUserInformationCollection();
+                    saveParam.setId(collection.getId());
+                    saveParam.setStoreOrderId(storeOrderId);
+                    fsUserInformationCollectionMapper.updateFsUserInformationCollection(saveParam);
                 }
             }
         } catch (Exception e) {
@@ -2194,8 +2203,13 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
                                 }
                                 //app派件通知
                                 if (dto.getState().equals("2") && (dto.getStateEx().equals("211"))) {
-                                    //ai向客户发送发货物流信息
-                                    requestExpressInfo(order.getOrderId());
+                                    try {
+                                        //ai向客户发送发货物流信息
+                                        requestExpressInfo(order.getOrderId(),aiHostProper.getIpadUrl());
+                                    }catch (Exception e){
+                                        log.error("app派件通知推送失败:{}", e.getMessage());
+                                    }
+
                                 }
                                 //app派件通知
                                 if (dto.getState().equals("2") && (dto.getStateEx().equals("202"))) {
@@ -2267,8 +2281,8 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
         return fsStoreOrderMapper.selectFsStoreOrderListByDeliverySn(logisticCode);
     }
 
-    public static R requestExpressInfo(Long orderId){
-        String fileUrl = "http://ipad.cdwjyyh.com/msg/sendExpressInfo/" + orderId;
+    public static R requestExpressInfo(Long orderId,String ipadUrl){
+        String fileUrl = ipadUrl + "/msg/sendExpressInfo/" + orderId;
 
         try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
             HttpGet httpGet = new HttpGet(fileUrl);

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

@@ -97,7 +97,7 @@ public interface FsStoreAfterSalesScrmMapper
             " left join fs_user u on s.user_id=u.user_id " +
             " left join company c on c.company_id=s.company_id " +
             " left join company_user cu on cu.user_id=s.company_user_id " +
-            " left join fs_store_payment_scrm fsps on fsps.business_order_id = s.id and fsps.status in (-1,1) " +
+            " left join fs_store_payment_scrm fsps on fsps.business_order_id = o.id and fsps.status in (-1,1) " +
             " where 1=1 " +
             "<if test = 'maps.status != null    '> " +
             "and s.status = #{maps.status} " +

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

@@ -1283,9 +1283,9 @@ public interface FsStoreOrderScrmMapper
             "        </if>" +
             "<if test=\"maps.status == 6\">" +
             "            AND o.`status` = 1" +
-            "            AND (" +
-            "            o.store_id IN (SELECT store_id FROM fs_store WHERE delivery_type=2 OR delivery_type=1)" +
-            "            )" +
+//            "            AND (" +
+//            "            o.store_id IN (SELECT store_id FROM fs_store WHERE delivery_type=2 OR delivery_type=1)" +
+//            "            )" +
             "            AND (o.extend_order_id IS NULL OR o.extend_order_id = '')" +
             "        </if>" +
             "<if test = 'maps.deliveryStatus != null    '> " +

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

@@ -168,5 +168,5 @@ public interface LiveDataMapper {
      * @param liveId 直播间ID
      * @return 用户详情列表
      */
-    List<LiveUserDetailVo> selectLiveUserDetailListBySql(@Param("liveId") Long liveId);
+    List<LiveUserDetailVo> selectLiveUserDetailListBySql(@Param("liveId") Long liveId,@Param("companyId") Long companyId,@Param("companyUserId") Long companyUserId);
 }

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

@@ -370,7 +370,8 @@ public interface LiveOrderMapper {
     int batchUpdateErpByOrderIds(@Param("maps")ArrayList<Map<String, String>> maps);
 
     @Select({"<script> " +
-            "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  " +
+            "select a.id as afterSalesId,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  " +
+            " left join ( SELECT t.*,ROW_NUMBER() OVER (PARTITION BY t.order_id ORDER BY t.create_time DESC) AS rn FROM live_after_sales t ) a ON o.order_id = a.order_id AND a.rn = 1 " +
             "where o.is_del=0 " +
             "<if test = 'maps.status != null and maps.status != \"\"     '> " +
             "and o.status =#{maps.status} " +

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

@@ -145,7 +145,7 @@ public interface ILiveDataService {
      * @param liveId 直播间ID
      * @return 用户详情列表
      */
-    R getLiveUserDetailListBySql(Long liveId);
+    R getLiveUserDetailListBySql(Long liveId, Long companyId, Long companyUserId);
 
     /**
      * 查询直播间详情数据(查询数据服务器处理方式)
@@ -166,5 +166,5 @@ public interface ILiveDataService {
      * @param liveId 直播间ID
      * @return 导出VO列表
      */
-    List<LiveUserDetailExportVO> exportLiveUserDetail(Long liveId);
+    List<LiveUserDetailExportVO> exportLiveUserDetail(Long liveId, Long companyId, Long companyUserId);
 }

+ 8 - 12
fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java

@@ -317,6 +317,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
                     LiveOrder orderMap=new LiveOrder();
                     orderMap.setOrderId(order.getOrderId());
                     orderMap.setOrderCode(orderSn);
+                    orderMap.setStatus(order.getStatus());
                     liveOrderService.updateLiveOrder(orderMap);
                     liveOrderItemMapper.updateFsStoreOrderCode(order.getOrderId(),orderSn);
                     try {
@@ -426,6 +427,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
         storeAfterSales.setSalesStatus(0);
         storeAfterSales.setCreateTime(Timestamp.valueOf(LocalDateTime.now()));
         storeAfterSales.setIsDel(0);
+        storeAfterSales.setOrderStatus(orderStatus);
         storeAfterSales.setUserId(Long.valueOf(userId));
         storeAfterSales.setOrderStatus(orderStatus);
         storeAfterSales.setCompanyId(order.getCompanyId());
@@ -459,16 +461,9 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
         request.setRefund_state(1);
         request.setStoreAfterSalesId(storeAfterSales.getId());
         if (StringUtils.isNotBlank(order.getExtendOrderId())){
-            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();
-                    }
-                }
+            BaseResponse response=erpOrderService.refundUpdateLive(request);
+            if(response.getSuccess()){
+                return R.ok();
             }
             else{
                 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
@@ -913,7 +908,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
         logs.setStoreAfterSalesId(storeAfterSales.getId());
         logs.setChangeMessage(OrderInfoEnum.REFUND_STATUS_1.getDesc());
         liveAfterSalesLogsMapper.insertLiveAfterSalesLogs(logs);
-        if (storeAfterSales.getOrderStatus().equals(2)) {
+        if (storeAfterSales.getOrderStatus().equals(1)) {
             if (StringUtils.isNotEmpty(order.getExtendOrderId())) {
                 //更新订单code
                 String orderSn = IdUtil.getSnowflake(0, 0).nextIdStr();
@@ -923,6 +918,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
                 LiveOrder orderMap = new LiveOrder();
                 orderMap.setOrderId(order.getOrderId());
                 orderMap.setOrderCode(orderSn);
+                orderMap.setStatus(order.getStatus());
                 liveOrderService.updateLiveOrder(orderMap);
                 //生成新的订单
                 List<LiveOrderPayment> payments = liveOrderPaymentMapper.selectLiveOrderPaymentByPay(5, order.getOrderId());
@@ -934,7 +930,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
                 }
 
                 try {
-                    if (liveOrderPaymentMapper.selectByBuissnessId(Long.valueOf(order.getOrderCode())) != null) {
+                    if (liveOrderPaymentMapper.selectByBuissnessId(order.getOrderId()) != null) {
                         liveOrderService.createOmsOrder(order.getOrderId());
                     }
                 } catch (Exception e) {

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

@@ -695,8 +695,8 @@ public class LiveDataServiceImpl implements ILiveDataService {
     }
 
     @Override
-    public R getLiveUserDetailListBySql(Long liveId) {
-        List<LiveUserDetailVo> userDetailList = liveDataMapper.selectLiveUserDetailListBySql(liveId);
+    public R getLiveUserDetailListBySql(Long liveId, Long companyId, Long companyUserId ) {
+        List<LiveUserDetailVo> userDetailList = liveDataMapper.selectLiveUserDetailListBySql(liveId, companyId, companyUserId);
         return R.ok().put("data", userDetailList);
     }
 
@@ -1081,9 +1081,9 @@ public class LiveDataServiceImpl implements ILiveDataService {
      * @return 导出VO列表
      */
     @Override
-    public List<LiveUserDetailExportVO> exportLiveUserDetail(Long liveId) {
+    public List<LiveUserDetailExportVO> exportLiveUserDetail(Long liveId, Long companyId, Long companyUserId) {
         // 查询用户详情列表
-        List<LiveUserDetailVo> userDetailList = liveDataMapper.selectLiveUserDetailListBySql(liveId);
+        List<LiveUserDetailVo> userDetailList = liveDataMapper.selectLiveUserDetailListBySql(liveId, companyId, companyUserId);
         if (userDetailList == null || userDetailList.isEmpty()) {
             return new ArrayList<>();
         }

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

@@ -2631,6 +2631,12 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
     {
         liveOrder.setUpdateTime(DateUtils.getNowDate());
         liveUserLotteryRecordMapper.updateOrderStatusByOrderId(liveOrder.getOrderId(), liveOrder.getStatus());
+        //推送修改的商城订单地址到聚水潭ERP
+        try {
+            pushOrderAddressToErp(liveOrder);
+        }catch (Exception e){
+            log.error("修改商城订单地址推送到聚水潭ERP失败,orderId: {}", liveOrder.getOrderId(), e);
+        }
         return baseMapper.updateLiveOrder(liveOrder);
     }
 
@@ -3566,9 +3572,9 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                     dto.setBarCode(fsStoreProductAttrValue.getBarCode());
                     dto.setGroupBarCode(fsStoreProductAttrValue.getGroupBarCode());
                 }
-                if (fsStoreProductAttrValue != null) {
-                    dto.setBarCode(fsStoreProductAttrValue.getBarCode());
-                    dto.setGroupBarCode(fsStoreProductAttrValue.getGroupBarCode());
+                if (attrValue != null) {
+                    dto.setBarCode(attrValue.getBarCode());
+                    dto.setGroupBarCode(attrValue.getGroupBarCode());
                 }
                 dto.setPrice(fsStoreProduct.getPrice());
                 dto.setProductName(fsStoreProduct.getProductName());

+ 10 - 2
fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java

@@ -290,10 +290,18 @@ public class LiveServiceImpl implements ILiveService
                 .eq(FsUserWx::getFsUserId, userId)
                 .eq(FsUserWx::getAppId, StringUtils.isEmpty(param.getAppId()) ? "wx44beed5640bcb1ba" : param.getAppId()); // 卓美小程序
         FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
-        String maOpenId = fsUserWx.getOpenId();
-        if (StringUtils.isEmpty(maOpenId)) {
+        String maOpenId = "";
+        if (fsUserWx == null) {
             maOpenId = param.getMaOpenId();
+        }else {
+            maOpenId = fsUserWx.getOpenId();
+        }
+
+        if (StringUtils.isEmpty(maOpenId)) {
+            log.error("用户没有绑定微信,无法发送预约提醒:{}", userId);
+            return R.ok();
         }
+
         notifyTask.setTouser(maOpenId);
         notifyTask.setPage(String.valueOf(1));
 

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

@@ -201,13 +201,13 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
 
     @Override
     public LiveWatchUser join(FsUserScrm fsUser,long liveId, long userId, String location) {
-        Date now = DateUtils.getNowDate();
 
         // 查询直播间信息
         Live live = liveMapper.selectLiveByLiveId(liveId);
         if (live == null) {
             throw new RuntimeException("直播间不存在");
         }
+        Date now = DateUtils.getNowDate();
 
         // 获取直播/回放状态(带缓存)
         Map<String, Integer> flagMap = getLiveFlagWithCache(liveId);
@@ -429,7 +429,6 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
         liveWatchUser.setLiveId(liveId);
         List<LiveWatchUserVO> liveWatchUserVOS = selectOnlineUserList(liveWatchUser);
 
-        log.info("开始同步直播在线人数到缓存,共{}条数据", liveWatchUserVOS.size());
         if (CollUtil.isNotEmpty(liveWatchUserVOS)){
             ThreadUtil.execute(()->{
                 String hashKey  = String.format(LiveKeysConstant.LIVE_WATCH_USERS, liveId);

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

@@ -26,6 +26,7 @@ public class FsMyLiveOrderListQueryVO implements Serializable
     private Long id;
     private Long orderId;
     private Long liveId;
+    private Long afterSalesId;
 
     /** 订单号 */
     private String orderCode;

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

@@ -98,3 +98,4 @@ public class LiveDataDetailVo {
 
 
 
+

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

@@ -40,3 +40,4 @@ public class LiveUserDetailVo {
 
 
 
+

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

@@ -28,3 +28,4 @@ public class ProductSalesVo {
 
 
 
+

+ 11 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java

@@ -488,4 +488,15 @@ public interface QwUserMapper extends BaseMapper<QwUser>
     List<QwUser> selectQwUserByIds(@Param("qwUserIdList") List<Long> qwUserIdList);
 
     List<QwUser> selectQwUserByTest();
+
+    @Select("<script>" +
+            "select id from qw_user where 1=1 " +
+            "            <if test=\"userIds != null and userIds.size() > 0\">\n" +
+            "                AND company_user_id IN\n" +
+            "                <foreach collection=\"userIds\" open=\"(\" close=\")\" separator=\",\" item=\"item\">\n" +
+            "                    ${item}\n" +
+            "                </foreach>\n" +
+            "            </if>" +
+            "</script>")
+    List<Long> selectQwUserListByCompanyUserIdS(@Param("userIds") List<String> userIds);
 }

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

@@ -204,4 +204,5 @@ public interface IQwUserService
     List<Long> selectDeptByParentId(Long deptId,String cropId);
 
     List<QwUser> selectQwUserByIds(List<Long> qwUserIdList);
+    List<Long> selectQwUserListByCompanyUserIdS(List<String> userIds);
 }

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

@@ -1563,6 +1563,12 @@ public class QwUserServiceImpl implements IQwUserService
     public List<QwUser> selectQwUserByIds(List<Long> qwUserIdList) {
         return qwUserMapper.selectQwUserByIds(qwUserIdList);
     }
+
+    @Override
+    public List<Long> selectQwUserListByCompanyUserIdS(List<String> userIds) {
+        return qwUserMapper.selectQwUserListByCompanyUserIdS(userIds);
+    }
+
     /**
      * 根据销售公司和企微ID查询企微用户
      */

+ 28 - 1
fs-service/src/main/java/com/fs/sop/domain/QwSop.java

@@ -8,7 +8,9 @@ import com.fs.common.annotation.Excel;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 企微sop对象 qw_sop
@@ -135,7 +137,7 @@ public class QwSop implements Serializable
     private String userType;
 
     @TableField(exist = false)
-    private List<Long> qwUserIdList;
+    private List<Long> qwUserIdList = new ArrayList<>();  // 初始化
 
     @Excel(name = "开启评论或者弹幕,1-开启评论;2-开启弹幕;3-都关闭")
     @TableField(exist = false)
@@ -154,4 +156,29 @@ public class QwSop implements Serializable
     private Integer autoUserReg;
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private String pullTime;
+
+
+    @TableField(exist = false)
+    private List<String> userIds = new ArrayList<>();
+
+    public List<String> getUserIds() {
+        if (userIds == null || userIds.isEmpty()) {
+            return userIds;
+        }
+
+        // 直接在原始列表上修改
+        for (int i = 0; i < userIds.size(); i++) {
+            String id = userIds.get(i);
+            if (id != null) {
+                if (id.startsWith("dept_")) {
+                    userIds.set(i, id.substring(5));
+                } else if (id.startsWith("company_")) {
+                    userIds.set(i, id.substring(8));
+                } else if (id.startsWith("user_")) {
+                    userIds.set(i, id.substring(5));
+                }
+            }
+        }
+        return userIds;
+    }
 }

+ 25 - 0
fs-service/src/main/java/com/fs/sop/domain/QwSopTemp.java

@@ -111,6 +111,31 @@ public class QwSopTemp implements Serializable
     @TableField(exist = false)
     private List<Long> userIds;
 
+    @TableField(exist = false)
+    private List<String> companyUserIds=new ArrayList<>();
+
+
+    public List<String> getCompanyUserIds() {
+        if (companyUserIds == null || companyUserIds.isEmpty()) {
+            return companyUserIds;
+        }
+
+        // 直接在原始列表上修改
+        for (int i = 0; i < companyUserIds.size(); i++) {
+            String id = companyUserIds.get(i);
+            if (id != null) {
+                if (id.startsWith("dept_")) {
+                    companyUserIds.set(i, id.substring(5));
+                } else if (id.startsWith("company_")) {
+                    companyUserIds.set(i, id.substring(8));
+                } else if (id.startsWith("user_")) {
+                    companyUserIds.set(i, id.substring(5));
+                }
+            }
+        }
+        return companyUserIds;
+    }
+
     /**
      * 部门类型 00 管理员 01 员工
      */

+ 43 - 0
fs-service/src/main/java/com/fs/store/dto/ClientCredGrantReqDTO.java

@@ -0,0 +1,43 @@
+package com.fs.store.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 客户端凭证授权请求DTO
+ * <p>
+ * 用于构建客户端凭证授权模式下的请求参数。
+ * </p>
+ *
+ * @author xdd
+ * @version 1.0
+ * @since 2025-02-27
+ */
+@Data
+public class ClientCredGrantReqDTO implements Serializable {
+
+    /**
+     * 授权类型
+     * <p>
+     * 固定值 "client_credential",表示客户端凭证授权模式。
+     * </p>
+     */
+    private String grant_type;
+
+    /**
+     * 应用ID
+     * <p>
+     * 应用程序的唯一标识符。
+     * </p>
+     */
+    private String appid;
+
+    /**
+     * 应用密钥
+     * <p>
+     * 应用程序的密钥,用于验证请求的合法性。  <b>注意:应妥善保管,避免泄露。</b>
+     * </p>
+     */
+    private String secret;
+}

+ 52 - 0
fs-service/src/main/java/com/fs/store/dto/MiniGramSubsMsgResultDTO.java

@@ -0,0 +1,52 @@
+package com.fs.store.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 消息发送结果DTO
+ * <p>
+ * 用于封装消息发送接口的响应结果。
+ * </p>
+ *
+ * @author xdd
+ * @version 1.0
+ * @since 2025-02-27
+ */
+@Data
+public class MiniGramSubsMsgResultDTO implements Serializable {
+
+    /**
+     * 错误码
+     * <p>
+     * 返回码,0表示成功,其他值表示失败。
+     * </p>
+     */
+    private Integer errcode;
+
+    /**
+     * 错误信息
+     * <p>
+     * 返回码的文本描述,成功时为 "ok",失败时包含具体的错误信息。
+     * </p>
+     */
+    private String errmsg;
+
+    /**
+     * 消息ID
+     * <p>
+     * 消息的唯一标识符,成功发送时返回。
+     * </p>
+     *  <p>
+     *     注意:这个字段可能为null,发送失败时,此字段可能为null
+     *  </p>
+     */
+    private Long msgid;
+
+    /**
+     * rid  请求的唯一标识
+     * 仅在发生错误时出现
+     */
+    private String rid;
+}

+ 68 - 0
fs-service/src/main/java/com/fs/store/dto/TemplateMessageSendRequestDTO.java

@@ -0,0 +1,68 @@
+package com.fs.store.dto;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * 模板消息发送请求DTO
+ * <p>
+ * 用于构建发送模板消息的请求体。
+ * </p>
+ *
+ * @author xdd
+ * @version 1.0
+ * @since 2025-02-27
+ */
+@Data
+public class TemplateMessageSendRequestDTO {
+
+    /**
+     * 接收者openid
+     * <p>
+     * 用户的唯一标识符。
+     * </p>
+     */
+    private String touser;
+
+    /**
+     * 模板ID
+     * <p>
+     * 所需下发的模板消息的id。
+     * </p>
+     */
+    private String template_id;
+
+    /**
+     * 跳转页面
+     * <p>
+     * 点击模板消息后跳转的页面,可以为空。
+     * </p>
+     */
+    private String page;
+
+    /**
+     * 模板数据
+     * <p>
+     * 模板内容,键值对形式,键名为模板中的变量名,值为要替换的内容。
+     * </p>
+     */
+    private Map<String, TemplateDataValue> data;
+
+    /**
+     * 模板数据值对象
+     * <p>
+     * 内部类,用于表示模板数据中的单个值。
+     * </p>
+     */
+    @Data
+    public static class TemplateDataValue {
+        /**
+         * 模板变量值
+         * <p>
+         * 要替换模板变量的具体内容。
+         * </p>
+         */
+        private String value;
+    }
+}

+ 36 - 0
fs-service/src/main/java/com/fs/store/dto/WeXinAccessTokenDTO.java

@@ -0,0 +1,36 @@
+package com.fs.store.dto;
+
+import lombok.Data;
+
+/**
+ * 访问令牌DTO
+ * <p>
+ * 用于存储从认证服务器获取的访问令牌及其相关信息。
+ * </p>
+ *
+ * @author xdd
+ * @version 1.0
+ * @since 2025-02-27
+ */
+@Data
+public class WeXinAccessTokenDTO {
+
+    /**
+     * 访问令牌
+     * <p>
+     * 用于访问受保护资源的令牌。
+     * </p>
+     */
+    private String accessToken;
+
+    /**
+     * 过期时间(秒)
+     * <p>
+     * 访问令牌的有效时间,单位为秒。
+     * </p>
+     */
+    private Integer expiresIn;
+
+    private Long errcode;
+    private String errmsg;
+}

+ 44 - 0
fs-service/src/main/java/com/fs/store/enums/MiniAppNotifyTaskStatusEnum.java

@@ -0,0 +1,44 @@
+package com.fs.store.enums;
+
+
+import lombok.Getter;
+
+@Getter
+public enum MiniAppNotifyTaskStatusEnum {
+    /**
+     * 待执行
+     */
+    WAITING(0),
+    /**
+     * 执行中
+     */
+    RUNNING(1),
+    /**
+     * 执行成功
+     */
+    SUCCESS(2),
+    /**
+     * 执行失败
+     */
+    FAILED(3),
+    /**
+     * 已取消
+     */
+    CANCELED(4);
+
+    private final int value;
+
+    MiniAppNotifyTaskStatusEnum(int value) {
+        this.value = value;
+    }
+
+
+    public static MiniAppNotifyTaskStatusEnum fromValue(int value) {
+        for (MiniAppNotifyTaskStatusEnum status : values()) {
+            if (status.getValue() == value) {
+                return status;
+            }
+        }
+        throw new IllegalArgumentException("Invalid value: " + value);
+    }
+}

+ 28 - 0
fs-service/src/main/java/com/fs/store/service/IWechatMiniProgrService.java

@@ -0,0 +1,28 @@
+package com.fs.store.service;
+
+
+import com.fs.store.dto.ClientCredGrantReqDTO;
+import com.fs.store.dto.MiniGramSubsMsgResultDTO;
+import com.fs.store.dto.TemplateMessageSendRequestDTO;
+import com.fs.store.dto.WeXinAccessTokenDTO;
+
+/**
+ * 小程序调用相关
+ */
+public interface IWechatMiniProgrService {
+    /**
+     * 获取稳定的token
+     *
+     * @param param 请求参数
+     * @return {@link com.fs.store.dto.WeXinAccessTokenDTO}
+     */
+    WeXinAccessTokenDTO getStableToken(ClientCredGrantReqDTO param);
+
+    /**
+     * 微信小程序发送订阅消息
+     *
+     * @param param 请求参数
+     * @return {@link MiniGramSubsMsgResultDTO}
+     */
+    MiniGramSubsMsgResultDTO sendSubscribeMsg(String accessToken, TemplateMessageSendRequestDTO param);
+}

+ 29 - 0
fs-service/src/main/java/com/fs/store/service/impl/IWechatMiniProgrServiceImpl.java

@@ -0,0 +1,29 @@
+package com.fs.store.service.impl;
+
+import com.fs.store.dto.ClientCredGrantReqDTO;
+import com.fs.store.dto.MiniGramSubsMsgResultDTO;
+import com.fs.store.dto.TemplateMessageSendRequestDTO;
+import com.fs.store.dto.WeXinAccessTokenDTO;
+import com.fs.store.service.IWechatMiniProgrService;
+import com.fs.store.utils.MiniProgramHttp;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@RequiredArgsConstructor
+@Service
+@Slf4j
+public class IWechatMiniProgrServiceImpl implements IWechatMiniProgrService {
+
+    private final MiniProgramHttp miniProgramHttp;
+
+    @Override
+    public WeXinAccessTokenDTO getStableToken(ClientCredGrantReqDTO param) {
+        return miniProgramHttp.getStableAccessToken(param);
+    }
+
+    @Override
+    public MiniGramSubsMsgResultDTO sendSubscribeMsg(String accessToken,TemplateMessageSendRequestDTO param) {
+        return miniProgramHttp.sendSubscribeMessage(accessToken,param);
+    }
+}

+ 95 - 0
fs-service/src/main/java/com/fs/store/utils/MiniProgramHttp.java

@@ -0,0 +1,95 @@
+package com.fs.store.utils;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.fs.store.dto.ClientCredGrantReqDTO;
+import com.fs.store.dto.MiniGramSubsMsgResultDTO;
+import com.fs.store.dto.TemplateMessageSendRequestDTO;
+import com.fs.store.dto.WeXinAccessTokenDTO;
+import com.hc.openapi.tool.fastjson.JSON;
+import com.hc.openapi.tool.util.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+public class MiniProgramHttp {
+
+    /**
+     * 微信小程序-发送订阅消息地址
+     */
+    private static final String BASE_URL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send";
+
+    /**
+     * 微信小程序-获取accessToken地址
+     */
+    private static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/stable_token";
+
+
+    /**
+     * 发送微信订阅消息 (使用 Hutool)
+     * @param accessToken token
+     * @param param 请求数据
+     * @return String
+     */
+    public MiniGramSubsMsgResultDTO sendSubscribeMessage(String accessToken, TemplateMessageSendRequestDTO param) {
+        String url = BASE_URL + "?access_token=" + accessToken;
+
+        log.info("发送小程序订阅消息, 请求 URL: {}", url);
+
+        String requestBody = JSON.toJSONString(param);
+        log.info("发送小程序订阅消息, 请求参数: {}", requestBody);
+
+        try {
+            String response = HttpUtil.post(url, requestBody);
+            log.info("发送小程序订阅消息, HTTP 请求 URL: {}", url);
+            log.info("发送小程序订阅消息, HTTP 请求体: {}", requestBody);
+            log.info("发送小程序订阅消息, HTTP 响应: {}", response);
+
+            MiniGramSubsMsgResultDTO result = JSONObject.parseObject(response, MiniGramSubsMsgResultDTO.class);
+            if(ObjectUtil.notEqual(result.getErrcode(),0)){
+                throw new RuntimeException("发送小程序订阅消息失败, " + result.getErrmsg());
+            }
+            log.info("发送小程序订阅消息, 解析结果: {}", JSON.toJSONString(result));
+            return result;
+
+        } catch (Exception e) {
+            log.error("发送小程序订阅消息失败: {}", e.getMessage());
+            throw e;
+        }
+    }
+
+
+    /**
+     * 获取微信 Stable Access Token
+     * @return WeXinAccessTokenDTO
+     */
+    public WeXinAccessTokenDTO getStableAccessToken(ClientCredGrantReqDTO param) {
+        String requestBody = JSONObject.toJSONString(param);
+        log.info("获取微信 Stable Access Token, 请求参数: {}", requestBody); // 打印请求参数
+
+        try {
+            String responseJson =
+            HttpRequest.post(TOKEN_URL).contentType("application/json").body(requestBody).execute().body();
+            log.info("获取微信 Stable Access Token, HTTP 请求 URL: {}", TOKEN_URL);
+            log.info("获取微信 Stable Access Token, HTTP 请求体: {}", requestBody);
+            log.info("获取微信 Stable Access Token, HTTP 响应: {}", responseJson);
+
+            if(StringUtils.isBlank(responseJson)){
+                throw new RuntimeException("获取微信 Stable Access Token 失败,response为空");
+            }
+            WeXinAccessTokenDTO result = JSONObject.parseObject(responseJson, WeXinAccessTokenDTO.class);
+            log.info("获取微信 Stable Access Token, 解析结果: {}", JSONObject.toJSONString(result)); //记录解析结果
+            if(result == null || StringUtils.isBlank(result.getAccessToken())){
+                throw new RuntimeException("获取微信 Stable Access Token 失败, accessToken为空");
+            }
+            return result;
+
+        } catch (Exception e) {
+            log.error("获取微信 Stable Access Token 失败", e);
+            throw e;
+        }
+    }
+}

+ 43 - 0
fs-service/src/main/java/com/fs/store/vo/ClientCredGrantReqDTO.java

@@ -0,0 +1,43 @@
+package com.fs.store.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 客户端凭证授权请求DTO
+ * <p>
+ * 用于构建客户端凭证授权模式下的请求参数。
+ * </p>
+ *
+ * @author xdd
+ * @version 1.0
+ * @since 2025-02-27
+ */
+@Data
+public class ClientCredGrantReqDTO implements Serializable {
+
+    /**
+     * 授权类型
+     * <p>
+     * 固定值 "client_credential",表示客户端凭证授权模式。
+     * </p>
+     */
+    private String grant_type;
+
+    /**
+     * 应用ID
+     * <p>
+     * 应用程序的唯一标识符。
+     * </p>
+     */
+    private String appid;
+
+    /**
+     * 应用密钥
+     * <p>
+     * 应用程序的密钥,用于验证请求的合法性。  <b>注意:应妥善保管,避免泄露。</b>
+     * </p>
+     */
+    private String secret;
+}

+ 36 - 0
fs-service/src/main/java/com/fs/store/vo/WeXinAccessTokenDTO.java

@@ -0,0 +1,36 @@
+package com.fs.store.vo;
+
+import lombok.Data;
+
+/**
+ * 访问令牌DTO
+ * <p>
+ * 用于存储从认证服务器获取的访问令牌及其相关信息。
+ * </p>
+ *
+ * @author xdd
+ * @version 1.0
+ * @since 2025-02-27
+ */
+@Data
+public class WeXinAccessTokenDTO {
+
+    /**
+     * 访问令牌
+     * <p>
+     * 用于访问受保护资源的令牌。
+     * </p>
+     */
+    private String accessToken;
+
+    /**
+     * 过期时间(秒)
+     * <p>
+     * 访问令牌的有效时间,单位为秒。
+     * </p>
+     */
+    private Integer expiresIn;
+
+    private Long errcode;
+    private String errmsg;
+}

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

@@ -96,6 +96,6 @@ ipad:
 wx_miniapp_temp:
   pay_order_temp_id: V
   inquiry_temp_id: 9
-enableRedPackAccount: 1
+enableRedPackAccount: 0
 
 

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

@@ -172,6 +172,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                     #{item}
                 </foreach>
             </if>
+           <if test="maps.userIds != null and !maps.userIds.isEmpty()">
+            AND l.company_user_id IN
+               <foreach collection='maps.userIds' item='item' open='(' separator=',' close=')'>
+                  #{item}
+               </foreach>
+            </if>
         </where>
          order by l.finish_time desc,l.update_time desc,l.create_time desc
     </select>

+ 20 - 2
fs-service/src/main/resources/mapper/his/FsStoreOrderMapper.xml

@@ -532,6 +532,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             ) sp_latest ON sp_latest.business_code = so.order_code AND sp_latest.rn = 1
             LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
         </if>
+        <if test="maps.status != null and (maps.status == 7 or maps.status == 6)">
+            LEFT JOIN fs_user_information_collection fuic ON fuic.store_order_id = so.order_id
+        </if>
 
         where so.is_del=0
         <if test="maps.packageSecondName != null and maps.packageSecondName != ''">
@@ -567,7 +570,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <if test="maps.isFirst != null">
             and so.is_first = #{maps.isFirst}
         </if>
-        <if test="maps.status != null and maps.status != 6">
+        <if test="maps.status != null and maps.status != 6 and maps.status != 7">
             and so.status = #{maps.status}
         </if>
         <if test="maps.status == 6">
@@ -576,6 +579,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             so.store_id in (select store_id from fs_store where delivery_type=2 or delivery_type=1)
             )
             and  (so.extend_order_id is null or  so.extend_order_id like '')
+            and (fuic.doctor_type2_confirm = 1 or fuic.doctor_type2_confirm = null)
+        </if>
+        <if test="maps.status == 7">
+            and so.`status`= 2
+            and fuic.doctor_type2_confirm = 0
+
         </if>
         <if test="maps.source != null">
             and so.source = #{maps.source}
@@ -741,6 +750,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         WHERE sp.business_code IS NOT NULL
         ) sp_latest ON sp_latest.business_code = so.order_code AND sp_latest.rn = 1
         LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
+        <if test="maps.status != null and (maps.status == 7 or maps.status == 6)">
+            LEFT JOIN fs_user_information_collection fuic ON fuic.store_order_id = so.order_id
+        </if>
         <where>
             <if test="maps.packageSecondName != null and maps.packageSecondName != ''">
                 and so.package_second_name like concat('%', #{maps.packageSecondName}, '%')
@@ -775,7 +787,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="maps.isFirst != null">
                 and so.is_first = #{maps.isFirst}
             </if>
-            <if test="maps.status != null and maps.status != 6">
+            <if test="maps.status != null and maps.status != 6 and maps.status != 7">
                 and so.status = #{maps.status}
             </if>
             <if test="maps.status == 6">
@@ -784,6 +796,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 so.store_id in (select store_id from fs_store where delivery_type=2 or delivery_type=1)
                 )
                 and  (so.extend_order_id is null or  so.extend_order_id like '')
+                and (fuic.doctor_type2_confirm = 1 or fuic.doctor_type2_confirm = null)
+            </if>
+            <if test="maps.status == 7">
+                and so.`status`= 2
+                and fuic.doctor_type2_confirm = 0
+
             </if>
             <if test="maps.source != null">
                 and so.source = #{maps.source}

+ 6 - 1
fs-service/src/main/resources/mapper/his/FsUserInformationCollectionMapper.xml

@@ -36,6 +36,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="doctorType2Id"    column="doctor_type2_id"    />
         <result property="doctorType2Confirm"    column="doctor_type2_confirm"    />
         <result property="doctorType2Sign"    column="doctor_type2_sign"    />
+        <result property="storeOrderId"    column="store_order_id"    />
     </resultMap>
 
     <sql id="selectFsUserInformationCollectionVo">
@@ -43,7 +44,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              , doctor_confirm, create_time, update_time,doctor_id,company_user_id
              ,package_id,pay_type,amount,is_package,user_confirm2,package_order_code,package_order_id
              ,status,user_advice,doctor_advice,doctor_sign,doctor_confirm_time,sex,user_name,user_phone_four
-             ,allergy,remark,patient_id,doctor_type2_id,doctor_type2_confirm,doctor_type2_sign  from fs_user_information_collection
+             ,allergy,remark,patient_id,doctor_type2_id,doctor_type2_confirm,doctor_type2_sign,store_order_id  from fs_user_information_collection
     </sql>
 
     <select id="selectFsUserInformationCollectionList" parameterType="FsUserInformationCollection" resultMap="FsUserInformationCollectionResult">
@@ -55,6 +56,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <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="storeOrderId != null "> and store_order_id = #{storeOrderId}</if>
             <if test="packageOrderCode != null and packageOrderCode !=''"> and package_order_code = #{packageOrderCode}</if>
         </where>
         order by id desc
@@ -152,6 +154,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="doctorType2Id != null">doctor_type2_id,</if>
             <if test="doctorType2Confirm != null">doctor_type2_confirm,</if>
             <if test="doctorType2Sign != null">doctor_type2_sign,</if>
+            <if test="storeOrderId != null">store_order_id,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="questionId != null">#{questionId},</if>
@@ -184,6 +187,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="doctorType2Id != null">#{doctorType2Id},</if>
             <if test="doctorType2Confirm != null">#{doctorType2Confirm},</if>
             <if test="doctorType2Sign != null">#{doctorType2Sign},</if>
+            <if test="storeOrderId != null">#{storeOrderId},</if>
          </trim>
     </insert>
 
@@ -220,6 +224,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="doctorType2Id != null">doctor_type2_id = #{doctorType2Id},</if>
             <if test="doctorType2Confirm != null">doctor_type2_confirm = #{doctorType2Confirm},</if>
             <if test="doctorType2Sign != null">doctor_type2_sign = #{doctorType2Sign},</if>
+            <if test="storeOrderId != null">store_order_id = #{storeOrderId},</if>
         </trim>
         where id = #{id}
     </update>

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

@@ -1761,6 +1761,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <if test="maps.companyUserNickName != null and  maps.companyUserNickName !=  ''">
             left join company_user cu on cu.user_id=o.company_user_id
         </if>
+        <if test="maps.erpAccount != null and maps.erpAccount != ''">
+            LEFT JOIN fs_store_order_df df on df.order_id=o.id
+
+        </if>
         <if test = "maps.productName != null and  maps.productName !=  '' ">
             left join fs_store_order_item_scrm oi on o.id = oi.order_id
             left join fs_store_product_scrm fsp on fsp.product_id = oi.product_id
@@ -1780,6 +1784,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="maps.appId != null and map.appId != ''">
                 and csc.appid = #{maps.appId}
             </if>
+            <if test="maps.orderCodes != null  and maps.orderCodes.size > 0">
+                and o.order_code in
+                <foreach collection="maps.orderCodes" item="orderCode" open="(" close=")" separator=",">
+                    #{orderCode}
+                </foreach>
+            </if>
             <if test="maps.orderCode != null and  maps.orderCode !=''">
                 and o.order_code like CONCAT('%',#{maps.orderCode},'%')
             </if>
@@ -1881,6 +1891,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="maps.appId != null and maps.appId != ''">
                 and csc.appid = #{maps.appId}
             </if>
+            <if test="maps.erpPhoneNumber != null and maps.erpPhoneNumber != ''">
+                and o.erp_phone like concat(#{maps.erpPhoneNumber},'%')
+            </if>
+            <if test="maps.erpAccount != null and maps.erpAccount != '未分拣' and maps.erpAccount != ''">
+                and df.login_account like #{maps.erpAccount}
+            </if>
+            <if test="maps.erpAccount == '未分拣'">
+                and ( df.login_account is null or df.login_account like '')
+            </if>
         </where>
         ${maps.params.dataScope}
         <if test="maps.productName != null and  maps.productName !=  ''   ">

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

@@ -522,7 +522,7 @@
     <update id="updateFsUser" parameterType="FsUserScrm">
         update fs_user
         <trim prefix="SET" suffixOverrides=",">
-            <if test="nickName != null">nick_name = #{nickName},</if>
+            <if test="nickname != null">nick_name = #{nickname},</if>
             <if test="nickname != null">nickname = #{nickname},</if>
             <if test="avatar != null">avatar = #{avatar},</if>
             <if test="phone != null">phone = #{phone},</if>

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

@@ -540,6 +540,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         LEFT JOIN company c ON lufe.company_id = c.company_id
         LEFT JOIN company_user cu ON lufe.company_user_id = cu.user_id
         WHERE lwu.live_id = #{liveId}
+        <if test="companyId != null">
+            and lufe.company_id = #{companyId}
+        </if>
+        <if test="companyUserId != null">
+            and lufe.company_user_id = #{companyUserId}
+        </if>
         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>

+ 7 - 1
fs-service/src/main/resources/mapper/sop/QwSopMapper.xml

@@ -236,7 +236,13 @@
             <if test="minSend != null "> and min_send = #{minSend}</if>
             <if test="maxSend != null "> and max_send = #{maxSend}</if>
             <if test="stopTime != null "> and stop_time = #{stopTime}</if>
-            <!-- 加入固定条件 -->
+            <if test="qwUserIdList != null and !qwUserIdList.isEmpty() ">
+              and (
+                <foreach collection='qwUserIdList' item='item' index='index' separator=' or '>
+                   find_in_set( #{item} , REGEXP_REPLACE(qw_user_ids,  '[\"\\\\[\\\\]]', '' ) )
+                </foreach>
+                )
+            </if>
             and status != 6
         </where>
         order by create_time desc

+ 6 - 0
fs-service/src/main/resources/mapper/sop/QwSopTempMapper.xml

@@ -56,6 +56,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                     #{item}
                 </foreach>
             </if>
+            <if test="companyUserIds != null and !companyUserIds.isEmpty()">
+                AND create_by IN
+                <foreach collection='companyUserIds' item='item' open='(' separator=',' close=')'>
+                    #{item}
+                </foreach>
+            </if>
         </where>
         order by sort,create_time desc
     </select>