Procházet zdrojové kódy

Merge remote-tracking branch 'origin/openIm' into openIm

zx před 2 týdny
rodič
revize
958ca4d0a7
63 změnil soubory, kde provedl 2274 přidání a 215 odebrání
  1. 4 0
      fs-admin/pom.xml
  2. 2 2
      fs-admin/src/main/java/com/fs/his/controller/FsInquiryOrderController.java
  3. 7 24
      fs-admin/src/main/java/com/fs/his/controller/FsUserController.java
  4. 41 0
      fs-admin/src/main/java/com/fs/his/task/Task.java
  5. 46 4
      fs-admin/src/main/java/com/fs/web/controller/common/CommonController.java
  6. 103 0
      fs-admin/src/test/java/com/fs/course/controller/OpenIMServiceTest.java
  7. 1 0
      fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
  8. 26 0
      fs-company/src/main/java/com/fs/user/FsUserAdminController.java
  9. 19 6
      fs-doctor-app/src/main/java/com/fs/app/controller/CommonController.java
  10. 75 6
      fs-doctor-app/src/main/java/com/fs/app/controller/DoctorController.java
  11. 17 5
      fs-doctor-app/src/main/java/com/fs/app/controller/DrugReportController.java
  12. 34 10
      fs-doctor-app/src/main/java/com/fs/app/controller/InquiryOrderController.java
  13. 67 5
      fs-doctor-app/src/main/java/com/fs/app/controller/PrescribeController.java
  14. 11 0
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java
  15. 1 1
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  16. 1 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCoursePeriodDays.java
  17. 28 0
      fs-service/src/main/java/com/fs/course/dto/BatchSendCourseAllDTO.java
  18. 56 0
      fs-service/src/main/java/com/fs/course/dto/BatchSendCourseDTO.java
  19. 1 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  20. 2 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCompanyUserMapper.java
  21. 1 1
      fs-service/src/main/java/com/fs/course/param/FsCourseSendRewardUParam.java
  22. 11 4
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  23. 1 1
      fs-service/src/main/java/com/fs/erp/http/JstErpHttpServiceImpl.java
  24. 2 1
      fs-service/src/main/java/com/fs/erp/service/impl/FsJstAftersalePushServiceImpl.java
  25. 13 0
      fs-service/src/main/java/com/fs/gtPush/service/impl/uniPush2ServiceImpl.java
  26. 3 0
      fs-service/src/main/java/com/fs/gtPush/service/uniPush2Service.java
  27. 1 0
      fs-service/src/main/java/com/fs/his/domain/FsInquiryOrderMsg.java
  28. 1 0
      fs-service/src/main/java/com/fs/his/dto/PayloadDTO.java
  29. 1 1
      fs-service/src/main/java/com/fs/his/mapper/FsDoctorMapper.java
  30. 1 1
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  31. 3 1
      fs-service/src/main/java/com/fs/his/service/IFsFollowService.java
  32. 6 0
      fs-service/src/main/java/com/fs/his/service/IFsInquiryOrderMsgService.java
  33. 6 5
      fs-service/src/main/java/com/fs/his/service/IFsInquiryOrderService.java
  34. 45 8
      fs-service/src/main/java/com/fs/his/service/impl/FsDoctorServiceImpl.java
  35. 53 10
      fs-service/src/main/java/com/fs/his/service/impl/FsFollowServiceImpl.java
  36. 274 4
      fs-service/src/main/java/com/fs/his/service/impl/FsInquiryOrderMsgServiceImpl.java
  37. 96 21
      fs-service/src/main/java/com/fs/his/service/impl/FsInquiryOrderServiceImpl.java
  38. 16 6
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  39. 31 0
      fs-service/src/main/java/com/fs/im/config/IMConfig.java
  40. 67 0
      fs-service/src/main/java/com/fs/im/domain/ImSendLog.java
  41. 52 0
      fs-service/src/main/java/com/fs/im/dto/OpenImBatchMsgDTO.java
  42. 31 0
      fs-service/src/main/java/com/fs/im/dto/OpenImBatchResponseDataDTO.java
  43. 12 0
      fs-service/src/main/java/com/fs/im/dto/OpenImMsgCallBackResponse.java
  44. 11 0
      fs-service/src/main/java/com/fs/im/dto/OpenImResponseDataDTO.java
  45. 70 0
      fs-service/src/main/java/com/fs/im/mapper/ImSendLogMapper.java
  46. 61 0
      fs-service/src/main/java/com/fs/im/service/IImSendLogService.java
  47. 31 1
      fs-service/src/main/java/com/fs/im/service/OpenIMService.java
  48. 92 0
      fs-service/src/main/java/com/fs/im/service/impl/ImSendLogServiceImpl.java
  49. 465 48
      fs-service/src/main/java/com/fs/im/service/impl/OpenIMServiceImpl.java
  50. 6 5
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  51. 17 0
      fs-service/src/main/java/com/fs/qw/service/impl/AsyncSopTestService.java
  52. 2 1
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java
  53. 4 0
      fs-service/src/main/java/com/fs/store/param/h5/FsUserPageListParam.java
  54. 2 0
      fs-service/src/main/resources/application-druid-fby.yml
  55. 4 2
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  56. 22 0
      fs-service/src/main/resources/mapper/course/FsUserCompanyUserMapper.xml
  57. 6 1
      fs-service/src/main/resources/mapper/his/FsInquiryOrderMsgMapper.xml
  58. 1 1
      fs-service/src/main/resources/mapper/his/FsUserMapper.xml
  59. 138 0
      fs-service/src/main/resources/mapper/im/ImSendLogMapper.xml
  60. 2 2
      fs-user-app/src/main/java/com/fs/app/controller/CommonController.java
  61. 36 25
      fs-user-app/src/main/java/com/fs/app/controller/FollowController.java
  62. 10 0
      fs-user-app/src/main/java/com/fs/app/controller/WxUserController.java
  63. 25 2
      fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

+ 4 - 0
fs-admin/pom.xml

@@ -94,6 +94,10 @@
             <artifactId>clickhouse-jdbc</artifactId>
             <version>0.4.6</version>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+        </dependency>
 
     </dependencies>
 

+ 2 - 2
fs-admin/src/main/java/com/fs/his/controller/FsInquiryOrderController.java

@@ -1,6 +1,7 @@
 package com.fs.his.controller;
 
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.controller.BaseController;
@@ -155,8 +156,7 @@ public class FsInquiryOrderController extends BaseController
 
     @PreAuthorize("@ss.hasPermi('his:inquiryOrder:sendMsg')")
     @GetMapping(value = "/sendMsg/{orderId}")
-    public AjaxResult sendMsg(@PathVariable("orderId") Long orderId)
-    {
+    public AjaxResult sendMsg(@PathVariable("orderId") Long orderId) throws JsonProcessingException {
 
         return AjaxResult.success(fsInquiryOrderService.sendStartMsg(orderId));
     }

+ 7 - 24
fs-admin/src/main/java/com/fs/his/controller/FsUserController.java

@@ -4,7 +4,7 @@ import java.util.*;
 import java.util.stream.Collectors;
 
 import com.alibaba.fastjson.JSON;
-import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.domain.entity.SysRole;
@@ -13,9 +13,10 @@ import com.fs.common.exception.CustomException;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.StringUtils;
-import com.fs.course.domain.FsUserWatchStatistics;
-import com.fs.course.mapper.FsUserWatchStatisticsMapper;
+import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.course.param.FsCourseLinkCreateParam;
 import com.fs.course.service.IFsUserCompanyUserService;
+import com.fs.course.service.IFsUserCourseService;
 import com.fs.his.domain.FsUserAddress;
 import com.fs.his.enums.FsUserIntegralLogTypeEnum;
 import com.fs.his.mapper.FsUserMapper;
@@ -27,6 +28,8 @@ import com.fs.his.utils.PhoneUtil;
 import com.fs.his.vo.FsUserExportListVO;
 import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.UserVo;
+import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.service.OpenIMService;
 import com.fs.qw.dto.UserProjectDTO;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.vo.h5.FsUserPageListVO;
@@ -39,6 +42,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.session.ExecutorType;
 import org.apache.ibatis.session.SqlSession;
 import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.beans.BeanUtils;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -340,26 +344,5 @@ public class FsUserController extends BaseController
         return userIntegralLogsService.addIntegralTemplate(integralTemplateParam);
     }
 
-//    @PutMapping("/encryptPhoneTemp")
-//    @ApiOperation("临时接口")
-//    public void encryptPhoneTemp(){
-//        FsUser fsUser = new FsUser();
-//        List<FsUser> list = fsUserService.selectFsUserList(fsUser);
-//        List<FsUser> fsUserList = list.stream().peek(v -> v.setPhone(encryptPhone(v.getPhone()))).collect(Collectors.toList());
-//
-//        // 分批次处理,一次提交500条
-//        List<List<FsUser>> batches = Lists.partition(fsUserList, 500);
-//        batches.forEach(batch -> {
-//            SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
-//            try {
-//                FsUserMapper mapper = sqlSession.getMapper(FsUserMapper.class);
-//                batch.forEach(mapper::updateFsUser);
-//                sqlSession.commit();
-//            } finally {
-//                sqlSession.close();
-//            }
-//        });
-//
-//    }
 
 }

+ 41 - 0
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -15,6 +15,7 @@ import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.vo.QwIpadTotalVo;
 import com.fs.company.vo.RedPacketMoneyVO;
+import com.fs.course.dto.BatchSendCourseAllDTO;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.course.service.ITencentCloudCosService;
@@ -45,6 +46,7 @@ import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.FsSubOrderResultVO;
 import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
 import com.fs.qw.domain.QwCompany;
 import com.fs.qw.service.*;
 import com.fs.qwApi.service.QwApiService;
@@ -56,12 +58,14 @@ import org.apache.commons.lang3.StringUtils;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 @Slf4j
 @Component("task")
@@ -157,6 +161,10 @@ public class Task {
     private IQwCompanyService qwCompanyService;
     @Autowired
     private IQwUserService qwUserService;
+    @Autowired
+    private OpenIMService openIMService;
+    @Autowired
+    public RedisTemplate redisTemplate;
 
     @Autowired
     private ICompanyUserService userService;
@@ -1089,4 +1097,37 @@ public class Task {
         }
         return null;
     }
+
+
+    /**
+     * 定时任务-im会员定时发课,每一分钟执行一次
+     */
+    public void sendOpenImCourse(){
+        String redisKey = "openIm:batchSendMsg";
+        Map<String, BatchSendCourseAllDTO> cacheMap = redisCache.getCacheMap(redisKey);
+        if(cacheMap == null || cacheMap.isEmpty()){
+            logger.info("=====================会员IM定时发课,不存在对应的redisKey==================");
+            return;
+        }
+        List<Map.Entry<String, BatchSendCourseAllDTO>> toSendMap = cacheMap.entrySet().parallelStream().filter((v) -> {
+            String[] split = v.getKey().split(":");
+            long timestamp = Long.parseLong(split[3]);
+            return timestamp < System.currentTimeMillis();
+        }).collect(Collectors.toList());
+
+        if(toSendMap.isEmpty()){
+            logger.info("=====================会员IM定时发课,不存在可执行的发课任务==================");
+            return;
+        }
+        for (Map.Entry<String, BatchSendCourseAllDTO> entry : toSendMap) {
+            //执行发送消息任务
+            BatchSendCourseAllDTO batchSendCourseAllDTO = entry.getValue();
+            openIMService.batchSendMsgTask(batchSendCourseAllDTO.getBatchSendCourseDTO(), batchSendCourseAllDTO.getOpenImBatchMsgDTO(), batchSendCourseAllDTO.getProject(), 1);
+
+            // 执行结束,删除
+            this.redisTemplate.<String, BatchSendCourseAllDTO>opsForHash().delete(redisKey, entry.getKey());
+
+        }
+
+    }
 }

+ 46 - 4
fs-admin/src/main/java/com/fs/web/controller/common/CommonController.java

@@ -3,16 +3,15 @@ package com.fs.web.controller.common;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-
 import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.file.OssException;
-import com.fs.course.service.IHuaweiObsService;
-import com.fs.course.service.IHuaweiVodService;
+import com.fs.course.dto.BatchSendCourseAllDTO;;
 import com.fs.course.service.ITencentCloudCosService;
-import com.fs.course.service.impl.HuaweiObsServiceImpl;
 import com.fs.framework.config.ServerConfig;
 import com.fs.his.domain.FsExportTask;
 import com.fs.his.service.IFsExportTaskService;
+import com.fs.im.service.OpenIMService;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 
@@ -22,6 +21,7 @@ import com.huaweicloud.sdk.vod.v1.model.BaseInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
@@ -36,6 +36,7 @@ import java.io.File;
 import java.io.IOException;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
 
 import static com.fs.course.service.impl.HuaweiObsServiceImpl.fileUrlMap;
 import static com.fs.course.service.impl.HuaweiObsServiceImpl.uploadProgress;
@@ -60,6 +61,17 @@ public class CommonController
     private ITencentCloudCosService tencentCloudCosService;
     @Autowired
     private IFsExportTaskService exportTaskService;
+
+    @Autowired
+    private OpenIMService openIMService;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    public RedisTemplate redisTemplate;
+
+    org.slf4j.Logger logger= LoggerFactory.getLogger(getClass());
     @GetMapping(value = "common/getTask/{taskId}")
     public R getTask(@PathVariable("taskId") Long taskId)
     {
@@ -227,7 +239,37 @@ public class CommonController
         return tencentCloudCosService.getKeyAndCredentials();
     }
 
+    /**
+     * 测试接口
+     */
+    @PostMapping("/common/im/testTask")
+    public void testIMTask(){
+        String redisKey = "openIm:batchSendMsg";
+        Map<String, BatchSendCourseAllDTO> cacheMap = redisCache.getCacheMap(redisKey);
+        if(cacheMap == null || cacheMap.isEmpty()){
+            logger.info("=====================会员IM定时发课,不存在对应的redisKey==================");
+            return;
+        }
+        List<Map.Entry<String, BatchSendCourseAllDTO>> toSendMap = cacheMap.entrySet().parallelStream().filter((v) -> {
+            String[] split = v.getKey().split(":");
+            long timestamp = Long.parseLong(split[3]);
+            return timestamp < System.currentTimeMillis();
+        }).collect(Collectors.toList());
+
+        if(toSendMap.isEmpty()){
+            logger.info("=====================会员IM定时发课,不存在可执行的发课任务==================");
+            return;
+        }
+        for (Map.Entry<String, BatchSendCourseAllDTO> entry : toSendMap) {
+            //执行发送消息任务
+            BatchSendCourseAllDTO batchSendCourseAllDTO = entry.getValue();
+            openIMService.batchSendMsgTask(batchSendCourseAllDTO.getBatchSendCourseDTO(), batchSendCourseAllDTO.getOpenImBatchMsgDTO(), batchSendCourseAllDTO.getProject(), 1);
+
+            // 执行结束,删除
+            this.redisTemplate.<String, BatchSendCourseAllDTO>opsForHash().delete(redisKey, entry.getKey());
 
+        }
 
+    }
 
 }

+ 103 - 0
fs-admin/src/test/java/com/fs/course/controller/OpenIMServiceTest.java

@@ -0,0 +1,103 @@
+package com.fs.course.controller;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fs.FSApplication;
+import com.fs.common.annotation.DataSource;
+import com.fs.im.dto.OpenImMsgDTO;
+import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.service.OpenIMService;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.test.context.ActiveProfiles;
+
+import static org.junit.Assert.assertNotNull;
+
+@Slf4j
+@ActiveProfiles("druid-fby-test")
+@RunWith(org.springframework.test.context.junit4.SpringRunner.class)
+@SpringBootTest(classes = FSApplication.class)
+public class OpenIMServiceTest {
+
+    @Autowired
+    private OpenIMService openIMService;
+    @Test
+    public void openIMSendMsg() {
+        OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+        openImMsgDTO.setSendID("fbyC8584");
+        openImMsgDTO.setRecvID("fbyU1077739");
+//        openImMsgDTO.setGroupID("group789");
+        openImMsgDTO.setSenderNickname("测试用户");
+        openImMsgDTO.setSenderFaceURL("https://example.com/avatar.jpg");
+        openImMsgDTO.setSenderPlatformID(1);
+        openImMsgDTO.setContentType(101);
+        openImMsgDTO.setSessionType(1);
+        openImMsgDTO.setOnlineOnly(false);
+        openImMsgDTO.setNotOfflinePush(false);
+        openImMsgDTO.setSendTime(System.currentTimeMillis());
+        openImMsgDTO.setEx("额外信息");
+
+        // Content
+        OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+        content.setContent("Hello World");
+        content.setData("test data");
+        content.setDescription("测试消息");
+        content.setExtension("ext");
+        openImMsgDTO.setContent(content);
+
+        // OfflinePushInfo
+        OpenImMsgDTO.OfflinePushInfo offlinePushInfo = new OpenImMsgDTO.OfflinePushInfo();
+        offlinePushInfo.setTitle("新消息");
+        offlinePushInfo.setDesc("您收到一条新消息");
+        offlinePushInfo.setEx("push ex");
+        offlinePushInfo.setIOSPushSound("default");
+        offlinePushInfo.setIOSBadgeCount(true);
+        openImMsgDTO.setOfflinePushInfo(offlinePushInfo);
+
+        // 调用方法
+        OpenImResponseDTO result = openIMService.openIMSendMsg(openImMsgDTO);
+
+        // 断言
+        assertNotNull(result);
+    }
+
+    @Test
+    public void aiAutoReply() {
+    }
+
+    @Test
+    public void sendUtil() {
+    }
+
+    @Test
+    public void sendUtilUserToDoctor() {
+    }
+
+    @Test
+    public void editConversation() {
+    }
+
+    @Test
+    public void sendCourse() throws JsonProcessingException {
+        Long userId = 1077739L;
+        Long companyUserId = 8584L;
+        String url = "https://example.com/course/123";
+        String title = "Java编程基础课程";
+        String linkImageUrl = "https://example.com/images/course-cover.jpg";
+        String cropId = "crop_123456";
+
+        OpenImResponseDTO actualResponse = openIMService.sendCourse(
+                userId, companyUserId, url, title, linkImageUrl, cropId
+        );
+        log.info("返回结果: {}",actualResponse);
+    }
+
+    @Test
+    public void sendPackageUtil() {
+    }
+}

+ 1 - 0
fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java

@@ -90,6 +90,7 @@ public class FsUserController extends AppBaseController {
         log.debug("用户会员分页列表 param: {}", JSON.toJSONString(param));
         param.setUserId(Long.parseLong(getUserId()));
 //        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setIsHidePhoneMiddle(false);
         PageInfo<FsUserPageListVO> fsUserPageListVOPageInfo = fsUserService.selectFsUserPageList(param);
 //        PageInfo<FsUserPageListVO> pageInfo = new PageInfo<>(list);
         return ResponseResult.ok(fsUserPageListVOPageInfo);

+ 26 - 0
fs-company/src/main/java/com/fs/user/FsUserAdminController.java

@@ -1,6 +1,7 @@
 package com.fs.user;
 
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -10,12 +11,17 @@ import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.cache.ICompanyUserCacheService;
+import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.service.IFsUserCourseService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
 
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.utils.PhoneUtil;
+import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.service.OpenIMService;
 import com.fs.qw.domain.CustomerTransferApproval;
 import com.fs.qw.dto.FsUserTransferParamDTO;
 import com.fs.qw.service.ICustomerTransferApprovalService;
@@ -24,6 +30,7 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -51,6 +58,12 @@ public class FsUserAdminController extends BaseController {
     @Autowired
     private ICustomerTransferApprovalService transferApprovalService;
 
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
+
+    @Autowired
+    private OpenIMService openIMService;
+
     @PreAuthorize("@ss.hasPermi('user:fsUser:list')")
     @PostMapping("/list")
     @ApiOperation("会员列表(与移动端使用的相同查询)")
@@ -145,4 +158,17 @@ public class FsUserAdminController extends BaseController {
     }
 
 
+    @ApiOperation("后台会员批量发送课程消息")
+    @PostMapping("/batchSendCourse")
+    public OpenImResponseDTO batchSendCourse(@RequestBody BatchSendCourseDTO batchSendCourseDTO) throws JsonProcessingException {
+        // 生成看课短链
+        FsCourseLinkCreateParam fsCourseLinkCreateParam = new FsCourseLinkCreateParam();
+        BeanUtils.copyProperties(batchSendCourseDTO, fsCourseLinkCreateParam);
+        R courseSortLink = fsUserCourseService.createCourseSortLink(fsCourseLinkCreateParam);
+        String url = courseSortLink.get("url").toString();
+        batchSendCourseDTO.setUrl(url);
+
+        return openIMService.batchSendCourse(batchSendCourseDTO);
+    }
+
 }

+ 19 - 6
fs-doctor-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -1,8 +1,9 @@
 package com.fs.app.controller;
 
 
-
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.app.annotation.Login;
 import com.fs.app.param.SignParam;
 import com.fs.app.utils.CityTreeUtil;
@@ -25,11 +26,10 @@ import com.fs.his.param.ImMsgParam;
 import com.fs.his.service.*;
 
 import com.fs.his.utils.ConfigUtil;
-import com.fs.im.dto.MsgCustomDTO;
-import com.fs.im.dto.MsgDTO;
-import com.fs.im.dto.MsgDataDTO;
-import com.fs.im.dto.MsgDataFormatDTO;
+import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
+import com.fs.im.vo.OpenImMsgCallBackVO;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 import com.fs.system.service.ISysConfigService;
@@ -60,7 +60,6 @@ import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Logger;
 
-
 @Api("公共接口")
 @RestController
 @RequestMapping(value="/app/common")
@@ -102,6 +101,8 @@ public class CommonController {
 	private IFsFollowService followService;
 	@Autowired
 	private IImService imService;
+	@Autowired
+	private OpenIMService openIMService;
 	@ApiOperation("测试")
 	@GetMapping(value = "/getTest")
 	public AjaxResult getTest()
@@ -239,6 +240,18 @@ public class CommonController {
 		vo.setActionStatus("OK");
 		return vo;
 	}
+	@ApiOperation("openIm聊天数据回调")
+	@PostMapping(value = "/callbackAfterSendSingleMsgCommand")
+	public OpenImMsgCallBackResponse openImMsgCallBack(@RequestBody String body, HttpServletRequest request) throws JsonProcessingException {
+
+		Gson gson = new Gson();
+		OpenImMsgCallBackVO messageInfo = gson.fromJson(body, OpenImMsgCallBackVO.class);
+
+		//openIMService.AiAutoReply(messageInfo);
+
+		//log.info("收到的参数{}", JSON.toJSONString(messageInfo));
+		return inquiryOrderMsgService.openImSaveMsg(messageInfo);
+	}
 	/**
 	 * 生成验证码
 	 */

+ 75 - 6
fs-doctor-app/src/main/java/com/fs/app/controller/DoctorController.java

@@ -1,6 +1,7 @@
 package com.fs.app.controller;
 
 
+import cn.hutool.http.HttpRequest;
 import cn.hutool.json.JSONUtil;
 import com.fs.app.annotation.Login;
 import com.fs.app.param.DoctorEditParam;
@@ -20,13 +21,19 @@ import com.fs.his.param.FsDoctorExtractListSParam;
 import com.fs.his.service.*;
 import com.fs.his.vo.FsDoctorBillListSVO;
 import com.fs.his.vo.FsDoctorExtractListSVO;
+import com.fs.im.config.IMConfig;
+import com.fs.im.service.OpenIMService;
 import com.fs.sms.service.SmsService;
 import com.fs.system.service.ISysConfigService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
+import com.github.pagehelper.util.StringUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
@@ -34,13 +41,11 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 
 
+@Slf4j
 @Api("个人中心")
 @RestController
 @RequestMapping(value="/app/doctor")
@@ -59,7 +64,8 @@ public class DoctorController extends  AppBaseController {
     private IFsDoctorExtractService doctorExtractService;
     @Autowired
     private ISysConfigService configService;
-
+    @Autowired
+    private OpenIMService openIMService;
     @Autowired
     private SmsService smsService;
     @ApiOperation("登录")
@@ -100,7 +106,70 @@ public class DoctorController extends  AppBaseController {
             return R.ok(map);
         }
     }
-
+    @ApiOperation("校验医生是否注册新的im")
+    @PostMapping("/accountCheck")
+    public R accountCheck(@RequestBody Map<String, String> userIdMap){
+        //获取管理员token
+        String userId = userIdMap.get("userId");
+        String adminToken = openIMService.getAdminToken();
+        JSONObject requestBody = new JSONObject();
+        // 解析响应
+        if (StringUtil.isNotEmpty(adminToken)) {
+            //查询用户是否注册
+            ArrayList<String> userIds = new ArrayList<>();
+            requestBody = new JSONObject();
+            userIds.add(userId);
+            requestBody.put("checkUserIDs", userIds);
+            String body = HttpRequest.post(IMConfig.URL+"/user/account_check")
+                    .header("operationID", String.valueOf(System.currentTimeMillis()))
+                    .header("token", adminToken)
+                    .body(requestBody.toString())
+                    .execute()
+                    .body();
+            JSONObject jsonObject = new JSONObject(body);
+            JSONArray results = jsonObject.getJSONObject("data").getJSONArray("results");
+            if (results != null && results.length() > 0) {
+                JSONObject resultObj = results.getJSONObject(0);
+                int accountStatus = resultObj.getInt("accountStatus");
+                //未注册自动注册
+                if (accountStatus==0){
+                    String s = userId.replaceFirst("^"+"D", "");
+                    FsDoctor fsDoctor = doctorService.selectFsDoctorByDoctorId(Long.parseLong(s));
+                    if (null==fsDoctor){
+                        return R.error("用户不存在");
+                    }
+                    ArrayList<Object> users = new ArrayList<>();
+                    HashMap<String, String> map = new HashMap<>();
+                    map.put("userID",userId);
+                    map.put("nickname",fsDoctor.getDoctorName());
+                    map.put("faceURL",StringUtils.isEmpty(fsDoctor.getAvatar())?"https://cos.his.cdwjyyh.com/fs/20240926/420728ee06e54575ba82665dedb4756b.png":fsDoctor.getAvatar());
+                    users.add(map);
+                    requestBody = new JSONObject();
+                    //userIds.add(userId);
+                    requestBody.put("users", users);
+                    String body1 = HttpRequest.post(IMConfig.URL+"/user/user_register")
+                            .header("operationID", String.valueOf(System.currentTimeMillis()))
+                            .header("token", adminToken).body(requestBody.toString()).execute().body();
+                    log.info("注册结果:"+body1);
+                }
+            } else {
+                return R.error("返回结果为空");
+            }
+            requestBody = new JSONObject();
+            requestBody.put("platformID",5);
+            requestBody.put("userID",userId);
+            String body1 = HttpRequest.post(IMConfig.URL+"/api/auth/get_user_token")
+                    .header("operationID", String.valueOf(System.currentTimeMillis()))
+                    .header("token", adminToken)
+                    .body(requestBody.toString()).execute().body();
+            JSONObject userJson = new JSONObject(body1);
+            JSONObject userData = userJson.getJSONObject("data");
+            String userToken = userData.getString("token");
+            return R.ok().put("token", userToken);
+        } else {
+            return R.error("获取管理员token失败");
+        }
+    }
     @ApiOperation("登录")
     @PostMapping("/loginByWeb")
     public R loginByWeb(@Validated @RequestBody DoctorLoginParam param) {

+ 17 - 5
fs-doctor-app/src/main/java/com/fs/app/controller/DrugReportController.java

@@ -2,6 +2,8 @@ package com.fs.app.controller;
 
 
 import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.app.param.DrugReportAddParam;
 import com.fs.app.param.DrugReportFinishParam;
 import com.fs.common.annotation.RepeatSubmit;
@@ -19,8 +21,10 @@ import com.fs.his.service.IFsFollowService;
 import com.fs.his.vo.FsDrugReportDVO;
 import com.fs.his.vo.FsDrugReportListDVO;
 import com.fs.his.vo.FsFollowListDVO;
+import com.fs.im.config.IMConfig;
 import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -53,7 +57,8 @@ public class DrugReportController extends AppBaseController {
     private IImService imService;
     @Autowired
     private IFsDrugReportCountService fsDrugReportCountService;
-
+    @Autowired
+    private OpenIMService openIMService;
     @ApiOperation("获取报告列表")
     @GetMapping("/getDrugReportList")
     public R getDrugReportList(FsDrugReportListDParam param)
@@ -74,7 +79,7 @@ public class DrugReportController extends AppBaseController {
 
     @ApiOperation("提交报告")
     @PostMapping("/addReport")
-    public R addReport(@Validated @RequestBody DrugReportAddParam param, HttpServletRequest request){
+    public R addReport(@Validated @RequestBody DrugReportAddParam param, HttpServletRequest request) throws JsonProcessingException {
         FsFollow follow=followService.selectFsFollowByFollowId(param.getFollowId());
 
         FsDrugReport report=new FsDrugReport();
@@ -104,7 +109,10 @@ public class DrugReportController extends AppBaseController {
             msg.setMsgContent(new MsgDataFormatDTO("drugReport",ext,report.getReportId().toString()));
             msgs.add(msg);
             msgDTO.setMsgBody(msgs);
-            imService.sendMsg(msgDTO);
+            //imService.sendMsg(msgDTO);
+            ObjectMapper objectMapper = new ObjectMapper();
+            String ex = objectMapper.writeValueAsString(customDTO);
+            openIMService.sendUtil("D"+follow.getDoctorId(),"U"+follow.getUserId(),110,"drugReport","","","",report.getReportId().toString(),ex);
 
             fsDrugReportCountService.addReportCountByUserIdAndDoctorId(follow.getUserId(),follow.getDoctorId());
 
@@ -118,7 +126,7 @@ public class DrugReportController extends AppBaseController {
     @ApiOperation("完成咨询")
     @PostMapping("/finishDrugReport")
     @RepeatSubmit
-    public R finishDrugReport(@Validated @RequestBody DrugReportFinishParam param, HttpServletRequest request){
+    public R finishDrugReport(@Validated @RequestBody DrugReportFinishParam param, HttpServletRequest request) throws JsonProcessingException {
         FsFollow follow=followService.selectFsFollowByFollowId(param.getFollowId());
         //发送给用户
         MsgDTO msgDTO=new MsgDTO();
@@ -136,7 +144,11 @@ public class DrugReportController extends AppBaseController {
         msg.setMsgContent(new MsgDataFormatDTO("您的用药咨询报告已出具,本次咨询结束"));
         msgs.add(msg);
         msgDTO.setMsgBody(msgs);
-        imService.sendMsg(msgDTO);
+        //imService.sendMsg(msgDTO);
+        ObjectMapper objectMapper = new ObjectMapper();
+        String ex = objectMapper.writeValueAsString(customDTO);
+        openIMService.sendUtil("D"+follow.getDoctorId(),"U"+follow.getUserId(),110,"finishDrugReport","","您的用药咨询报告已出具,本次咨询结束","","",ex);
+
         redisTemplate.delete("DrugReport:doctorId:" + follow.getDoctorId() + ":userId:" + follow.getUserId());
         return R.ok();
 

+ 34 - 10
fs-doctor-app/src/main/java/com/fs/app/controller/InquiryOrderController.java

@@ -5,6 +5,8 @@ import cn.hutool.core.util.IdUtil;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.app.annotation.Login;
 import com.fs.app.param.InquiryOrderMsgListParam;
 import com.fs.common.BeanCopyUtils;
@@ -30,6 +32,7 @@ import com.fs.im.dto.MsgDTO;
 import com.fs.im.dto.MsgDataDTO;
 import com.fs.im.dto.MsgDataFormatDTO;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
 import com.github.binarywang.wxpay.config.WxPayConfig;
@@ -84,7 +87,8 @@ public class InquiryOrderController extends  AppBaseController {
     private IFsInquiryOrderReportService orderReportService;
     @Autowired
     private IImService imService;
-
+    @Autowired
+    private OpenIMService openIMService;
     @Login
     @GetMapping("/getInquiryOrderList")
     public R getInquiryOrderList(FsInquiryOrderListPDParam param)
@@ -177,7 +181,7 @@ public class InquiryOrderController extends  AppBaseController {
     @Login
     @ApiOperation("抢单")
     @PostMapping("/acceptOrder")
-    public R acceptOrder(@Validated @RequestBody FsInquiryOrderAcceptParam param, HttpServletRequest request){
+    public R acceptOrder(@Validated @RequestBody FsInquiryOrderAcceptParam param, HttpServletRequest request) throws JsonProcessingException {
         param.setDoctorId(Long.parseLong(getDoctorId()));
         return inquiryOrderService.acceptOrder(param);
     }
@@ -185,7 +189,7 @@ public class InquiryOrderController extends  AppBaseController {
     @Login
     @ApiOperation("接单")
     @PostMapping("/receiveOrder")
-    public R receiveOrder(@Validated @RequestBody FsInquiryOrderReceiveParam param, HttpServletRequest request){
+    public R receiveOrder(@Validated @RequestBody FsInquiryOrderReceiveParam param, HttpServletRequest request) throws JsonProcessingException {
         param.setDoctorId(Long.parseLong(getDoctorId()));
         return inquiryOrderService.receiveOrder(param);
     }
@@ -201,7 +205,7 @@ public class InquiryOrderController extends  AppBaseController {
     @Login
     @ApiOperation("完成订单")
     @PostMapping("/finishOrder")
-    public R finishOrder(@Validated @RequestBody FsInquiryOrderFinishParam param, HttpServletRequest request){
+    public R finishOrder(@Validated @RequestBody FsInquiryOrderFinishParam param, HttpServletRequest request) throws JsonProcessingException {
         param.setDoctorId(Long.parseLong(getDoctorId()));
         return inquiryOrderService.finishOrder(param);
     }
@@ -254,7 +258,7 @@ public class InquiryOrderController extends  AppBaseController {
     @ApiOperation("提交诊断报告")
     @PostMapping("/submitInquiryOrderReport")
     @Transactional
-    public R submitInquiryOrderReport(@Validated @RequestBody FsInquiryOrderReportSubmitDParam param, HttpServletRequest request){
+    public R submitInquiryOrderReport(@Validated @RequestBody FsInquiryOrderReportSubmitDParam param, HttpServletRequest request) throws JsonProcessingException {
         FsInquiryOrderReport report=null;
         if(param.getReportId()!=null&&param.getReportId()>0){
             report=orderReportService.selectFsInquiryOrderReportByReportId(param.getReportId());
@@ -295,15 +299,15 @@ public class InquiryOrderController extends  AppBaseController {
 
         }
         FsInquiryOrder inquiryOrder=inquiryOrderService.selectFsInquiryOrderByOrderId(report.getOrderId());
-        MsgDTO msgDTO=new MsgDTO();
+        /*MsgDTO msgDTO=new MsgDTO();
         msgDTO.setFrom_Account("D-"+report.getDoctorId().toString());
-        msgDTO.setTo_Account("U-"+report.getUserId().toString());
+        msgDTO.setTo_Account("U-"+report.getUserId().toString());*/
         MsgCustomDTO customDTO=new MsgCustomDTO();
         customDTO.setType("startInquiry");
         customDTO.setImType(1);
         customDTO.setOrderType(inquiryOrder.getOrderType());
         customDTO.setOrderId(report.getOrderId().toString());
-        msgDTO.setCloudCustomData(JSONUtil.toJsonStr(customDTO));
+        /*msgDTO.setCloudCustomData(JSONUtil.toJsonStr(customDTO));
         List<MsgDataDTO> msgs=new ArrayList<>();
         MsgDataDTO msg=new MsgDataDTO();
         String ext= JSONUtil.toJsonStr(report);
@@ -311,8 +315,28 @@ public class InquiryOrderController extends  AppBaseController {
         msg.setMsgContent(new MsgDataFormatDTO("report",ext,orderId));
         msg.setMsgType("TIMCustomElem");//TIMCustomElem
         msgs.add(msg);
-        msgDTO.setMsgBody(msgs);
-        imService.sendMsg(msgDTO);
+        msgDTO.setMsgBody(msgs);*/
+        //imService.sendMsg(msgDTO);
+        ObjectMapper objectMapper = new ObjectMapper();
+        String ex = objectMapper.writeValueAsString(customDTO);
+        openIMService.sendUtil("D"+report.getDoctorId(),"U"+report.getUserId(),110,"report","","","",report.getOrderId().toString(),ex);
+        /*JSONObject jsonObject = new JSONObject();
+        OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+        openImMsgDTO.setSendID("D"+report.getDoctorId().toString());
+        openImMsgDTO.setRecvID("U"+report.getUserId().toString());
+        openImMsgDTO.setContentType(110);
+        openImMsgDTO.setSenderPlatformID(5);
+        openImMsgDTO.setSessionType(1);
+
+        OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+        PayloadDTO payloadDTO = new PayloadDTO();
+        payloadDTO.setData("startInquiry");
+        PayloadDTO.Extension extension = new PayloadDTO.Extension();
+        extension.setTitle();
+        //content.setContent(ext);
+        openImMsgDTO.setContent(content);
+        openImMsgDTO.setEx(customDTO);
+        openIMService.openIMSendMsg(openImMsgDTO);*/
         return R.ok("操作成功");
     }
 

+ 67 - 5
fs-doctor-app/src/main/java/com/fs/app/controller/PrescribeController.java

@@ -2,23 +2,35 @@ package com.fs.app.controller;
 
 
 import cn.hutool.core.util.IdUtil;
+import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.app.annotation.Login;
 import com.fs.common.core.domain.R;
+import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.core.utils.OrderCodeUtils;
 import com.fs.his.domain.*;
 import com.fs.his.dto.FsInquiryOrderPatientDTO;
+import com.fs.his.dto.PayloadDTO;
 import com.fs.his.param.*;
 import com.fs.his.service.*;
 import com.fs.his.vo.FsDoctorPrescribeListDVO;
 import com.fs.his.vo.FsPrescribeListDVO;
+import com.fs.im.config.IMConfig;
+import com.fs.im.dto.MsgCustomDTO;
+import com.fs.im.dto.OpenImMsgDTO;
+import com.fs.im.dto.OpenImResponseDTO;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
+import com.fs.qw.domain.QwExternalContact;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.Synchronized;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
@@ -26,12 +38,10 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 
+@Slf4j
 @Api("处方接口")
 @RestController
 @RequestMapping(value="/app/prescribe")
@@ -55,7 +65,8 @@ public class PrescribeController extends  AppBaseController {
     private IFsStoreOrderService storeOrderService;
     @Autowired
     private IImService imService;
-
+    @Autowired
+    private OpenIMService openIMService;
     @Login
     @GetMapping("/getDoctorPrescribeList")
     public R getDoctorPrescribeList(FsDoctorPrescribeListDParam param)
@@ -275,4 +286,55 @@ public class PrescribeController extends  AppBaseController {
         String url=prescribeService.getPrescribeCodeUrl(prescribeId);
         return R.ok().put("url",url);
     }
+    @PostMapping("/test")
+    public R test(@RequestBody HashMap<String,String> map) throws JsonProcessingException {
+        ObjectMapper objectMapper = new ObjectMapper();
+        OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+        openImMsgDTO.setSendID("D"+79);
+        openImMsgDTO.setRecvID("U"+map.get("userId"));
+        openImMsgDTO.setContentType(110);
+        openImMsgDTO.setSenderPlatformID(5);
+        openImMsgDTO.setSessionType(1);
+        OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+        //content.setContent(ext);
+        PayloadDTO payload = new PayloadDTO();
+        payload.setData("prescribe");
+        PayloadDTO.Extension extension = new PayloadDTO.Extension();
+        extension.setDiagnose("感冒发烧,胸闷");
+        payload.setExtension(extension);
+        //log.info("payload:{}",payload);
+        OpenImMsgDTO.ImData imData = new OpenImMsgDTO.ImData();
+
+        imData.setPayload(payload);
+
+        String imJson = objectMapper.writeValueAsString(imData);
+        content.setData(imJson);
+        openImMsgDTO.setContent(content);
+        //log.info("openImMsgDTO:{}",openImMsgDTO);
+        JSONObject jsonObject = new JSONObject(openImMsgDTO);
+        //log.info("jsonObject:{}",jsonObject);
+        MsgCustomDTO customDTO=new MsgCustomDTO();
+        //customDTO.setType("startInquiry");
+        customDTO.setType(map.get("payloadDAata").toString());
+        customDTO.setOrderType(2);
+        customDTO.setOrderId(map.get("orderId"));
+        String imtype = map.get("imtype").toString();
+
+        customDTO.setImType(Integer.parseInt(imtype));
+        //openImMsgDTO.setEx(payload);
+        String ex = objectMapper.writeValueAsString(customDTO);
+        QwExternalContact qwExternalContact = new QwExternalContact();
+        qwExternalContact.setId(1l);
+        qwExternalContact.setFsUserId(1l);
+        //WatchSleepData last = watchSleepDataMapper.getLast("861389060165680");
+        //watchAudioMsgLogMapper.insert(watchAudioMsgLog);
+        //qwExternalContactMapper.selectRemarkByCompanyUserAndFsUser(map.get("sendId"), map.get("userId"),"wwwwww");
+        //fsUserCouponService.updateFsUserCouponStatusByLimtType2();
+//        openIMService.checkAndImportFriendByDianBo(Long.parseLong(map.get("sendId")),map.get("userId"),qwExternalContact.getCorpId());
+        //OpenImResponseDTO openImResponseDTO = openIMService.sendCourse(Long.parseLong(map.get("userId")), Long.parseLong(map.get("sendId")), "/pages/courseAnswer/index?link=1932017457275338752", "《五仙传医2.0》","https://cos.his.cdwjyyh.com/fs/20241108/a8ed49ae9a264c7483cec5bdcbcf6060.png");
+        log.info("请求地址{}",IMConfig.URL);
+//        log.info("前缀{}",IMConfig.PREFIX);
+        //OpenImResponseDTO openImResponseDTO = openIMService.sendUtil("D" +map.get("sendId"), "U"+map.get("userId").toString(), 110, map.get("payloadDAata").toString(), "", map.get("title").toString(), "", "3135749",ex);
+        return R.ok().put("data",null);
+    }
 }

+ 11 - 0
fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java

@@ -207,6 +207,17 @@ public class SendMsg {
                     log.error("线程等待错误!");
                 }
             }
+            // 推送 APP
+            if (!setting.getSetting().isEmpty()) {
+                new Thread(() -> {
+                    try {
+                        List<QwSopTempSetting.Content.Setting> settings = JSON.parseArray(JSON.toJSONString(setting.getSetting()), QwSopTempSetting.Content.Setting.class).stream().filter(e -> "9".equals(e.getContentType())).collect(Collectors.toList());
+                        asyncSopTestService.asyncSendMsgBySopAppLinkNormalIM(settings, qwSopLogs.getCorpId(), user.getCompanyUserId(), qwSopLogs.getFsUserId());
+                    } catch (Exception e) {
+                        log.error("推送APP失败", e);
+                    }
+                }).start();
+            }
             qwSopLogs.setSend(true);
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             QwSopLogs updateQwSop = new QwSopLogs();

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

@@ -36,7 +36,7 @@ import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.service.ISysConfigService;
 import com.google.gson.Gson;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;

+ 1 - 0
fs-service/src/main/java/com/fs/course/domain/FsUserCoursePeriodDays.java

@@ -96,6 +96,7 @@ public class FsUserCoursePeriodDays extends BaseEntityTow {
     private LocalDate maxDate;
 
     /** 项目id */
+    @TableField(exist = false)
     private Long projectId;
 
 }

+ 28 - 0
fs-service/src/main/java/com/fs/course/dto/BatchSendCourseAllDTO.java

@@ -0,0 +1,28 @@
+package com.fs.course.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.im.dto.OpenImBatchMsgDTO;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 批量发课-定时任务入参(存入redis)
+ */
+@Data
+@Accessors(chain = true)
+public class BatchSendCourseAllDTO {
+
+    @ApiModelProperty(value = "前端点击发送时的入参")
+    private BatchSendCourseDTO batchSendCourseDTO;
+
+    @ApiModelProperty(value = "发IM消息时的参数")
+    private OpenImBatchMsgDTO openImBatchMsgDTO;
+
+    @ApiModelProperty(value = "课程所属项目")
+    private Long project;
+
+}

+ 56 - 0
fs-service/src/main/java/com/fs/course/dto/BatchSendCourseDTO.java

@@ -0,0 +1,56 @@
+package com.fs.course.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 批量发课-入参
+ */
+@Data
+public class BatchSendCourseDTO {
+
+    @ApiModelProperty(value = "用户ids")
+    private List<Long> userIds;
+
+    @ApiModelProperty(value = "销售id")
+    private Long companyUserId;
+
+    @ApiModelProperty(value = "公司id")
+    private Long companyId;
+
+    @ApiModelProperty(value = "营期id")
+    private Long periodId;
+
+    @ApiModelProperty(value = "课程id")
+    private Long courseId;
+
+    @ApiModelProperty(value = "视频id")
+    private Long videoId;
+
+    @ApiModelProperty(value = "标签ids")
+    private List<Long> tagIds;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(value = "发送消息时间,定时发送需要传")
+    private Date sendTime;
+
+    @ApiModelProperty(value = "发送标题")
+    private String title;
+
+    @ApiModelProperty(value = "链接有效时长(分钟)")
+    private Integer effectiveDuration;
+
+    @ApiModelProperty(value = "营期课程id")
+    private Long id;
+
+    @ApiModelProperty(value = "看课短链,不用传")
+    private String url;
+
+    @ApiModelProperty(value = "项目id")
+    private Long projectId;
+
+}

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

@@ -6,6 +6,7 @@ import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.dto.WatchLogDTO;
 import com.fs.course.param.*;
 import com.fs.course.vo.*;
+import com.fs.im.dto.OpenImBatchResponseDataDTO;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.sop.vo.QwRatingVO;
 import org.apache.ibatis.annotations.Param;

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

@@ -93,4 +93,6 @@ public interface FsUserCompanyUserMapper extends BaseMapper<FsUserCompanyUser>{
     int batchUpdateStatus(@Param("ids") List<Long> ids, @Param("status") int status);
 
     List<Long> selectFsUserCompanyUserListByMap(@Param("param") Map<String, Object> param);
+
+    List<FsUserCompanyUser> selectFsUserCompanyUserByIds(@Param("param") Map<String, Object> param);
 }

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

@@ -22,7 +22,7 @@ public class FsCourseSendRewardUParam implements Serializable
     private String corpId;
     private Integer linkType;
     private Long qwExternalId;
-    private Integer source=1;//来源 1:h5  2:小程序
+    private Integer source=1;//来源 1:h5  2:小程序 3:app
     private Integer isRoom;
     private Integer sendType;
     private Long periodId;

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

@@ -966,6 +966,12 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
 
+        // 判断来源是否是app,如是app,则发放积分奖励
+        int sourceApp = 3;
+        if(sourceApp == param.getSource()){
+            return sendIntegralReward(param, user, log, config);
+        }
+
         // 根据奖励类型发放不同奖励
         switch (config.getRewardType()) {
             // 红包奖励
@@ -1585,11 +1591,12 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             return ResponseResult.fail(504, "请观看最新的课程项目");
         }
         // 项目看课数限制
-        Integer logCount = fsUserCourseMapper.selectTodayCourseWatchLogCountByUserIdAndProjectId(param.getUserId(), param.getProjectId());
-        if (Objects.isNull(watchCourseVideo) && logCount > 0) {
-            return ResponseResult.fail(504, "超过项目看课数量限制");
+        if(!"福本源".equals(signProjectName)) {
+            Integer logCount = fsUserCourseMapper.selectTodayCourseWatchLogCountByUserIdAndProjectId(param.getUserId(), param.getProjectId());
+            if (Objects.isNull(watchCourseVideo) && logCount > 0) {
+                return ResponseResult.fail(504, "超过项目看课数量限制");
+            }
         }
-
         //添加判断:该用户是否已经存在此课程的看课记录,并且看课记录的销售id不是传入的销售id
         if(watchCourseVideo != null){
             if(!watchCourseVideo.getCompanyUserId().equals(param.getCompanyUserId())) {

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

@@ -14,7 +14,7 @@ import com.fs.erp.service.impl.JstTokenService;
 import com.fs.erp.utils.SignUtil;
 import com.fs.ybPay.dto.RefundOrderDTO;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;

+ 2 - 1
fs-service/src/main/java/com/fs/erp/service/impl/FsJstAftersalePushServiceImpl.java

@@ -19,7 +19,8 @@ import com.fs.his.mapper.FsStoreOrderMapper;
 import com.fs.his.service.IFsStoreOrderItemService;
 import com.fs.ybPay.dto.RefundOrderDTO;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang.StringUtils;
+
+import org.apache.commons.lang3.StringUtils;
 import org.apache.http.util.Asserts;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;

+ 13 - 0
fs-service/src/main/java/com/fs/gtPush/service/impl/uniPush2ServiceImpl.java

@@ -2,14 +2,19 @@ package com.fs.gtPush.service.impl;
 
 import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.core.domain.R;
 import com.fs.gtPush.domain.PushReqBean;
 import com.fs.gtPush.domain.PushResult;
 import com.fs.gtPush.service.uniPush2Service;
+import com.fs.im.service.OpenIMService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 @Service
 public class uniPush2ServiceImpl implements uniPush2Service {
+    @Autowired
+    private OpenIMService openIMService;
     private static final String url = "https://fc-mp-de6e03a9-c1a3-439b-9eec-d0dc3c565e4e.next.bspapp.com/push";
     @Override
     public PushResult pushMessage(PushReqBean push) {
@@ -17,4 +22,12 @@ public class uniPush2ServiceImpl implements uniPush2Service {
         PushResult pushResult = JSONUtil.toBean(result, PushResult.class);
         return pushResult;
     }
+    @Override
+    public void pushSopAppLinkMsgByExternalIM(String cropId, String linkTile, String linkDescribe,String linkImageUrl, String link, Long companyUserId,Long fsUserId) throws JsonProcessingException {
+
+        if (companyUserId!=null&&fsUserId!=null && fsUserId!=0){
+            openIMService.sendCourse(fsUserId,companyUserId,link,linkDescribe,linkImageUrl,cropId);
+        }
+
+    }
 }

+ 3 - 0
fs-service/src/main/java/com/fs/gtPush/service/uniPush2Service.java

@@ -1,9 +1,12 @@
 package com.fs.gtPush.service;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.core.domain.R;
 import com.fs.gtPush.domain.PushReqBean;
 import com.fs.gtPush.domain.PushResult;
 
 public interface uniPush2Service {
     PushResult pushMessage(PushReqBean push);
+    void pushSopAppLinkMsgByExternalIM(String cropId,String linkTile,String linkDescribe,String linkImageUrl,String link,Long companyUserId,Long fsUserId) throws JsonProcessingException;
+
 }

+ 1 - 0
fs-service/src/main/java/com/fs/his/domain/FsInquiryOrderMsg.java

@@ -42,4 +42,5 @@ public class FsInquiryOrderMsg extends BaseEntity
 
     private String msgKey;
     private Integer msgContentType;
+    private String companyUserAccount;
 }

+ 1 - 0
fs-service/src/main/java/com/fs/his/dto/PayloadDTO.java

@@ -27,6 +27,7 @@ public class PayloadDTO {
         private String courseUrl;
         private String appRealLink;
         private String writeStatus;
+        private String packageId;
 
     }
 

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

@@ -201,5 +201,5 @@ public interface FsDoctorMapper
     @Select("SELECT doctor_id id,doctor_name name  FROM fs_doctor WHERE FIND_IN_SET(doctor_id, (select doctor_ids from company where company_id=#{companyId})); ")
     List<UserVo> selectUserDocVoListByCompany(Long companyId);
 
-    String selectDoctorNameByIds(List<Long> doctorIds);
+    String selectDoctorNameByIds(@Param("doctorIds") String doctorIds);
 }

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

@@ -79,7 +79,7 @@ public interface FsUserMapper
      */
     public int deleteFsUserByUserIds(Long[] userIds);
     @Select({"<script> " +
-                "select f1.*,f2.nick_name tui_name,f2.phone tui_phone,cu.nick_name AS companyUserNickName,co.company_name FROM fs_user f1 LEFT JOIN fs_user f2 ON f1.tui_user_id =f2.user_id "+
+                "select f1.*, ifnull(cuc.remark, f1.remark) as remark, f2.nick_name tui_name,f2.phone tui_phone,cu.nick_name AS companyUserNickName,co.company_name FROM fs_user f1 LEFT JOIN fs_user f2 ON f1.tui_user_id =f2.user_id "+
             " LEFT JOIN fs_user_company_user cuc ON cuc.user_id = f1.user_id\n" +
             " LEFT JOIN company_user cu ON cu.user_id = cuc.company_user_id\n" +
             " LEFT JOIN company co ON co.company_id = cuc.company_id "+

+ 3 - 1
fs-service/src/main/java/com/fs/his/service/IFsFollowService.java

@@ -1,6 +1,8 @@
 package com.fs.his.service;
 
 import java.util.List;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.his.domain.FsFollow;
 import com.fs.his.domain.FsPackageOrder;
 import com.fs.his.param.FsFollowListDParam;
@@ -68,7 +70,7 @@ public interface IFsFollowService
 
     FsFollowVO selectFsFollowVOByFollowId(Long followId);
 
-    void addFsFollowByPackageOrder(FsPackageOrder order);
+    void addFsFollowByPackageOrder(FsPackageOrder order) throws JsonProcessingException;
 
     List<FsFollowListUVO> selectFsFollowListUVO(FsFollowListUParam param);
 

+ 6 - 0
fs-service/src/main/java/com/fs/his/service/IFsInquiryOrderMsgService.java

@@ -1,10 +1,14 @@
 package com.fs.his.service;
 
 import java.util.List;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.his.domain.FsInquiryOrderMsg;
 import com.fs.his.param.FsInquiryOrderMsgListDParam;
 import com.fs.his.param.ImMsgParam;
 import com.fs.his.vo.FsInquiryOrderMsgListDVO;
+import com.fs.im.dto.OpenImMsgCallBackResponse;
+import com.fs.im.vo.OpenImMsgCallBackVO;
 
 /**
  * 图文订单Service接口
@@ -65,4 +69,6 @@ public interface IFsInquiryOrderMsgService
     void saveMsg(ImMsgParam messageInfo);
 
     List<FsInquiryOrderMsgListDVO> selectFsInquiryOrderMsgListDVO(FsInquiryOrderMsgListDParam param);
+
+    OpenImMsgCallBackResponse openImSaveMsg(OpenImMsgCallBackVO messageInfo) throws JsonProcessingException;
 }

+ 6 - 5
fs-service/src/main/java/com/fs/his/service/IFsInquiryOrderService.java

@@ -2,6 +2,7 @@ package com.fs.his.service;
 
 import java.util.List;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.core.domain.R;
 import com.fs.his.domain.*;
 import com.fs.his.param.*;
@@ -81,15 +82,15 @@ public interface IFsInquiryOrderService
 
     List<FsInquiryOrderListDVO> selectFsInquiryOrderListDVO(FsInquiryOrderListDParam param);
 
-    R acceptOrder(FsInquiryOrderAcceptParam param);
+    R acceptOrder(FsInquiryOrderAcceptParam param) throws JsonProcessingException;
 
-    R receiveOrder(FsInquiryOrderReceiveParam param);
+    R receiveOrder(FsInquiryOrderReceiveParam param) throws JsonProcessingException;
 
     R refuseOrder(FsInquiryOrderRefuseParam param);
 
-    R finishOrder(FsInquiryOrderFinishParam param);
+    R finishOrder(FsInquiryOrderFinishParam param) throws JsonProcessingException;
 
-    R autoFinishOrder(FsInquiryOrderFinishParam param);
+    R autoFinishOrder(FsInquiryOrderFinishParam param) throws JsonProcessingException;
 
     R pingOrder(FsInquiryOrderPingParam param);
 
@@ -109,7 +110,7 @@ public interface IFsInquiryOrderService
 
     R createOrderByPackageOrderStatus4(FsPackageOrder packageOrder);
 
-    String sendStartMsg(Long orderId);
+    String sendStartMsg(Long orderId) throws JsonProcessingException;
 
     R computeOrder(FsInquiryOrderComputeParam param, FsInquiryOrder order, FsUser user);
 

+ 45 - 8
fs-service/src/main/java/com/fs/his/service/impl/FsDoctorServiceImpl.java

@@ -2,6 +2,7 @@ package com.fs.his.service.impl;
 
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.SecurityUtils;
@@ -21,11 +22,10 @@ import com.fs.his.utils.ConfigUtil;
 import com.fs.his.utils.HttpUtil;
 import com.fs.his.utils.qrcode.QRCodeUtils;
 import com.fs.his.vo.*;
-import com.fs.im.dto.MsgCustomDTO;
-import com.fs.im.dto.MsgDTO;
-import com.fs.im.dto.MsgDataDTO;
-import com.fs.im.dto.MsgDataFormatDTO;
+import com.fs.im.config.IMConfig;
+import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.oss.CloudStorageService;
@@ -33,6 +33,7 @@ import com.fs.system.oss.OSSFactory;
 import com.google.common.reflect.TypeToken;
 import com.google.gson.Gson;
 import com.google.zxing.WriterException;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -51,6 +52,7 @@ import java.util.List;
  * @author fs
  * @date 2023-06-05
  */
+@Slf4j
 @Service
 public class FsDoctorServiceImpl implements IFsDoctorService
 {
@@ -71,6 +73,9 @@ public class FsDoctorServiceImpl implements IFsDoctorService
     private IFsPrescribeService fsPrescribeService;
     @Autowired
     ConfigUtil configUtil;
+    @Autowired
+    private OpenIMService openIMService;
+
     /**
      * 查询医生管理
      *
@@ -411,7 +416,21 @@ public class FsDoctorServiceImpl implements IFsDoctorService
                         msg.setMsgType("TIMCustomElem");//TIMCustomElem
                         msgs.add(msg);
                         msgDTO.setMsgBody(msgs);
-                        imService.sendMsg(msgDTO);
+                        //imService.sendMsg(msgDTO);
+                        ObjectMapper objectMapper = new ObjectMapper();
+                        String ex = objectMapper.writeValueAsString(customDTO3);
+                        openIMService.sendUtil("D"+fsFollow.getDoctorId(),"U"+fsFollow.getUserId(),110,"follow","","",followId,"",ex);
+                        /*OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+                        openImMsgDTO.setSendID("D"+fsFollow.getDoctorId().toString());
+                        openImMsgDTO.setRecvID("U"+fsFollow.getUserId().toString());
+                        openImMsgDTO.setContentType(110);
+                        openImMsgDTO.setSenderPlatformID(5);
+                        openImMsgDTO.setSessionType(1);
+                        OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+                        content.setContent(ext);
+                        openImMsgDTO.setContent(content);
+                        //openImMsgDTO.setEx(customDTO);
+                        openIMService.openIMSendMsg(openImMsgDTO);*/
 
                         MsgDTO msgDTO2 = new MsgDTO();
                         MsgCustomDTO customDTO = new MsgCustomDTO();
@@ -430,10 +449,28 @@ public class FsDoctorServiceImpl implements IFsDoctorService
                         msg2.setMsgType("TIMTextElem");//TIMCustomElem
                         msgs2.add(msg2);
                         msgDTO2.setMsgBody(msgs2);
-                        imService.sendMsg(msgDTO2);
+                        //imService.sendMsg(msgDTO2);
+                        OpenImMsgDTO openImMsgDTO1 = new OpenImMsgDTO();
+                        openImMsgDTO1.setSendID("D"+fsFollow.getDoctorId().toString());
+                        openImMsgDTO1.setRecvID("U"+fsFollow.getUserId().toString());
+                        openImMsgDTO1.setContentType(101);
+                        openImMsgDTO1.setSenderPlatformID(5);
+                        openImMsgDTO1.setSessionType(1);
+                        OpenImMsgDTO.Content content1 = new OpenImMsgDTO.Content();
+                        content1.setContent("尊敬的用户,为了您的健康,请认真填写随访单,以便医生随时了解您的用药情况,评估治疗效果。如果在用药中出现任何不适,可直接在对话框中描述您目前的情况,我将及时为您解答!");
+                        openImMsgDTO1.setContent(content1);
+                        //openImMsgDTO.setEx(customDTO);
+                        OpenImMsgDTO.OfflinePushInfo offlinePushInfo = new OpenImMsgDTO.OfflinePushInfo();
+                        offlinePushInfo.setDesc(content1.getContent());
+                        String doctorNameByIds = fsDoctorMapper.selectDoctorNameByIds(fsFollow.getDoctorId().toString());
+                        offlinePushInfo.setTitle(doctorNameByIds);
+                        offlinePushInfo.setIOSBadgeCount(true);
+                        offlinePushInfo.setIOSPushSound("");
+                        openImMsgDTO1.setOfflinePushInfo(offlinePushInfo);
+                        openIMService.openIMSendMsg(openImMsgDTO1);
                     }
                 }catch (Exception e){
-
+                    log.error("随访失败!订单编号: {}",order.getOrderId(),e);
                 }
             }
         }
@@ -459,7 +496,7 @@ public class FsDoctorServiceImpl implements IFsDoctorService
 
     @Override
     public String selectDoctorByIds(String doctorId) {
-        return fsDoctorMapper.selectDoctorNameByIds(doctorIds);
+        return fsDoctorMapper.selectDoctorNameByIds(doctorId);
     }
 
     public static BufferedImage convertCircular(BufferedImage bi1) throws IOException {

+ 53 - 10
fs-service/src/main/java/com/fs/his/service/impl/FsFollowServiceImpl.java

@@ -5,10 +5,13 @@ import java.util.*;
 
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.common.annotation.Log;
 import com.fs.common.utils.DateUtils;
 import com.fs.his.domain.FsFollowTemp;
 import com.fs.his.domain.FsPackageOrder;
+import com.fs.his.mapper.FsDoctorMapper;
 import com.fs.his.mapper.FsFollowTempMapper;
 import com.fs.his.mapper.FsPackageOrderMapper;
 import com.fs.his.param.FsFollowListDParam;
@@ -16,8 +19,10 @@ import com.fs.his.param.FsFollowListUParam;
 import com.fs.his.param.FsFollowParam;
 import com.fs.his.param.FsStoreOrderParam;
 import com.fs.his.vo.*;
+import com.fs.im.config.IMConfig;
 import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import org.slf4j.Logger;
@@ -47,6 +52,10 @@ public class FsFollowServiceImpl implements IFsFollowService
     private IImService imService;
     @Autowired
     private SysConfigMapper sysConfigMapper;
+    @Autowired
+    private OpenIMService openIMService;
+    @Autowired
+    private FsDoctorMapper fsDoctorMapper;
     Logger logger= LoggerFactory.getLogger(getClass());
     /**
      * 查询随访管理
@@ -133,7 +142,7 @@ public class FsFollowServiceImpl implements IFsFollowService
     }
 
     @Override
-    public void addFsFollowByPackageOrder(FsPackageOrder order) {
+    public void addFsFollowByPackageOrder(FsPackageOrder order) throws JsonProcessingException {
         SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.package");
         Map<String, Object> config = (Map<String, Object>) JSON.parse(sysConfig.getConfigValue());
         Integer followRate = (Integer)config.get("followRate");
@@ -160,17 +169,17 @@ public class FsFollowServiceImpl implements IFsFollowService
             fsFollow.setFormJson(fsFollowTemp.getFormJson());
             fsFollow.setCreateTime(DateUtils.getNowDate());
             int i = fsFollowMapper.insertFsFollow(fsFollow);
-            MsgDTO msgDTO=new MsgDTO();
+            //MsgDTO msgDTO=new MsgDTO();
             MsgCustomDTO customDTO=new MsgCustomDTO();
             customDTO.setType("follow");
             customDTO.setOrderType(2);
             customDTO.setFollowId(fsFollow.getFollowId().toString());
             customDTO.setOrderId(fsFollow.getPackageOrderId().toString());
             customDTO.setImType(2);
-            msgDTO.setCloudCustomData(JSONUtil.toJsonStr(customDTO));
+            //msgDTO.setCloudCustomData(JSONUtil.toJsonStr(customDTO));
 
-            msgDTO.setFrom_Account("D-"+fsFollow.getDoctorId());
-            msgDTO.setTo_Account("U-"+fsFollow.getUserId());
+            //msgDTO.setFrom_Account("D-"+fsFollow.getDoctorId());
+            //msgDTO.setTo_Account("U-"+fsFollow.getUserId());
             List<MsgDataDTO> msgs=new ArrayList<>();
             MsgDataDTO msg=new MsgDataDTO();
             String ext= JSONUtil.toJsonStr(fsFollow);
@@ -178,9 +187,24 @@ public class FsFollowServiceImpl implements IFsFollowService
             msg.setMsgContent(new MsgDataFormatDTO("follow",ext,followId));
             msg.setMsgType("TIMCustomElem");//TIMCustomElem
             msgs.add(msg);
-            msgDTO.setMsgBody(msgs);
-            MsgResponseDTO msgResponseDTO = imService.sendMsg(msgDTO);
-            logger.info("发送随访"+msgResponseDTO);
+            //msgDTO.setMsgBody(msgs);
+            //MsgResponseDTO msgResponseDTO = imService.sendMsg(msgDTO);
+            ObjectMapper objectMapper = new ObjectMapper();
+            String ex = objectMapper.writeValueAsString(customDTO);
+            OpenImResponseDTO follow = openIMService.sendUtil("D"+fsFollow.getDoctorId(), "U"+fsFollow.getUserId(), 110, "follow", "", "", followId, "",ex);
+            /*OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+            openImMsgDTO.setSendID("D"+fsFollow.getDoctorId().toString());
+            openImMsgDTO.setRecvID("U"+fsFollow.getUserId().toString());
+            openImMsgDTO.setContentType(110);
+            openImMsgDTO.setSenderPlatformID(5);
+            openImMsgDTO.setSessionType(1);
+            OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+            content.setContent(ext);
+            openImMsgDTO.setContent(content);
+            //openImMsgDTO.setEx(customDTO);
+            OpenImResponseDTO openImResponseDTO = openIMService.openIMSendMsg(openImMsgDTO);*/
+
+            logger.info("发送随访"+follow);
 
             MsgDTO msgDTO2=new MsgDTO();
             msgDTO2.setCloudCustomData(JSONUtil.toJsonStr(customDTO));
@@ -195,8 +219,27 @@ public class FsFollowServiceImpl implements IFsFollowService
             msgs2.add(msg2);
             msgDTO2.setMsgBody(msgs2);
 
-            MsgResponseDTO msgResponseDTO2 = imService.sendMsg(msgDTO2);
-            logger.info("发送随访2"+msgResponseDTO2);
+            //MsgResponseDTO msgResponseDTO2 = imService.sendMsg(msgDTO2);
+            OpenImMsgDTO openImMsgDTO1 = new OpenImMsgDTO();
+            openImMsgDTO1.setSendID("D"+fsFollow.getDoctorId().toString());
+            openImMsgDTO1.setRecvID("U"+fsFollow.getUserId().toString());
+            openImMsgDTO1.setContentType(101);
+            openImMsgDTO1.setSenderPlatformID(5);
+            openImMsgDTO1.setSessionType(1);
+            OpenImMsgDTO.Content content1 = new OpenImMsgDTO.Content();
+            content1.setContent("尊敬的用户,为了您的健康,请认真填写随访单,以便医生随时了解您的用药情况,评估治疗效果。如果在用药中出现任何不适,可直接在对话框中描述您目前的情况,我将及时为您解答!");
+            openImMsgDTO1.setContent(content1);
+            //openImMsgDTO.setEx(customDTO);
+            OpenImMsgDTO.OfflinePushInfo offlinePushInfo = new OpenImMsgDTO.OfflinePushInfo();
+            offlinePushInfo.setDesc(content1.getContent());
+            String doctorNameByIds = fsDoctorMapper.selectDoctorNameByIds(fsFollow.getDoctorId().toString());
+            offlinePushInfo.setTitle(doctorNameByIds);
+            offlinePushInfo.setIOSBadgeCount(true);
+            offlinePushInfo.setIOSPushSound("");
+            openImMsgDTO1.setOfflinePushInfo(offlinePushInfo);
+            OpenImResponseDTO openImResponseDTO1 = openIMService.openIMSendMsg(openImMsgDTO1);
+
+            logger.info("发送随访"+openImResponseDTO1);
             if (i>0){
                 FsPackageOrder fsPackageOrder = new FsPackageOrder();
                 fsPackageOrder.setOrderId(order.getOrderId());

+ 274 - 4
fs-service/src/main/java/com/fs/his/service/impl/FsInquiryOrderMsgServiceImpl.java

@@ -9,32 +9,42 @@ import java.util.List;
 import java.util.Map;
 
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.json.JsonMapper;
 import com.fs.common.service.impl.SmsServiceImpl;
 import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.event.TemplateBean;
 import com.fs.event.TemplateEvent;
 import com.fs.event.TemplateListenEnum;
 import com.fs.event.TemplateListener;
 import com.fs.his.domain.*;
 import com.fs.his.dto.FsInquiryOrderPatientDTO;
-import com.fs.his.mapper.FsDoctorMapper;
-import com.fs.his.mapper.FsFollowReportMapper;
-import com.fs.his.mapper.FsUserMapper;
+import com.fs.his.dto.PayloadDTO;
+import com.fs.his.mapper.*;
 import com.fs.his.param.FsFollowReportParam;
 import com.fs.his.param.FsInquiryOrderMsgListDParam;
 import com.fs.his.param.ImMsgParam;
 import com.fs.his.service.IFsFollowReportService;
+import com.fs.his.service.IFsInquiryOrderReportService;
 import com.fs.his.vo.FsInquiryOrderMsgListDVO;
+import com.fs.im.config.IMConfig;
+import com.fs.im.dto.OpenImMsgCallBackResponse;
 import com.fs.im.service.IImService;
+import com.fs.im.vo.OpenImMsgCallBackVO;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
+import com.fs.watch.domain.WatchDeviceInfo;
+import com.fs.watch.mapper.WatchDeviceInfoMapper;
+import com.fs.watch.service.DeviceSetUpService;
+import lombok.extern.slf4j.Slf4j;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Service;
-import com.fs.his.mapper.FsInquiryOrderMsgMapper;
 import com.fs.his.service.IFsInquiryOrderMsgService;
 
 /**
@@ -43,6 +53,7 @@ import com.fs.his.service.IFsInquiryOrderMsgService;
  * @author fs
  * @date 2023-06-12
  */
+@Slf4j
 @Service
 public class FsInquiryOrderMsgServiceImpl implements IFsInquiryOrderMsgService
 {
@@ -60,6 +71,14 @@ public class FsInquiryOrderMsgServiceImpl implements IFsInquiryOrderMsgService
     FsUserMapper fsUserMapper;
     @Autowired
     ApplicationEventPublisher publisher;
+    @Autowired
+    private FsPrescribeMapper fsPrescribeMapper;
+    @Autowired
+    private IFsInquiryOrderReportService fsInquiryOrderReportService;
+    @Autowired
+    private WatchDeviceInfoMapper watchDeviceInfoMapper;
+    @Autowired
+    private DeviceSetUpService deviceSetUpService;
 
 //    @Autowired
 //    TemplateListener publisher;
@@ -308,5 +327,256 @@ public class FsInquiryOrderMsgServiceImpl implements IFsInquiryOrderMsgService
     public List<FsInquiryOrderMsgListDVO> selectFsInquiryOrderMsgListDVO(FsInquiryOrderMsgListDParam param) {
         return fsInquiryOrderMsgMapper.selectFsInquiryOrderMsgListDVO(param);
     }
+    @Override
+    public OpenImMsgCallBackResponse openImSaveMsg(OpenImMsgCallBackVO openImMsgCallBackVO) throws JsonProcessingException {
+        OpenImMsgCallBackResponse openImMsgCallBackResponse = new OpenImMsgCallBackResponse();
+        ObjectMapper objectMapper = new ObjectMapper();
+        try {
+            if (openImMsgCallBackVO.getCallbackCommand().equals("callbackBeforeAfterMsgCommand")){
+                fsInquiryOrderMsgMapper.deleteFsInquiryOrderMsgByMsgKey(openImMsgCallBackVO.getClientMsgID());
+                return openImMsgCallBackResponse;
+            }
+            String send =openImMsgCallBackVO.getSendID();
+            String to =openImMsgCallBackVO.getRecvID();
+            Long time =openImMsgCallBackVO.getSendTime();
+            Date date = new Date(time);
+            String content = openImMsgCallBackVO.getContent();
+
+
+            String userId="";
+            String doctorId="";
+            String companyUserId="";
+            String msgType="2";
+            //用户发送消息
+            if (send.contains("U")){
+                msgType="1";
+                userId=send.replace("U","");
+                if (to.contains("D")){
+                    doctorId=to.replace("D","");
+                    fsFollowReportService.addReport(userId,doctorId);
+                }else if (to.contains("C")){
+                    companyUserId=to.replace("C","");
+                }
+                //医生发送消息
+            }else if (send.contains("D")){
+                doctorId=send.replace("D","");
+                if (to.contains("U")){
+                    userId=to.replace("U","");
+                    TemplateBean templateBean = TemplateBean.builder()
+                            .title("您收到咨询回复")
+                            .remark("您的咨询已回复")
+                            .uid(Long.parseLong(userId))
+                            .templateType(TemplateListenEnum.TYPE_5.getValue())
+                            .build();
+                    publisher.publishEvent(new TemplateEvent(this, templateBean));
+                    if (openImMsgCallBackVO.getContentType()==101){
+                        if (content.contains("您的信息我已收到")){
+                            long doctorIdL = Long.parseLong(doctorId);
+                            long userIdL = Long.parseLong(userId);
+                            int count = fsFollowReportMapper.selectFollowByUserIdAndDoctorIdAndType(userIdL, doctorIdL);
+                            if (count==0){
+                                FsUser fsUser = fsUserMapper.selectFsUserByUserId(userIdL);
+                                if (fsUser!=null&&fsUser.getPhone()!=null){
+                                    FsInquiryOrder order = fsFollowReportMapper.selectFsInquiryOrderByUserAndDoc(userIdL, doctorIdL);
+                                    if (order!=null){
+                                        FsInquiryOrderPatientDTO patientDTO = JSON.parseObject(order.getPatientJson(),FsInquiryOrderPatientDTO.class);
+                                        if (patientDTO!=null&&patientDTO.getPatientName()!=null){
+                                            FsDoctor doctor=doctorMapper.selectFsDoctorByDoctorId(doctorIdL);
+                                            if (doctor.getDeptId()!=null&&doctor.getDeptId().compareTo(39L)==0){
+                                                logger.info("药师回复发送短信:"+fsUser.getPhone()+patientDTO.getPatientName());
+                                                smsService.sendUserSms(fsUser.getPhone(), patientDTO.getPatientName(), "7");
+                                            }else {
+                                                logger.info("医生回复发送短信:"+patientDTO.getMobile()+patientDTO.getPatientName());
+                                                smsService.sendUserSms(fsUser.getPhone(), patientDTO.getPatientName(), "4");
+                                            }
+                                        }
+                                    }else {
+                                        FsFollow fo = fsFollowReportMapper.selectFsFollowById(userIdL, doctorIdL);
+                                        if (fo!=null&&fo.getPatientName()!=null){
+                                            logger.info("药师回复发送短信:"+fsUser.getPhone()+fo.getPatientName());
+                                            smsService.sendUserSms(fsUser.getPhone(), fo.getPatientName(), "7");
+                                        }
+                                    }
+
+                                }
+
+                            }
+                        }
+                    }
+                }else {
+                    companyUserId = to.replace("C","");
+                }
+                //销售发送消息
+            }else if (send.contains("C")){
+                companyUserId=send.replace("C","");
+                if (to.contains("U")){
+                    userId=to.replace("U","");
+                }else {
+                    doctorId = to.replace("D","");
+                }
+            }
+            Long orderId = fsInquiryOrderMsgMapper.selectFsInquiryOrderMsgOrderId(doctorId, userId);
+            if (orderId==null){
+                orderId = fsInquiryOrderMsgMapper.selectFsInquiryOrderMsgOrderIdByDate(doctorId, userId,date);
+            }
+
+            Integer msgContentType = openImMsgCallBackVO.getContentType();
+            Integer type=0;
+            String cont="";
+            JsonNode jsonNode = null;
+            if (msgContentType != null ) {
+                switch (msgContentType) {
+                    //普通消息
+                    case 101:
+                        type=1;
+                        jsonNode = objectMapper.readTree(content);
+                        cont=jsonNode.get("content").asText();
+                        break;
+                    //语音消息
+                    case 103:
+                        jsonNode = objectMapper.readTree(content); // 转为 JsonNode
+                        String soundUrl = jsonNode.get("sourceUrl").asText();
+                        try {
+                            // 创建URL对象
+                            URL url = new URL(soundUrl);
+                            InputStream in =  url.openStream();
+                            CloudStorageService storage = OSSFactory.build();
+                            cont = storage.uploadSuffix(in, ".m4a");
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                        type=2;
+                        //医生发送的语音消息,这一段是同步语音消息到腕表的
+                        /*if (send.contains("D")){
+                            WatchDeviceInfo u = watchDeviceInfoMapper.selectByUserId(to.replace("U", ""));
+                            if (u!=null&& StringUtils.isNotEmpty(u.getDeviceNumber())){
+                                DeviceSendParam deviceSendParam = new DeviceSendParam();
+                                deviceSendParam.setDeviceId(u.getDeviceNumber());
+                                deviceSendParam.setFileUrl(soundUrl);
+                                deviceSendParam.setSendUserName(openImMsgCallBackVO.getSenderNickname());
+                                deviceSetUpService.sendMp3(deviceSendParam);
+                            }
+                        }*/
+                        break;
+                    //图片消息
+                    case 102:
+                        jsonNode = objectMapper.readTree(content); // 转为 JsonNode
+                        String imgUrl = jsonNode.get("sourcePicture").get("url").asText();
+                        try {
+                            URL url = new URL(imgUrl);
+                            InputStream in =  url.openStream();
+                            CloudStorageService storage = OSSFactory.build();
+                            cont = storage.uploadSuffix(in, ".jpg");
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                        type=3;
+                        break;
+                    //视频消息
+                    case 104:
+                        jsonNode = objectMapper.readTree(content); // 转为 JsonNode
+                        String videoUrl = jsonNode.get("videoUrl").asText();
+                        try {
+                            URL url = new URL(videoUrl);
+                            InputStream in =  url.openStream();
+                            CloudStorageService storage = OSSFactory.build();
+                            cont = storage.uploadSuffix(in, "."+jsonNode.get("videoType").asText());
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                        type=4;
+                        break;
+                    //自定义消息
+                    case 110:
+                        PayloadDTO payloadDTO = objectMapper.readValue(content, PayloadDTO.class);
+
+                        jsonNode = objectMapper.readTree(payloadDTO.getData());
+                        JsonNode payload = jsonNode.get("payload");
+                        String data = payload.get("data").asText();
+                        JsonNode extension = payload.get("extension");
+                        if (data.equals("prescribe")){
+                            String prescribeId = extension.get("prescribeId").asText();
+                            FsPrescribe fsPrescribe = fsPrescribeMapper.selectFsPrescribeByPrescribeId(Long.parseLong(prescribeId));
+                            cont=objectMapper.writeValueAsString(fsPrescribe);
+                            //orderId = fsPrescribe.getInquiryOrderId();
+                            type=5;
+                            break;
+                        }else if (data.equals("report")){
+
+                            String description = payload.get("description").asText();
+                            FsInquiryOrderReport fsInquiryOrderReport = fsInquiryOrderReportService.selectFsInquiryOrderReportByOrderId(Long.parseLong(description));
+                            if (fsInquiryOrderReport!=null){
+                                cont = fsInquiryOrderReport.getOrderId().toString();
+                            }
+                            type=6;
+                            break;
+                        }else if (data.equals("follow")){
+                            cont=payload.get("extension").get("followId").asText();
+                            type= 7;
+                            //orderId = payload.get("extension").get("followId").asLong();
+                            break;
+                        }else if (data.equals("drugReport")){
+                            cont=payload.get("description").asText();
+                            //orderId = payload.get("description").asLong();
+                            type= 8;
+                            break;
+                        } else if (data.equals("package")){
+                            cont=payload.get("extension").get("title").asText();
+                            //orderId = payload.get("description").asLong();
+                            type= 9;
+                            break;
+                        }else if (data.equals("couponPackage")){
+                            cont=payload.get("extension").get("title").asText();
+                            //orderId = payload.get("description").asLong();
+                            type= 10;
+                            break;
+                        }else if (data.equals("inquirySelect")){
+                            cont=payload.get("extension").get("title").asText();
+                            //orderId = payload.get("description").asLong();
+                            type= 11;
+                            break;
+                        }else if (data.equals("startInquiry")||data.equals("finishInquiry")){
+                            cont =payload.get("extension").get("title").asText();
+                            type=1;
+                            break;
+                        }
+
+
+                }
+            }
+            if (StringUtils.isEmpty(cont)){
+                openImMsgCallBackResponse.setErrMsg("无消息内容,未保存到数据库");
+                return openImMsgCallBackResponse;
+            }
+            if (orderId==null&&StringUtils.isNotEmpty(openImMsgCallBackVO.getEx())){
+                JsonNode exJson = objectMapper.readTree(openImMsgCallBackVO.getEx());
+                orderId = exJson.get("orderId").asLong();
+            }
+            FsInquiryOrderMsg msg = new FsInquiryOrderMsg();
+            msg.setOrderId(orderId);
+            msg.setMsgContentType(type);
+            msg.setFromAccount(userId);
+            msg.setCompanyUserAccount(companyUserId);
+            msg.setToAccount(doctorId);
+            msg.setMsgKey(openImMsgCallBackVO.getClientMsgID());
+            msg.setContent(cont);
+            msg.setCreateTime(date);
+            msg.setMsgType(msgType);
+            fsInquiryOrderMsgMapper.insertFsInquiryOrderMsg(msg);
+            // 极光推送
+            //String pushContent = (type == 1) ? content :"您有一条新消息";
+            //uniPush2Service.pushOne(Long.parseLong(to.replace("U","")), orderId, null, "新消息提醒", pushContent, PushLogTypeEnum.MARKET.getValue(), PushLogDesTypeEnum.MARKET_PUSH.getValue());
+            log.info("返回的参数 {}", JSON.toJSONString(openImMsgCallBackResponse));
+            return openImMsgCallBackResponse;
+        } catch (Exception e){
+            openImMsgCallBackResponse.setActionCode(202);
+            openImMsgCallBackResponse.setErrCode(100);
+            openImMsgCallBackResponse.setErrDlt("");
+            openImMsgCallBackResponse.setErrMsg(e.getMessage());
+            log.info("返回的参数 {}", JSON.toJSONString(openImMsgCallBackResponse));
+            return openImMsgCallBackResponse;
+        }
+
+    }
 
 }

+ 96 - 21
fs-service/src/main/java/com/fs/his/service/impl/FsInquiryOrderServiceImpl.java

@@ -7,6 +7,8 @@ import cn.hutool.json.JSONUtil;
 import cn.jiguang.common.resp.APIConnectionException;
 import cn.jiguang.common.resp.APIRequestException;
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.common.constant.FsConstants;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
@@ -42,8 +44,10 @@ import com.fs.his.vo.*;
 import com.fs.huifuPay.domain.HuiFuRefundResult;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayRefundRequest;
 import com.fs.huifuPay.service.HuiFuService;
+import com.fs.im.config.IMConfig;
 import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
 import com.fs.jpush.service.JpushService;
 import com.fs.repeat.vo.RepeatUploadVo;
 import com.fs.system.domain.SysConfig;
@@ -175,6 +179,8 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
 
     @Autowired
     private ConfigUtil configUtil;
+    @Autowired
+    private OpenIMService openIMService;
     /**
      * 查询问诊订单
      *
@@ -863,7 +869,7 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
     @Override
     @Transactional
     @Synchronized
-    public R acceptOrder(FsInquiryOrderAcceptParam param) {
+    public R acceptOrder(FsInquiryOrderAcceptParam param) throws JsonProcessingException {
         FsInquiryOrder order=fsInquiryOrderMapper.selectFsInquiryOrderByOrderId(param.getOrderId());
         if(!order.getStatus().equals(FsInquiryOrderStatusEnum.STATUS_2.getValue())){
             return R.error("非法操作");
@@ -906,32 +912,44 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
 
             }
         }
-        MsgDTO msgDTO=new MsgDTO();
+        //MsgDTO msgDTO=new MsgDTO();
         MsgCustomDTO customDTO=new MsgCustomDTO();
         customDTO.setType("startInquiry");
         customDTO.setOrderType(order.getOrderType());
         customDTO.setOrderId(order.getOrderId().toString());
         customDTO.setImType(1);
-        msgDTO.setCloudCustomData(JSONUtil.toJsonStr(customDTO));
+        /*msgDTO.setCloudCustomData(JSONUtil.toJsonStr(customDTO));
         msgDTO.setFrom_Account("D-"+param.getDoctorId());
-        msgDTO.setTo_Account("U-"+order.getUserId());
-        List<MsgDataDTO> msgs=new ArrayList<>();
+        msgDTO.setTo_Account("U-"+order.getUserId());*/
+        /*List<MsgDataDTO> msgs=new ArrayList<>();
         MsgDataDTO msg=new MsgDataDTO();
-        msg.setMsgType("TIMCustomElem");
+        msg.setMsgType("TIMCustomElem");*/
         FsDoctor doctor=doctorMapper.selectFsDoctorByDoctorId(param.getDoctorId());
         String doc="医生";
         if (doctor.getDeptId()!=null&&doctor.getDeptId().compareTo(39L)==0){
             doc="药师";
         }
-        InquiryOrderMsgDTO inquiryOrderMsgDTO=new InquiryOrderMsgDTO();
+        /*InquiryOrderMsgDTO inquiryOrderMsgDTO=new InquiryOrderMsgDTO();
         inquiryOrderMsgDTO.setTitle(doctor.getDoctorName()+doc+"为您服务");
         String ext= JSONUtil.toJsonStr(inquiryOrderMsgDTO);
         msg.setMsgContent(new MsgDataFormatDTO("startInquiry",ext,order.getOrderId().toString()));
         msgs.add(msg);
-        msgDTO.setMsgBody(msgs);
-        imService.sendMsg(msgDTO);
-
-
+        msgDTO.setMsgBody(msgs);*/
+        //imService.sendMsg(msgDTO);
+        ObjectMapper objectMapper = new ObjectMapper();
+        String ex = objectMapper.writeValueAsString(customDTO);
+        openIMService.sendUtil("D"+param.getDoctorId(),"U"+order.getUserId(),110,"startInquiry","",doctor.getDoctorName()+doc+"为您服务","","","");
+        /*OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+        openImMsgDTO.setSendID("D"+param.getDoctorId().toString());
+        openImMsgDTO.setRecvID("U"+order.getUserId().toString());
+        openImMsgDTO.setContentType(110);
+        openImMsgDTO.setSenderPlatformID(5);
+        openImMsgDTO.setSessionType(1);
+        OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+        content.setContent(ext);
+        openImMsgDTO.setContent(content);
+        openImMsgDTO.setEx(customDTO);
+        openIMService.openIMSendMsg(openImMsgDTO);*/
         if (doc.equals("药师")){
             MsgDTO msgDTO1=new MsgDTO();
             MsgCustomDTO customDTO1=new MsgCustomDTO();
@@ -949,7 +967,22 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
             msg1.setMsgContent(dto1);
             msgs1.add(msg1);
             msgDTO1.setMsgBody(msgs1);
-            imService.sendMsg(msgDTO1);
+            //imService.sendMsg(msgDTO1);
+
+            ObjectMapper objectMapper1 = new ObjectMapper();
+            String ex1 = objectMapper.writeValueAsString(customDTO1);
+            openIMService.sendUtil("D"+param.getDoctorId(),"U"+order.getUserId(),110,"startInquiry","","您好,我是芸医汇互联网医院执业药师,请问您有什么问题想咨询?","","",ex1);
+            /*OpenImMsgDTO openImMsgDTO1 = new OpenImMsgDTO();
+            openImMsgDTO1.setSendID("D"+param.getDoctorId().toString());
+            openImMsgDTO1.setRecvID("U"+order.getUserId().toString());
+            openImMsgDTO1.setContentType(101);
+            openImMsgDTO1.setSenderPlatformID(5);
+            openImMsgDTO1.setSessionType(1);
+            OpenImMsgDTO.Content content1 = new OpenImMsgDTO.Content();
+            content1.setContent(ext);
+            openImMsgDTO1.setContent(content1);
+            openImMsgDTO1.setEx(customDTO);
+            openIMService.openIMSendMsg(openImMsgDTO1);*/
         }
 
 
@@ -1003,7 +1036,7 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
 
     @Override
     @Transactional
-    public R receiveOrder(FsInquiryOrderReceiveParam param) {
+    public R receiveOrder(FsInquiryOrderReceiveParam param) throws JsonProcessingException {
         FsInquiryOrder order=fsInquiryOrderMapper.selectFsInquiryOrderByOrderId(param.getOrderId());
         if(!order.getStatus().equals(FsInquiryOrderStatusEnum.STATUS_2.getValue())){
             return R.error("非法操作");
@@ -1066,7 +1099,21 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
         msg.setMsgContent(new MsgDataFormatDTO("startInquiry",ext,order.getOrderId().toString()));
         msgs.add(msg);
         msgDTO.setMsgBody(msgs);
-        imService.sendMsg(msgDTO);
+        //imService.sendMsg(msgDTO);
+        ObjectMapper objectMapper = new ObjectMapper();
+        String ex = objectMapper.writeValueAsString(customDTO);
+        openIMService.sendUtil("D"+order.getDoctorId(),"U"+order.getUserId(),110,"startInquiry","",doctor.getDoctorName()+doc+"为您服务","","",ex);
+        /*OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+        openImMsgDTO.setSendID("D"+order.getDoctorId().toString());
+        openImMsgDTO.setRecvID("U"+order.getUserId().toString());
+        openImMsgDTO.setContentType(110);
+        openImMsgDTO.setSenderPlatformID(5);
+        openImMsgDTO.setSessionType(1);
+        OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+        content.setContent(ext);
+        openImMsgDTO.setContent(content);
+        openImMsgDTO.setEx(customDTO);
+        openIMService.openIMSendMsg(openImMsgDTO);*/
         if (doc.equals("药师")){
             MsgDTO msgDTO1=new MsgDTO();
             MsgCustomDTO customDTO1=new MsgCustomDTO();
@@ -1084,7 +1131,22 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
             msg1.setMsgContent(dto1);
             msgs1.add(msg1);
             msgDTO1.setMsgBody(msgs1);
-            imService.sendMsg(msgDTO1);
+            //imService.sendMsg(msgDTO1);
+
+            ObjectMapper objectMapper1 = new ObjectMapper();
+            String ex1 = objectMapper.writeValueAsString(customDTO1);
+            openIMService.sendUtil("D"+param.getDoctorId(),"U"+order.getUserId(),110,"startInquiry","","您好,我是芸医汇互联网医院执业药师,请问您有什么问题想咨询?","","",ex1);
+           /* OpenImMsgDTO openImMsgDTO1 = new OpenImMsgDTO();
+            openImMsgDTO1.setSendID("D"+order.getDoctorId().toString());
+            openImMsgDTO1.setRecvID("U"+order.getUserId().toString());
+            openImMsgDTO1.setContentType(110);
+            openImMsgDTO1.setSenderPlatformID(5);
+            openImMsgDTO1.setSessionType(1);
+            OpenImMsgDTO.Content content1 = new OpenImMsgDTO.Content();
+            content1.setContent(ext);
+            openImMsgDTO1.setContent(content1);
+            openImMsgDTO1.setEx(customDTO);
+            openIMService.openIMSendMsg(openImMsgDTO1);*/
         }
         //发送给医生
 //        MsgDTO msgDTO1=new MsgDTO();
@@ -1181,7 +1243,7 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
 
     @Override
     @Transactional
-    public R finishOrder(FsInquiryOrderFinishParam param) {
+    public R finishOrder(FsInquiryOrderFinishParam param) throws JsonProcessingException {
         FsInquiryOrder order=fsInquiryOrderMapper.selectFsInquiryOrderByOrderId(param.getOrderId());
         if(!order.getStatus().equals(FsInquiryOrderStatusEnum.STATUS_3.getValue())){
             return R.error("非法操作");
@@ -1221,7 +1283,11 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
         msg.setMsgContent(new MsgDataFormatDTO("finishInquiry",ext,order.getOrderId().toString()));
         msgs.add(msg);
         msgDTO.setMsgBody(msgs);
-        imService.sendMsg(msgDTO);
+        //imService.sendMsg(msgDTO);
+        ObjectMapper objectMapper = new ObjectMapper();
+        String ex = objectMapper.writeValueAsString(customDTO);
+        openIMService.sendUtil("D"+order.getDoctorId(),"U"+order.getUserId(),110,"finishInquiry","","医生完成订单","","",ex);
+
         //发送给医生
 //        MsgDTO msgDTO1=new MsgDTO();
 //        MsgCustomDTO customDTO1=new MsgCustomDTO();
@@ -1251,7 +1317,7 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
     }
     @Override
     @Transactional
-    public R autoFinishOrder(FsInquiryOrderFinishParam param) {
+    public R autoFinishOrder(FsInquiryOrderFinishParam param) throws JsonProcessingException {
         FsInquiryOrder order=fsInquiryOrderMapper.selectFsInquiryOrderByOrderId(param.getOrderId());
         if(!order.getStatus().equals(FsInquiryOrderStatusEnum.STATUS_3.getValue())){
             return R.error("非法操作");
@@ -1290,7 +1356,11 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
         msg.setMsgContent(new MsgDataFormatDTO("finishInquiry",ext,order.getOrderId().toString()));
         msgs.add(msg);
         msgDTO.setMsgBody(msgs);
-        imService.sendMsg(msgDTO);
+        //imService.sendMsg(msgDTO);
+        ObjectMapper objectMapper = new ObjectMapper();
+        String ex = objectMapper.writeValueAsString(customDTO);
+        openIMService.sendUtil("D"+order.getDoctorId(),"U"+order.getUserId(),110,"finishInquiry","","医生完成订单","","",ex);
+
         redisTemplate.delete("DrugReport:doctorId:" + order.getDoctorId() + ":userId:" + order.getUserId());
         return R.ok();
     }
@@ -1572,7 +1642,7 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
     }
 
     @Override
-    public String sendStartMsg(Long orderId) {
+    public String sendStartMsg(Long orderId) throws JsonProcessingException {
 
         FsInquiryOrder order = fsInquiryOrderMapper.selectFsInquiryOrderByOrderId(orderId);
         MsgDTO msgDTO=new MsgDTO();
@@ -1594,7 +1664,12 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
         msg.setMsgContent(new MsgDataFormatDTO("startInquiry",ext,order.getOrderId().toString()));
         msgs.add(msg);
         msgDTO.setMsgBody(msgs);
-        imService.sendMsg(msgDTO);
+        //imService.sendMsg(msgDTO);
+
+        ObjectMapper objectMapper = new ObjectMapper();
+        String ex = objectMapper.writeValueAsString(customDTO);
+        openIMService.sendUtil("D"+order.getDoctorId(),"U"+order.getUserId(),110,"startInquiry","1","为您服务","","",ex);
+
         return "OK";
     }
 

+ 16 - 6
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -55,6 +55,7 @@ import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.FsUserExportListVO;
 import com.fs.his.vo.FsUserFollowDoctorVO;
 import com.fs.his.vo.UserVo;
+import com.fs.im.service.OpenIMService;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
 import com.fs.hisStore.domain.FsUserBillScrm;
 import com.fs.hisStore.enums.BillDetailEnum;
@@ -89,8 +90,7 @@ import com.fs.his.service.IFsUserService;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
-import static com.fs.his.utils.PhoneUtil.decryptPhoneMk;
-import static com.fs.his.utils.PhoneUtil.encryptPhone;
+import static com.fs.his.utils.PhoneUtil.*;
 import static com.fs.hisStore.enums.BillDetailEnum.CATEGORY_1;
 import static com.fs.hisStore.enums.BillDetailEnum.CATEGORY_3;
 
@@ -159,6 +159,8 @@ public class FsUserServiceImpl implements IFsUserService
     @Autowired
     private FsUserCourseCountMapper fsUserCourseCountMapper;
 
+    @Autowired
+    private OpenIMService openIMService;
     @Autowired
     private IFsUserBillScrmService billService;
     @Autowired
@@ -585,11 +587,16 @@ public class FsUserServiceImpl implements IFsUserService
 
             //解密
             if(fsUserPageListVO.getPhone() != null && fsUserPageListVO.getPhone() != ""){
-                if (fsUserPageListVO.getPhone().length()>11){
-                    fsUserPageListVO.setPhone(decryptPhoneMk(fsUserPageListVO.getPhone()));
-                }else {
-                    fsUserPageListVO.setPhone(fsUserPageListVO.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+                if(param.getIsHidePhoneMiddle()){
+                    if (fsUserPageListVO.getPhone().length()>11){
+                        fsUserPageListVO.setPhone(decryptPhoneMk(fsUserPageListVO.getPhone()));
+                    }else {
+                        fsUserPageListVO.setPhone(fsUserPageListVO.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+                    }
+                } else {
+                    fsUserPageListVO.setPhone(decryptPhone(fsUserPageListVO.getPhone()));
                 }
+
             }
         }
 
@@ -1071,6 +1078,9 @@ public class FsUserServiceImpl implements IFsUserService
 //            this.setRepeatFansTag(param);
 //        }
 
+        // 与im账号绑定
+        openIMService.checkAndImportFriend(param.getCompanyUserId(), String.valueOf(param.getUserId()));
+
         //如果是设置了需要进入小黑屋,则需要返回提示,否则正常返回
         if(isDefaultBlack == 1){
             return ResponseResult.fail(402, "已成功注册,待管理审核");

+ 31 - 0
fs-service/src/main/java/com/fs/im/config/IMConfig.java

@@ -3,6 +3,8 @@ package com.fs.im.config;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 
+import javax.annotation.PostConstruct;
+
 @Component
 //@ConfigurationProperties(prefix = "openIM")
 public class IMConfig {
@@ -10,6 +12,19 @@ public class IMConfig {
     private String secret;
     @Value("${openIM.userID}")
     private String userID;
+    @Value("${openIM.url}")
+    private String url;
+//    @Value("${openIM.prefix}")
+//    private String prefix;
+    // 静态常量
+    public static String PREFIX;
+    public static String URL;
+
+    @PostConstruct
+    public void init() {
+//        PREFIX = this.prefix;
+        URL = this.url;
+    }
 
     public String getSecret() {
         return secret;
@@ -26,4 +41,20 @@ public class IMConfig {
     public void setUserID(String userID) {
         this.userID = userID;
     }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+//    public String getPrefix() {
+//        return prefix;
+//    }
+//
+//    public void setPrefix(String scrm) {
+//        this.prefix = scrm;
+//    }
 }

+ 67 - 0
fs-service/src/main/java/com/fs/im/domain/ImSendLog.java

@@ -0,0 +1,67 @@
+package com.fs.im.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * openim消息发送记录对象 im_send_log
+ *
+ * @author fs
+ * @date 2025-08-12
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ImSendLog extends BaseEntity{
+
+    /** 发送记录id */
+    private Long logId;
+
+    /** 发送者id */
+    @Excel(name = "发送者id")
+    private String sendId;
+
+    /** 接收者id */
+    @Excel(name = "接收者id")
+    private String recvId;
+
+    /** 发送的标题 */
+    @Excel(name = "发送的标题")
+    private String sendTitle;
+
+    /** 预计发送时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "预计发送时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date planSendTime;
+
+    /** 实际发送时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "实际发送时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date actualSendTime;
+
+    /** 发送类型,1-定时;2-实时 */
+    @Excel(name = "发送类型,1-定时;2-实时")
+    private Integer sendType;
+
+    /** 执行入参json */
+    @Excel(name = "执行入参json")
+    private String paramJson;
+
+    /** 执行状态,0-正常;1-失败 */
+    @Excel(name = "执行状态,0-正常;1-失败")
+    private Integer status;
+
+    /** 执行结果 */
+    @Excel(name = "执行结果")
+    private String resultMessage;
+
+    /** 异常信息 */
+    @Excel(name = "异常信息")
+    private String exceptionInfo;
+
+
+}

+ 52 - 0
fs-service/src/main/java/com/fs/im/dto/OpenImBatchMsgDTO.java

@@ -0,0 +1,52 @@
+package com.fs.im.dto;
+
+import com.fs.his.dto.PayloadDTO;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+/**
+ * 批量发送消息-请求参数
+ */
+@Data
+@Accessors(chain = true)
+public class OpenImBatchMsgDTO {
+
+    private String sendID;
+    private List<String> recvIDs;
+    private String senderNickname;
+    private String senderFaceURL;
+    private Integer senderPlatformID;
+    private Content content;
+    private Integer contentType;
+    private Integer sessionType;
+    private Boolean isOnlineOnly;
+    private Boolean notOfflinePush;
+    private Long sendTime;
+    private OfflinePushInfo offlinePushInfo;
+    private String ex;
+    private Boolean isSendAll; //是否发送给全部人
+
+
+    @Data
+    public static class Content {
+        private String content;
+        private String data; //自定义消息
+        private String description;
+        private String extension;
+    }
+    @Data
+    public static class ImData{
+        private PayloadDTO payload;
+    }
+    @Data
+    public static class OfflinePushInfo {
+        private String title;
+        private String desc;
+        private String ex;
+        private String iOSPushSound;
+        private Boolean iOSBadgeCount;
+
+    }
+}

+ 31 - 0
fs-service/src/main/java/com/fs/im/dto/OpenImBatchResponseDataDTO.java

@@ -0,0 +1,31 @@
+package com.fs.im.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+/**
+ * 批量发送消息-相应参数的data参数
+ */
+@Data
+@Accessors(chain = true)
+public class OpenImBatchResponseDataDTO {
+    /**
+     * 结果列表
+     */
+    private List<Results> results;
+    /**
+     * 向其发送消息失败的用户ID列表
+     */
+    private String[] failedUserIDs;
+
+    @Data
+    public static class Results {
+        private String serverMsgID;//服务器消息ID,预留字段
+        private String clientMsgID;//客户端消息ID,此ID为消息唯一ID
+        private Long sendTime;
+        private String recvID;
+    }
+
+}

+ 12 - 0
fs-service/src/main/java/com/fs/im/dto/OpenImMsgCallBackResponse.java

@@ -0,0 +1,12 @@
+package com.fs.im.dto;
+
+import lombok.Data;
+
+@Data
+public class OpenImMsgCallBackResponse {
+    private Integer actionCode = 0;
+    private Integer errCode = 0;
+    private String errMsg = "";
+    private String errDlt = "";
+    private Integer nextCode = 0;
+}

+ 11 - 0
fs-service/src/main/java/com/fs/im/dto/OpenImResponseDataDTO.java

@@ -0,0 +1,11 @@
+package com.fs.im.dto;
+
+import lombok.Data;
+
+@Data
+public class OpenImResponseDataDTO {
+    private String serverMsgID;
+    private String clientMsgID;
+    private Long sendTime;
+    private String modify;
+}

+ 70 - 0
fs-service/src/main/java/com/fs/im/mapper/ImSendLogMapper.java

@@ -0,0 +1,70 @@
+package com.fs.im.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.im.domain.ImSendLog;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * openim消息发送记录Mapper接口
+ *
+ * @author fs
+ * @date 2025-08-12
+ */
+public interface ImSendLogMapper extends BaseMapper<ImSendLog>{
+    /**
+     * 查询openim消息发送记录
+     *
+     * @param logId openim消息发送记录主键
+     * @return openim消息发送记录
+     */
+    ImSendLog selectImSendLogByLogId(Long logId);
+
+    /**
+     * 查询openim消息发送记录列表
+     *
+     * @param imSendLog openim消息发送记录
+     * @return openim消息发送记录集合
+     */
+    List<ImSendLog> selectImSendLogList(ImSendLog imSendLog);
+
+    /**
+     * 新增openim消息发送记录
+     *
+     * @param imSendLog openim消息发送记录
+     * @return 结果
+     */
+    int insertImSendLog(ImSendLog imSendLog);
+
+    /**
+     * 修改openim消息发送记录
+     *
+     * @param imSendLog openim消息发送记录
+     * @return 结果
+     */
+    int updateImSendLog(ImSendLog imSendLog);
+
+    /**
+     * 删除openim消息发送记录
+     *
+     * @param logId openim消息发送记录主键
+     * @return 结果
+     */
+    int deleteImSendLogByLogId(Long logId);
+
+    /**
+     * 批量删除openim消息发送记录
+     *
+     * @param logIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteImSendLogByLogIds(Long[] logIds);
+
+    /**
+     * 批量新增
+     * @param sendLogs
+     * @return
+     */
+    int insertImSendLogBatch(@Param("sendLogs") List<ImSendLog> sendLogs);
+
+}

+ 61 - 0
fs-service/src/main/java/com/fs/im/service/IImSendLogService.java

@@ -0,0 +1,61 @@
+package com.fs.im.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.im.domain.ImSendLog;
+
+/**
+ * openim消息发送记录Service接口
+ * 
+ * @author fs
+ * @date 2025-08-12
+ */
+public interface IImSendLogService extends IService<ImSendLog>{
+    /**
+     * 查询openim消息发送记录
+     * 
+     * @param logId openim消息发送记录主键
+     * @return openim消息发送记录
+     */
+    ImSendLog selectImSendLogByLogId(Long logId);
+
+    /**
+     * 查询openim消息发送记录列表
+     * 
+     * @param imSendLog openim消息发送记录
+     * @return openim消息发送记录集合
+     */
+    List<ImSendLog> selectImSendLogList(ImSendLog imSendLog);
+
+    /**
+     * 新增openim消息发送记录
+     * 
+     * @param imSendLog openim消息发送记录
+     * @return 结果
+     */
+    int insertImSendLog(ImSendLog imSendLog);
+
+    /**
+     * 修改openim消息发送记录
+     * 
+     * @param imSendLog openim消息发送记录
+     * @return 结果
+     */
+    int updateImSendLog(ImSendLog imSendLog);
+
+    /**
+     * 批量删除openim消息发送记录
+     * 
+     * @param logIds 需要删除的openim消息发送记录主键集合
+     * @return 结果
+     */
+    int deleteImSendLogByLogIds(Long[] logIds);
+
+    /**
+     * 删除openim消息发送记录信息
+     * 
+     * @param logId openim消息发送记录主键
+     * @return 结果
+     */
+    int deleteImSendLogByLogId(Long logId);
+}

+ 31 - 1
fs-service/src/main/java/com/fs/im/service/OpenIMService.java

@@ -3,6 +3,8 @@ package com.fs.im.service;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.core.domain.R;
 import com.fs.company.domain.CompanyUser;
+import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.im.dto.OpenImBatchMsgDTO;
 import com.fs.im.dto.OpenImEditConversationDTO;
 import com.fs.im.dto.OpenImMsgDTO;
 import com.fs.im.dto.OpenImResponseDTO;
@@ -29,6 +31,34 @@ public interface OpenIMService {
     R accountCheck(String userId, String type);
     void checkAndImportFriend(Long companyUserId,String fsUserId);
     OpenImResponseDTO sendCourse(Long userId,Long companyUserId,String url,String title,String linkImageUrl,String cropId) throws JsonProcessingException;
-    void checkAndImportFriendByDianBo(Long companyUserId,String fsUserId,String cropId);
+    void checkAndImportFriendByDianBo(Long companyUserId,String fsUserId,String cropId, boolean isUpdate);
     OpenImResponseDTO updateUserInfo(CompanyUser companyUser);
+
+    OpenImResponseDTO sendPackageUtil(String sendID, String recvID, Integer contentType, String payloadData,String packageName,String packageId);
+
+    /**
+     * 批量发送消息
+     * @param openImBatchMsgDTO 入参
+     * @return
+     */
+    OpenImResponseDTO openIMBatchSendMsg(OpenImBatchMsgDTO openImBatchMsgDTO);
+
+    /**
+     * 会员批量发课
+     * @param batchSendCourseDTO
+     * @return
+     * @throws JsonProcessingException
+     */
+    OpenImResponseDTO batchSendCourse(BatchSendCourseDTO batchSendCourseDTO) throws JsonProcessingException;
+
+    /**
+     * 会有批量发课-任务
+     * @param batchSendCourseDTO
+     * @param openImBatchMsgDTO
+     * @param project
+     * @param sendType
+     * @return
+     */
+    OpenImResponseDTO batchSendMsgTask(BatchSendCourseDTO batchSendCourseDTO, OpenImBatchMsgDTO openImBatchMsgDTO, Long project, Integer sendType);
+
 }

+ 92 - 0
fs-service/src/main/java/com/fs/im/service/impl/ImSendLogServiceImpl.java

@@ -0,0 +1,92 @@
+package com.fs.im.service.impl;
+
+import java.util.List;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import com.fs.im.mapper.ImSendLogMapper;
+import com.fs.im.domain.ImSendLog;
+import com.fs.im.service.IImSendLogService;
+
+/**
+ * openim消息发送记录Service业务层处理
+ *
+ * @author fs
+ * @date 2025-08-12
+ */
+@Service
+public class ImSendLogServiceImpl extends ServiceImpl<ImSendLogMapper, ImSendLog> implements IImSendLogService {
+
+    /**
+     * 查询openim消息发送记录
+     *
+     * @param logId openim消息发送记录主键
+     * @return openim消息发送记录
+     */
+    @Override
+    public ImSendLog selectImSendLogByLogId(Long logId)
+    {
+        return baseMapper.selectImSendLogByLogId(logId);
+    }
+
+    /**
+     * 查询openim消息发送记录列表
+     *
+     * @param imSendLog openim消息发送记录
+     * @return openim消息发送记录
+     */
+    @Override
+    public List<ImSendLog> selectImSendLogList(ImSendLog imSendLog)
+    {
+        return baseMapper.selectImSendLogList(imSendLog);
+    }
+
+    /**
+     * 新增openim消息发送记录
+     *
+     * @param imSendLog openim消息发送记录
+     * @return 结果
+     */
+    @Override
+    public int insertImSendLog(ImSendLog imSendLog)
+    {
+        imSendLog.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertImSendLog(imSendLog);
+    }
+
+    /**
+     * 修改openim消息发送记录
+     *
+     * @param imSendLog openim消息发送记录
+     * @return 结果
+     */
+    @Override
+    public int updateImSendLog(ImSendLog imSendLog)
+    {
+        return baseMapper.updateImSendLog(imSendLog);
+    }
+
+    /**
+     * 批量删除openim消息发送记录
+     *
+     * @param logIds 需要删除的openim消息发送记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteImSendLogByLogIds(Long[] logIds)
+    {
+        return baseMapper.deleteImSendLogByLogIds(logIds);
+    }
+
+    /**
+     * 删除openim消息发送记录信息
+     *
+     * @param logId openim消息发送记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteImSendLogByLogId(Long logId)
+    {
+        return baseMapper.deleteImSendLogByLogId(logId);
+    }
+}

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

@@ -3,16 +3,26 @@ package com.fs.im.service.impl;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.http.HttpRequest;
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.StringUtils;
 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.FsCourseWatchLog;
+import com.fs.course.domain.FsUserCompanyUser;
+import com.fs.course.domain.FsUserCourse;
+import com.fs.course.dto.BatchSendCourseAllDTO;
+import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.course.mapper.FsUserCompanyUserMapper;
+import com.fs.course.mapper.FsUserCourseMapper;
 import com.fs.fastGpt.service.AiHookService;
 import com.fs.his.domain.FsDoctor;
 import com.fs.his.domain.FsFollow;
@@ -22,10 +32,9 @@ import com.fs.his.mapper.FsDoctorMapper;
 import com.fs.his.mapper.FsFollowMapper;
 import com.fs.his.mapper.FsUserMapper;
 import com.fs.im.config.IMConfig;
-import com.fs.im.dto.OpenImConversationDTO;
-import com.fs.im.dto.OpenImEditConversationDTO;
-import com.fs.im.dto.OpenImMsgDTO;
-import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.domain.ImSendLog;
+import com.fs.im.dto.*;
+import com.fs.im.mapper.ImSendLogMapper;
 import com.fs.im.service.OpenIMService;
 import com.fs.im.vo.OpenImMsgCallBackVO;
 import com.fs.im.vo.OpenImResponseDTOTest;
@@ -33,14 +42,17 @@ import com.fs.qw.mapper.QwExternalContactMapper;
 import com.github.pagehelper.util.StringUtil;
 import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
 import org.json.JSONArray;
 import org.json.JSONObject;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
-
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
@@ -67,6 +79,17 @@ public class OpenIMServiceImpl implements OpenIMService {
     @Autowired
     @Lazy
     private AiHookService aiHookService;
+    @Autowired
+    private FsUserCourseMapper fsUserCourseMapper;
+    @Autowired
+    private FsUserCompanyUserMapper fsUserCompanyUserMapper;
+    @Autowired
+    private FsCourseWatchLogMapper courseWatchLogMapper;
+    @Autowired
+    private ImSendLogMapper imSendLogMapper;
+
+//    @Value("${openIM.prefix}")
+//    private String openImPrefix;
     /*@Autowired
     private IFsUserService fsUserService;*/
     @Override
@@ -81,7 +104,7 @@ public class OpenIMServiceImpl implements OpenIMService {
         String adminToken = null;
         // 发起 HTTP POST 请求,获取管理员 token
         try {
-            String response = HttpRequest.post("https://web.im.cdwjyyh.com/api/auth/get_admin_token")
+            String response = HttpRequest.post(IMConfig.URL+"/auth/get_admin_token")
                     .header("operationID", String.valueOf(System.currentTimeMillis()))
                     .body(requestBody.toString())
                     .execute()
@@ -102,6 +125,7 @@ public class OpenIMServiceImpl implements OpenIMService {
         return null;
     }
 
+
     @Override
     public OpenImResponseDTO AiAutoReply(OpenImMsgCallBackVO openImMsgDTO) throws JsonProcessingException {
         try {
@@ -199,6 +223,106 @@ public class OpenIMServiceImpl implements OpenIMService {
 
     }
 
+    private Boolean checkImMsgContent(OpenImMsgCallBackVO openImMsgDTO) {
+        String content = openImMsgDTO.getContent();
+        if (content == null || content.isEmpty() || content.trim().isEmpty()) {
+            return true;
+        }
+        String conversationId = "si_" + openImMsgDTO.getRecvID() + "_" + openImMsgDTO.getSendID();
+        // 三种情况 ①正常聊天  ② 转人工 ③人工之后的聊天
+        // 检测内容是否包含
+        try {
+            //① 存在转人工配置 返回true 跳过自动回复
+            String ex = getIMList(openImMsgDTO.getSendID(),conversationId);
+            if (ex != null && ex.contains("IsArtificial")) {
+                return true;
+            }
+            //② 检查当前语句 是否包含转人工
+            if (content.contains("转人工")) {
+                OpenImEditConversationDTO currentConversation = new OpenImEditConversationDTO();
+                ArrayList<String> userIDs = new ArrayList<>();
+                userIDs.add(openImMsgDTO.getSendID());
+                currentConversation.setUserIDs(userIDs);
+                OpenImConversationDTO openImConversationDTO = new OpenImConversationDTO();
+                openImConversationDTO.setConversationID(conversationId);
+                openImConversationDTO.setConversationType(1);
+                openImConversationDTO.setUserID(openImMsgDTO.getRecvID());
+                // 不存在配置 直接放
+                if (ex == null || StringUtils.isEmpty(ex.trim())) {
+                    JSONObject jsonObject = new JSONObject();
+                    jsonObject.put("IsArtificial", 1);
+                    openImConversationDTO.setEx(jsonObject.toString());
+                } else {
+                    // 存在配置 添加之后再放
+                    JSONObject exJson = new JSONObject(ex);
+                    exJson.put("IsArtificial", 1);
+                    openImConversationDTO.setEx(exJson.toString());
+                }
+                currentConversation.setConversation(openImConversationDTO);
+                OpenImResponseDTO openImResponseDTO1 = editConversation(currentConversation);
+                log.info("修改回话返回参数:{}", openImResponseDTO1);
+                return true;
+            }
+            //③正常i情况 直接返回
+            return false;
+        } catch (Exception e) {
+            log.error("openImMsgCallBack,检测内容是否包含转人工失败:", e);
+            return true;
+        }
+    }
+
+    private  String getIMList( String recvID,String  conversationId)  {
+        int pageNumber = 1;
+        int pageSize = 20;
+        int maxPages = 10; // 最大搜索页数,防止无限循环
+        String adminToken = getAdminToken();
+        try {
+            while (pageNumber <= maxPages) {
+                JSONObject requestBody = new JSONObject();
+                requestBody.put("userID", recvID);
+                JSONObject pagination = new JSONObject();
+                pagination.put("pageNumber", pageNumber);
+                pagination.put("showNumber", pageSize);
+                requestBody.put("pagination", pagination);
+                String body = HttpRequest.post(IMConfig.URL+"/conversation/get_owner_conversation")
+                        .header("operationID", String.valueOf(System.currentTimeMillis()))
+                        .header("token", adminToken)
+                        .body(requestBody.toString())
+                        .execute()
+                        .body();
+                JSONObject jsonResponse = new JSONObject(body);
+                if (jsonResponse.getInt("errCode") == 0) {
+                    JSONObject data = jsonResponse.getJSONObject("data");
+                    int total = data.getInt("total");
+                    if (total > 0) {
+                        JSONArray conversations = data.getJSONArray("conversations");
+                        // 遍历当前页的所有会话
+                        for (int i = 0; i < conversations.length(); i++) {
+                            JSONObject conversation = conversations.getJSONObject(i);
+                            String convId = conversation.getString("conversationID");
+                            // 如果找到目标会话,返回其ex字段
+                            if (conversationId.equals(convId)) {
+                                return conversation.getString("ex");
+                            }
+                        }
+                        // 如果当前页没有找到且已经是最后一页,则退出循环
+                        if (conversations.length() < pageSize) {
+                            break;
+                        }
+                    } else {
+                        // 如果没有数据,直接退出循环
+                        break;
+                    }
+                }
+                // 移动到下一页
+                pageNumber++;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
     //批量修改销售名称,由昵称-公司名,改为昵称
     public static void main(String[] args) {
         String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiJpbUFkbWluIiwiUGxhdGZvcm1JRCI6MTAsImV4cCI6MTc1OTkxMDgwNywiaWF0IjoxNzUyMTM0ODAyfQ.y0akpb-TnOBqJewPUD13tnUeR1iF41A3CcgaXXsjyKE";
@@ -214,7 +338,7 @@ public class OpenIMServiceImpl implements OpenIMService {
             pagination.put("showNumber", pageSize);
             requestPage.put("pagination", pagination);
 
-            String result = HttpRequest.post("https://web.im.cdwjyyh.com/api/user/get_all_users_uid")
+            String result = HttpRequest.post(IMConfig.URL+"/user/get_all_users_uid")
                     .header("operationID", String.valueOf(time))
                     .header("token", token)
                     .body(requestPage.toString())
@@ -239,7 +363,7 @@ public class OpenIMServiceImpl implements OpenIMService {
                 paramMap.put("userIDs", userIds);
 
                 String jsonBody = JSONUtil.toJsonStr(paramMap);
-                String result1 = HttpRequest.post("https://web.im.cdwjyyh.com/api/user/get_users_info")
+                String result1 = HttpRequest.post(IMConfig.URL+"/user/get_users_info")
                         .header("operationID", String.valueOf(time))
                         .header("token", token)
                         .body(jsonBody)
@@ -261,7 +385,7 @@ public class OpenIMServiceImpl implements OpenIMService {
                         bodyMap.put("userInfo", updateUserInfo);
 
                         String jsonBody1 = JSONUtil.toJsonStr(bodyMap);
-                        String result2 = HttpRequest.post("https://web.im.cdwjyyh.com/api/user/update_user_info_ex")
+                        String result2 = HttpRequest.post(IMConfig.URL+"/user/update_user_info_ex")
                                 .header("operationID", String.valueOf(System.currentTimeMillis()))
                                 .header("token", token)
                                 .body(jsonBody1)
@@ -301,7 +425,7 @@ public class OpenIMServiceImpl implements OpenIMService {
         paramMap.put("userIDs", userIDs);
 
         String jsonBody = JSONUtil.toJsonStr(paramMap);
-        String result1 = HttpRequest.post("https://web.im.cdwjyyh.com/api/user/get_users_info")
+        String result1 = HttpRequest.post(IMConfig.URL+"/user/get_users_info")
                 .header("operationID", String.valueOf(time))
                 .header("token", adminToken)
                 .body(jsonBody)
@@ -315,14 +439,14 @@ public class OpenIMServiceImpl implements OpenIMService {
         }
         for (UserInfo user : users) {
             updateUserInfo.setUserID(user.getUserID());
-            updateUserInfo.setNickname(StringUtils.isNotEmpty(companyUser.getImNickName())?companyUser.getImNickName():companyUser.getNickName());
+            updateUserInfo.setNickname(companyUser.getNickName());
             updateUserInfo.setFaceURL(Optional.ofNullable(user.getFaceURL()).orElse(""));
             updateUserInfo.setEx(Optional.ofNullable(user.getEx()).orElse(""));
 
             Map<String, Object> bodyMap = new HashMap<>();
             bodyMap.put("userInfo", updateUserInfo);
             String jsonBody1 = JSONUtil.toJsonStr(bodyMap);
-            String result2 = HttpRequest.post("https://web.im.cdwjyyh.com/api/user/update_user_info_ex")
+            String result2 = HttpRequest.post(IMConfig.URL+"/user/update_user_info_ex")
                     .header("operationID", String.valueOf(System.currentTimeMillis()))
                     .header("token", adminToken)
                     .body(jsonBody1)
@@ -358,16 +482,21 @@ public class OpenIMServiceImpl implements OpenIMService {
         log.info("进入发消息的方法");
         String adminToken = getAdminToken();
         JSONObject jsonObject = new JSONObject(openImMsgDTO);
+        String url = IMConfig.URL+"/msg/send_msg";
+        log.info("请求url: {}",url);
         log.info("发送消息的请求体:\n{}", jsonObject.toString());
         long time = new Date().getTime();
-        String result = HttpRequest.post("https://web.im.cdwjyyh.com/api/msg/send_msg")
+
+        log.info("请求header: token={},operationID={}",adminToken,time);
+        String result = HttpRequest.post(url)
                 .header("operationID", time + "")
                 .header("token",adminToken)
                 .body(jsonObject.toString())
                 .execute()
                 .body();
+
         OpenImResponseDTO responseDTO= JSONUtil.toBean(result,OpenImResponseDTO.class);
-        log.info("发送消息返回内容:\n{}", responseDTO);
+        log.info("发送消息返回内容:\n{}", result);
         return responseDTO;
     }
     @Override
@@ -375,7 +504,7 @@ public class OpenIMServiceImpl implements OpenIMService {
         ObjectMapper objectMapper = new ObjectMapper();
         //userId = 61l;
         objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略null字段
-        checkAndImportFriendByDianBo(companyUserId,userId.toString(),cropId);
+        checkAndImportFriendByDianBo(companyUserId,userId.toString(),cropId,true);
         OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
         OpenImMsgDTO.ImData imData = new OpenImMsgDTO.ImData();
         PayloadDTO payload = new PayloadDTO();
@@ -392,7 +521,8 @@ public class OpenIMServiceImpl implements OpenIMService {
 
         OpenImMsgDTO.OfflinePushInfo offlinePushInfo = new OpenImMsgDTO.OfflinePushInfo();
         offlinePushInfo.setDesc(title);
-        offlinePushInfo.setTitle("芳华未来");
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(companyUserId);
+        offlinePushInfo.setTitle(companyUser.getNickName());
         offlinePushInfo.setIOSBadgeCount(true);
         offlinePushInfo.setIOSPushSound("");
 
@@ -400,19 +530,77 @@ public class OpenIMServiceImpl implements OpenIMService {
         openImMsgDTO.setOfflinePushInfo(offlinePushInfo);
         openImMsgDTO.setContent(content);
 
-
-
         openImMsgDTO.setSendID("C"+companyUserId);
         openImMsgDTO.setRecvID("U"+userId);
         openImMsgDTO.setContentType(110);
         openImMsgDTO.setSessionType(1);
         // 输出格式化JSON日志
-        log.info("课程消息:\n{}", objectMapper.writeValueAsString(openImMsgDTO));
+        log.info("课程消息: {}", JSON.toJSONString(openImMsgDTO));
         OpenImResponseDTO openImResponseDTO = openIMSendMsg(openImMsgDTO);
         openImMsgDTO = null;
         content = null;
         return openImResponseDTO;
     }
+    @Override
+    public OpenImResponseDTO sendPackageUtil(String sendID, String recvID, Integer contentType, String payloadData,String packageName,String packageId){
+        try {
+            OpenImMsgDTO.OfflinePushInfo offlinePushInfo = new OpenImMsgDTO.OfflinePushInfo();
+            ObjectMapper objectMapper = new ObjectMapper();
+            OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+            if (sendID.startsWith("C")){
+                CompanyUser company = companyUserMapper.selectCompanyUserByUserId(Long.parseLong(sendID.replace("C", "")));
+                if (null!=company){
+                    offlinePushInfo.setTitle(company.getNickName());
+                    openImMsgDTO.setSenderFaceURL(company.getAvatar());
+                }
+            }
+            accountCheck(recvID.toString(),"1");
+            openImMsgDTO.setSendID(sendID);
+            openImMsgDTO.setRecvID(recvID);
+            openImMsgDTO.setContentType(contentType);
+            openImMsgDTO.setSenderPlatformID(5);
+            openImMsgDTO.setSessionType(1);
+
+            OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+            //content.setContent(ext);
+            PayloadDTO payload = new PayloadDTO();
+            payload.setData(payloadData);
+            PayloadDTO.Extension extension = new PayloadDTO.Extension();
+            //extension.setDiagnose(diagnose);
+            if (StringUtils.isNotEmpty(packageName)){
+                extension.setTitle(packageName);
+            }
+            if (StringUtils.isNotEmpty(packageId)){
+                extension.setPackageId(packageId);
+            }
+            payload.setExtension(extension);
+            OpenImMsgDTO.ImData imData = new OpenImMsgDTO.ImData();
+
+            imData.setPayload(payload);
+
+            String imJson = objectMapper.writeValueAsString(imData);
+            content.setData(imJson);
+            openImMsgDTO.setContent(content);
+            if (contentType == 101){
+                content.setContent(packageName);
+            }
+            //cn.hutool.json.JSONObject jsonObject = new cn.hutool.json.JSONObject(openImMsgDTO);
+
+            //openImMsgDTO.setEx(payload);
+
+            offlinePushInfo.setDesc(packageName);
+
+            offlinePushInfo.setIOSBadgeCount(true);
+            offlinePushInfo.setIOSPushSound("");
+            openImMsgDTO.setOfflinePushInfo(offlinePushInfo);
+            OpenImResponseDTO openImResponseDTO = openIMSendMsg(openImMsgDTO);
+            return openImResponseDTO;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
     @Override
     public OpenImResponseDTO sendUtil(String sendID, String recvID, Integer contentType, String payloadData, String diagnose,String title,String followId,String orderId,String ex) throws JsonProcessingException {
         try {
@@ -429,7 +617,7 @@ public class OpenIMServiceImpl implements OpenIMService {
             }else if (sendID.startsWith("C")){
                 CompanyUser company = companyUserMapper.selectCompanyUserByUserId(Long.parseLong(sendID.replace("C", "")));
                 if (null!=company&&StringUtils.isNotEmpty(company.getAvatar())){
-                    offlinePushInfo.setTitle(company.getImNickName());
+                    offlinePushInfo.setTitle(company.getNickName());
                     openImMsgDTO.setSenderFaceURL(company.getAvatar());
                 }
             }
@@ -495,7 +683,7 @@ public class OpenIMServiceImpl implements OpenIMService {
                 OpenImConversationDTO openImConversationDTO = new OpenImConversationDTO();
                 openImConversationDTO.setConversationID("si_"+sendID+"_"+recvID);
                 openImConversationDTO.setConversationType(1);
-                openImConversationDTO.setUserID(recvID);
+                openImConversationDTO.setUserID(recvID.startsWith("U")?recvID:sendID);
                 openImConversationDTO.setEx(ex);
                 log.info("更新到会话的ex的值:{}",ex);
                 openImEditConversationDTO.setConversation(openImConversationDTO);
@@ -614,7 +802,7 @@ public class OpenIMServiceImpl implements OpenIMService {
         while (retryCount < maxRetries) {
             try {
                 JSONObject jsonObject = new JSONObject(dto);
-                String body = HttpRequest.post("https://web.im.cdwjyyh.com/api/conversation/set_conversations")
+                String body = HttpRequest.post(IMConfig.URL+"/conversation/set_conversations")
                         .header("operationID", String.valueOf(System.currentTimeMillis()))
                         .header("token", adminToken)
                         .body(jsonObject.toString())
@@ -646,7 +834,7 @@ public class OpenIMServiceImpl implements OpenIMService {
         JSONObject jsonObject = new JSONObject();
         jsonObject.put("userID1",userID1);
         jsonObject.put("userID2",userID2);
-        String body = HttpRequest.post("https://web.im.cdwjyyh.com/api/friend/is_friend")
+        String body = HttpRequest.post(IMConfig.URL+"/friend/is_friend")
                 .header("operationID", String.valueOf(System.currentTimeMillis()))
                 .header("token", adminToken)
                 .body(jsonObject.toString())
@@ -683,7 +871,7 @@ public class OpenIMServiceImpl implements OpenIMService {
         JSONObject jsonObject = new JSONObject();
         jsonObject.put("ownerUserID",ownerUserID);
         jsonObject.put("friendUserIDs",newFriendIds);
-        String body = HttpRequest.post("https://web.im.cdwjyyh.com/api/friend/import_friend")
+        String body = HttpRequest.post(IMConfig.URL+"/friend/import_friend")
                 .header("operationID", String.valueOf(System.currentTimeMillis()))
                 .header("token", adminToken)
                 .body(jsonObject.toString())
@@ -720,7 +908,7 @@ public class OpenIMServiceImpl implements OpenIMService {
             requestBody = new JSONObject();
             userIds.add(userId);
             requestBody.put("checkUserIDs", userIds);
-            String body = HttpRequest.post("https://web.im.cdwjyyh.com/api/user/account_check")
+            String body = HttpRequest.post(IMConfig.URL+"/user/account_check")
                     .header("operationID", String.valueOf(System.currentTimeMillis()))
                     .header("token", adminToken)
                     .body(requestBody.toString())
@@ -738,31 +926,38 @@ public class OpenIMServiceImpl implements OpenIMService {
                     String s = "";
                     switch (type){
                         case "2":
-                            s = userId.replaceFirst("^C", "");
+                            s = userId.replaceFirst("^"+"C", "");
                             CompanyUser companyUser = companyUserMapper.selectCompanyUserByCompanyUserId(Long.parseLong(s));
                             if (null==companyUser){
-                                return R.error("用户不存在");
+//                                return R.error("用户不存在");
+                                log.error("异步执行IM注册/添加好友失败,失败原因:{},userId:{},type:{}", "销售用户不存在",userId, type);
+                                throw new ServiceException("用户不存在");
                             }
                             Company company = companyMapper.selectCompanyById(companyUser.getCompanyId());
                             map.put("userID",userId);
-                            map.put("nickname",companyUser.getImNickName());
+                            //map.put("nickname",companyUser.getImNickName());
+                            map.put("nickname",companyUser.getNickName());
                             map.put("faceURL",companyUser.getAvatar());
                             break;
                         case "1":
-                            s = userId.replaceFirst("^U", "");
+                            s = userId.replaceFirst("^"+"U", "");
                             FsUser fsUser = fsUserMapper.selectFsUserByUserId(Long.parseLong(s));
                             if (null==fsUser){
-                                return R.error("用户不存在");
+//                                return R.error("用户不存在");
+                                log.error("异步执行IM注册/添加好友失败,失败原因:{},userId:{},type:{}", "user用户不存在", userId, type);
+                                throw new ServiceException("用户不存在");
                             }
                             map.put("userID",userId);
                             map.put("nickname",StringUtils.isEmpty(fsUser.getNickName())?"微信用户":fsUser.getNickName());
                             map.put("faceURL",fsUser.getAvatar());
                             break;
                         case "3":
-                            s = userId.replaceFirst("^D", "");
+                            s = userId.replaceFirst("^"+"D", "");
                             FsDoctor fsDoctor = fsDoctorMapper.selectFsDoctorByDoctorId(Long.parseLong(s));
                             if (null==fsDoctor){
-                                return R.error("用户不存在");
+//                                return R.error("用户不存在");
+                                log.error("异步执行IM注册/添加好友失败,失败原因:{},userId:{},type:{}", "医生用户不存在", userId, type);
+                                throw new ServiceException("用户不存在");
                             }
                             map.put("userID",userId);
                             map.put("nickname",fsDoctor.getDoctorName());
@@ -774,12 +969,14 @@ public class OpenIMServiceImpl implements OpenIMService {
                     requestBody = new JSONObject();
                     userIds.add(userId);
                     requestBody.put("users", users);
-                    HttpRequest.post("https://web.im.cdwjyyh.com/api/user/user_register")
+                    HttpRequest.post(IMConfig.URL+"/user/user_register")
                             .header("operationID", String.valueOf(System.currentTimeMillis()))
                             .header("token", adminToken).body(requestBody.toString()).execute().body();
                 }
             } else {
-                return R.error("返回结果为空");
+//                return R.error("返回结果为空");
+                log.error("异步执行IM注册/添加好友失败,失败原因:{},json结果:{}", "返回结果为空", jsonObject);
+                throw new ServiceException("返回结果为空");
             }
            /* HashMap<String, String> tokenMap = new HashMap<>();
             tokenMap.put("platformID","1");
@@ -787,7 +984,7 @@ public class OpenIMServiceImpl implements OpenIMService {
             requestBody = new JSONObject();
             requestBody.put("platformID",5);
             requestBody.put("userID",userId);
-            String body1 = HttpRequest.post("https://web.im.cdwjyyh.com/api/auth/get_user_token")
+            String body1 = HttpRequest.post(IMConfig.URL+"/auth/get_user_token")
                     .header("operationID", String.valueOf(System.currentTimeMillis()))
                     .header("token", adminToken)
                     .body(requestBody.toString()).execute().body();
@@ -798,31 +995,35 @@ public class OpenIMServiceImpl implements OpenIMService {
                 String userToken = userData.getString("token");
                 return R.ok().put("token", userToken);
             }
-            return R.error("注册失败");
+//            return R.error("注册失败");
+            log.error("异步执行IM注册/添加好友失败,errCode:{}", errCode);
+            throw new ServiceException("注册失败");
         } else {
-            return R.error("获取管理员token失败");
+//            return R.error("获取管理员token失败");
+            log.error("异步执行IM注册/添加好友失败,原因:{}, adminToken:{}", "获取管理员token失败", adminToken);
+            throw new ServiceException("获取管理员token失败");
         }
     }
-    @Async
+//    @Async
     @Override
     public void checkAndImportFriend(Long companyUserId,String fsUserId) {
-        try {
+//        try {
             // 注册账号
-            accountCheck("C" + companyUserId, "2");
-            accountCheck("U"+fsUserId, "1");
+            accountCheck( "C" + companyUserId, "2");
+            accountCheck( "U" + fsUserId, "1");
 
             // 导入好友关系
             ArrayList<String> userIds = new ArrayList<>();
             userIds.add("U" + fsUserId);
             importFriend("C" + companyUserId, userIds);
-        } catch (Exception e) {
-            log.error("异步执行IM注册/添加好友失败:", e);
-        }
+//        } catch (Exception e) {
+//            log.error("异步执行IM注册/添加好友失败:", e);
+//        }
     }
 
     @Async
     @Override
-    public void checkAndImportFriendByDianBo(Long companyUserId,String fsUserId,String cropId) {
+    public void checkAndImportFriendByDianBo(Long companyUserId,String fsUserId,String cropId, boolean isUpdate) {
         try {
             // 注册账号
             accountCheck("C" + companyUserId, "2");
@@ -832,12 +1033,228 @@ public class OpenIMServiceImpl implements OpenIMService {
             ArrayList<String> userIds = new ArrayList<>();
             userIds.add("U" + fsUserId);
             importFriend("C" + companyUserId, userIds);
-            updateFriendByDianBo("C" + companyUserId, userIds,cropId);
+            if(isUpdate){
+                updateFriendByDianBo("C" + companyUserId, userIds,cropId);
+            }
         } catch (Exception e) {
             log.error("异步执行IM注册/添加好友失败:", e);
         }
     }
 
+
+    @Override
+    @Transactional
+    public OpenImResponseDTO openIMBatchSendMsg(OpenImBatchMsgDTO openImBatchMsgDTO) {
+        log.info("========================== 批量发送消息 ==========================");
+        String adminToken = getAdminToken();
+        JSONObject jsonObject = new JSONObject(openImBatchMsgDTO);
+        String url = IMConfig.URL+"/msg/batch_send_msg";
+        log.info("请求url: {},\n请求参数:{}", url, jsonObject);
+        long timestamp = System.currentTimeMillis();
+        log.info("请求header,operationID:{},token:{}", timestamp, adminToken);
+
+        String result = HttpRequest.post(url)
+                .header("operationID", timestamp + "")
+                .header("token",adminToken)
+                .body(jsonObject.toString())
+                .execute()
+                .body();
+        log.info("批量发送消息返回内容:\n{}", result);
+        OpenImResponseDTO responseDTO= JSONUtil.toBean(result, OpenImResponseDTO.class);
+        return responseDTO;
+    }
+
+    @Override
+    @Transactional
+    public OpenImResponseDTO batchSendCourse(BatchSendCourseDTO batchSendCourseDTO) throws JsonProcessingException {
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略null字段
+
+        //获取需要发送的人
+        List<String> userIds;
+        if(!batchSendCourseDTO.getUserIds().isEmpty()){
+            userIds = batchSendCourseDTO.getUserIds().stream().map(v -> "U" + v).collect(Collectors.toList());
+        } else {
+            userIds = this.getRecvIds(batchSendCourseDTO);
+        }
+
+        //注册和添加好友
+        for (String userId : userIds) {
+            String uId = userId.substring(1);
+            checkAndImportFriendByDianBo(batchSendCourseDTO.getCompanyUserId(), uId,null,false);
+        }
+
+        //组装消息数据
+        PayloadDTO.Extension extension = new PayloadDTO.Extension();
+        extension.setTitle(batchSendCourseDTO.getTitle());
+        extension.setAppRealLink(batchSendCourseDTO.getUrl());
+        extension.setSendTime(new Date());
+        FsUserCourse fsUserCourse = fsUserCourseMapper.selectFsUserCourseByCourseId(batchSendCourseDTO.getCourseId());
+        Long project = fsUserCourse != null ? fsUserCourse.getProject() : null;
+        extension.setCourseUrl(fsUserCourse != null ? fsUserCourse.getImgUrl() : null);
+
+        PayloadDTO payload = new PayloadDTO();
+        payload.setData("course");
+        payload.setExtension(extension);
+
+        OpenImBatchMsgDTO.ImData imData = new OpenImBatchMsgDTO.ImData();
+        OpenImBatchMsgDTO.Content content = new OpenImBatchMsgDTO.Content();
+        imData.setPayload(payload);
+        String imJson = objectMapper.writeValueAsString(imData);
+        content.setData(imJson);
+
+        OpenImBatchMsgDTO.OfflinePushInfo offlinePushInfo = new OpenImBatchMsgDTO.OfflinePushInfo();
+        offlinePushInfo.setDesc(batchSendCourseDTO.getTitle());
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(batchSendCourseDTO.getCompanyUserId());
+        offlinePushInfo.setTitle(companyUser != null ? companyUser.getNickName(): null);
+        offlinePushInfo.setIOSBadgeCount(true);
+        offlinePushInfo.setIOSPushSound("");
+
+        // 设置发送的消息
+        OpenImBatchMsgDTO openImBatchMsgDTO = new OpenImBatchMsgDTO();
+        openImBatchMsgDTO.setSendID("C" + batchSendCourseDTO.getCompanyUserId());
+        openImBatchMsgDTO.setRecvIDs(userIds);
+        openImBatchMsgDTO.setContent(content);
+        openImBatchMsgDTO.setContentType(110);
+        openImBatchMsgDTO.setSessionType(1);
+        openImBatchMsgDTO.setIsOnlineOnly(false);
+        openImBatchMsgDTO.setNotOfflinePush(false);
+        long planSendTimeStamp;
+        if(batchSendCourseDTO.getSendTime() != null && batchSendCourseDTO.getSendTime().compareTo(new Date()) > 0){
+            planSendTimeStamp = batchSendCourseDTO.getSendTime().getTime();
+        } else {
+            planSendTimeStamp = System.currentTimeMillis();
+        }
+        openImBatchMsgDTO.setSendTime(planSendTimeStamp);
+        openImBatchMsgDTO.setOfflinePushInfo(offlinePushInfo);
+        openImBatchMsgDTO.setIsSendAll(false);
+
+        OpenImResponseDTO openImResponseDTO = new OpenImResponseDTO();
+        // 保存发送时间到缓存中
+        if(batchSendCourseDTO.getSendTime() != null && batchSendCourseDTO.getSendTime().compareTo(new Date()) > 0) {
+            // 定时发送
+            for (String userId : userIds) {
+                String redisKey = "openIm:batchSendMsg";
+                Map<String, Object> redisMap = new HashMap<>();
+                BatchSendCourseAllDTO batchSendCourseAllDTO = new BatchSendCourseAllDTO();
+                batchSendCourseAllDTO.setBatchSendCourseDTO(batchSendCourseDTO).setOpenImBatchMsgDTO(openImBatchMsgDTO).setProject(project);
+                redisMap.put(batchSendCourseDTO.getCourseId()+":"+batchSendCourseDTO.getVideoId()+":"+userId+":"+batchSendCourseDTO.getSendTime().getTime()
+                        , batchSendCourseAllDTO);
+                redisCache.setCacheMap(redisKey, redisMap);
+            }
+            openImResponseDTO.setErrCode(0);
+            openImResponseDTO.setErrMsg("计划发送创建成功,待消息发送");
+        } else {
+            // 实时发送
+            openImResponseDTO = this.batchSendMsgTask(batchSendCourseDTO, openImBatchMsgDTO, project, 2);
+            openImResponseDTO.setErrMsg("实时发送成功");
+        }
+        return openImResponseDTO;
+    }
+
+    @Override
+    @Transactional
+    public OpenImResponseDTO batchSendMsgTask(BatchSendCourseDTO batchSendCourseDTO, OpenImBatchMsgDTO openImBatchMsgDTO, Long project, Integer sendType) {
+
+         log.info("批量发送课程消息: \n{}", JSON.toJSONString(openImBatchMsgDTO));
+        OpenImResponseDTO openImResponseDTO = openIMBatchSendMsg(openImBatchMsgDTO);
+//        openImBatchMsgDTO = null;
+//        content = null;
+
+        //获取发送消息结果,成功后再生成看课记录和发送记录
+        if(openImResponseDTO.getErrCode() == 0 && openImResponseDTO.getData() != null){
+            Object data = openImResponseDTO.getData();
+            OpenImBatchResponseDataDTO openImBatchResponseDataDTO = JSON.parseObject(JSON.toJSONString(data), OpenImBatchResponseDataDTO.class);
+            List<OpenImBatchResponseDataDTO.Results> results = openImBatchResponseDataDTO.getResults();
+
+            // 生成发送记录
+            this.batchInsertSendLogs(openImBatchMsgDTO, openImResponseDTO, openImBatchResponseDataDTO, sendType);
+
+            // 生成看课记录
+            this.batchInsertWatchLogs(batchSendCourseDTO, results, project);
+
+        } else {
+            // 生成发送记录
+            this.batchInsertSendLogs(openImBatchMsgDTO, openImResponseDTO, null, sendType);
+            log.error("发送消息失败,结果:{}", openImResponseDTO);
+            throw new ServiceException("发送消息失败");
+        }
+
+        return openImResponseDTO;
+    }
+
+    private List<String> getRecvIds(BatchSendCourseDTO batchSendCourseDTO) {
+        Map<String,Object> param = new HashMap<>();
+        param.put("tagIds", batchSendCourseDTO.getTagIds());
+        param.put("companyUserId", batchSendCourseDTO.getCompanyUserId());
+        List<FsUserCompanyUser> fsUserCompanyUsers = fsUserCompanyUserMapper.selectFsUserCompanyUserByIds(param);
+        if(fsUserCompanyUsers.isEmpty()){
+            log.error("没有消息接收人,参数:{}", batchSendCourseDTO);
+            throw new ServiceException("没有消息接收人");
+        }
+        return fsUserCompanyUsers.stream().map(v -> "U" + v.getUserId()).collect(Collectors.toList());
+    }
+
+    private void batchInsertSendLogs(OpenImBatchMsgDTO openImBatchMsgDTO, OpenImResponseDTO openImResponseDTO, OpenImBatchResponseDataDTO openImBatchResponseDataDTO, Integer sendType) {
+        List<ImSendLog> list = new LinkedList<>();
+        if(openImResponseDTO.getErrCode() != 0){
+            ImSendLog imSendLog = createImsendLog(openImBatchMsgDTO, openImBatchMsgDTO.getSendTime());
+            imSendLog.setStatus(1);
+            imSendLog.setResultMessage(JSON.toJSONString(openImResponseDTO.getErrMsg()));
+            imSendLog.setExceptionInfo(JSON.toJSONString(openImResponseDTO.getErrDlt()));
+            list.add(imSendLog);
+        } else {
+            if(openImBatchResponseDataDTO.getFailedUserIDs() != null) {
+                for (String failedUserID : openImBatchResponseDataDTO.getFailedUserIDs()) {
+                    ImSendLog imSendLog = createImsendLog(openImBatchMsgDTO, openImBatchMsgDTO.getSendTime());
+                    imSendLog.setRecvId(failedUserID);
+                    imSendLog.setStatus(1);
+                    imSendLog.setResultMessage(JSON.toJSONString(openImBatchResponseDataDTO));
+                    list.add(imSendLog);
+                }
+            }
+            for (OpenImBatchResponseDataDTO.Results result : openImBatchResponseDataDTO.getResults()) {
+                ImSendLog imSendLog = createImsendLog(openImBatchMsgDTO, openImBatchMsgDTO.getSendTime());
+                imSendLog.setRecvId(result.getRecvID());
+                imSendLog.setActualSendTime(new Date(result.getSendTime()));
+                imSendLog.setSendType(sendType);
+                imSendLog.setStatus(0);
+                imSendLog.setResultMessage(JSON.toJSONString(openImBatchResponseDataDTO));
+                list.add(imSendLog);
+            }
+        }
+
+        imSendLogMapper.insertImSendLogBatch(list);
+    }
+
+    private static ImSendLog createImsendLog(OpenImBatchMsgDTO openImBatchMsgDTO, long planSendTimeStamp) {
+        ImSendLog imSendLog = new ImSendLog();
+        imSendLog.setSendId(openImBatchMsgDTO.getSendID());
+        imSendLog.setSendTitle(openImBatchMsgDTO.getOfflinePushInfo() != null ? openImBatchMsgDTO.getOfflinePushInfo().getTitle() : null);
+        imSendLog.setPlanSendTime(new Date(planSendTimeStamp));
+        imSendLog.setParamJson(JSON.toJSONString(openImBatchMsgDTO));
+
+        return imSendLog;
+    }
+
+    public void batchInsertWatchLogs(BatchSendCourseDTO batchSendCourseDTO, List<OpenImBatchResponseDataDTO.Results> results, Long project) {
+        List<FsCourseWatchLog> watchLogsInsertList = new LinkedList<>();
+        for (OpenImBatchResponseDataDTO.Results result : results) {
+            FsCourseWatchLog fsCourseWatchLog = new FsCourseWatchLog();
+            BeanUtils.copyProperties(batchSendCourseDTO, fsCourseWatchLog);
+            String userId = result.getRecvID().replaceFirst("^"+"U", "");
+            fsCourseWatchLog.setUserId(Long.parseLong(userId));
+            fsCourseWatchLog.setSendType(1);
+            fsCourseWatchLog.setDuration(0L);
+            fsCourseWatchLog.setCreateTime(new Date());
+            fsCourseWatchLog.setLogType(3);
+            fsCourseWatchLog.setProject(project);
+            watchLogsInsertList.add(fsCourseWatchLog);
+        }
+        courseWatchLogMapper.insertFsCourseWatchLogBatch(watchLogsInsertList);
+        log.info("批量插入FsCourseWatchLog表完成,共插入 {} 条记录", watchLogsInsertList.size());
+    }
+
     /**
      * 修改好友信息
      * @param ownerUserID
@@ -861,7 +1278,7 @@ public class OpenIMServiceImpl implements OpenIMService {
                 }
             }
         }
-        List<String> remark = qwExternalContactMapper.selectRemarkByCompanyUserAndFsUser(friendUserIDs.get(0).replaceFirst("^U", ""), ownerUserID.replaceFirst("^C", ""),cropId);
+        List<String> remark = qwExternalContactMapper.selectRemarkByCompanyUserAndFsUser(friendUserIDs.get(0).replaceFirst("^scrmU", ""), ownerUserID.replaceFirst("^scrmC", ""),cropId);
         if (CollectionUtils.isEmpty(remark)||remark.size()<=0){
             return null;
         }
@@ -870,7 +1287,7 @@ public class OpenIMServiceImpl implements OpenIMService {
         jsonObject.put("ownerUserID",ownerUserID);
         jsonObject.put("friendUserIDs",friendUserIDs);
         jsonObject.put("remark",remark.get(0));
-        String body = HttpRequest.post("https://web.im.cdwjyyh.com/api/friend/update_friends")
+        String body = HttpRequest.post(IMConfig.URL+"/friend/update_friends")
                 .header("operationID", String.valueOf(System.currentTimeMillis()))
                 .header("token", adminToken)
                 .body(jsonObject.toString())

+ 6 - 5
fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

@@ -416,11 +416,6 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
 
     List<QwExternalContact> selectExternalByFsUserIds(@Param("userIds")List<Long> userIds);
 
-    List<QwExternalContactComplaintVO> selectFsUserCourseComplaintRecordByUserId(Long userId);
-
-    @Select("select content from fastgpt_chat_artificial_words")
-    List<FastgptChatArtificialWords> selectChatGptChatArtificialWords();
-
     @Select({"<script> " +
             "SELECT qe.remark FROM qw_external_contact qe " +
             "where qe.company_user_id = #{companyUserId} and qe.fs_user_id = #{userId}" +
@@ -428,6 +423,12 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
             "</script>"})
     List<String> selectRemarkByCompanyUserAndFsUser(@Param("userId")String userId,@Param("companyUserId") String companyUserId,@Param("cropId") String cropId);
 
+
+    List<QwExternalContactComplaintVO> selectFsUserCourseComplaintRecordByUserId(Long userId);
+
+    @Select("select content from fastgpt_chat_artificial_words")
+    List<FastgptChatArtificialWords> selectChatGptChatArtificialWords();
+
     List<QwExternalContact> selectQwExternalContactByFsUserId(@Param("userId") Long userId);
 
     int insertQwUserDelLossLog(@Param("param") QwUserDelLossLog qwUserDelLossLog);

+ 17 - 0
fs-service/src/main/java/com/fs/qw/service/impl/AsyncSopTestService.java

@@ -1,6 +1,7 @@
 package com.fs.qw.service.impl;
 
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.utils.PubFun;
 import com.fs.course.domain.FsCourseSopAppLink;
 import com.fs.course.mapper.FsCourseSopAppLinkMapper;
@@ -501,6 +502,22 @@ public class AsyncSopTestService {
 //        push2Service.pushSopAppLinkMsgByExternal(externalId,linkTile,linkDescribe,link);
     }
 
+    /**
+     * 异步录入 发送有app的客户 之 正常sop版
+     */
+    @Async("scheduledExecutorService")
+    public void  asyncSendMsgBySopAppLinkNormalIM(List<QwSopTempSetting.Content.Setting> setting,String cropId,Long companyUserId,Long fsUserId){
+
+        setting.forEach(item->{
+            try {
+                push2Service.pushSopAppLinkMsgByExternalIM(cropId,item.getLinkTitle(),item.getLinkDescribe(),item.getLinkImageUrl(),item.getAppLinkUrl(),companyUserId,fsUserId);
+            } catch (JsonProcessingException e) {
+                e.printStackTrace();
+            }
+        });
+
+    }
+
     /**
      * 异步录入 发送有app的客户 之 正常sop版
      */

+ 2 - 1
fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java

@@ -1095,7 +1095,8 @@ public class QwSopLogsServiceImpl extends ServiceImpl<QwSopLogsMapper, QwSopLogs
 
                                 //有app的异步推送消息
                                 if (!setting.isEmpty()) {
-                                    asyncSopTestService.asyncSendMsgBySopAppLinkNormal(setting, param.getExternalId());
+                                    //asyncSopTestService.asyncSendMsgBySopAppLinkNormal(setting, Long.parseLong(log.getExternalId()));
+                                    asyncSopTestService.asyncSendMsgBySopAppLinkNormalIM(setting, param.getCorpId(),qwUser.getCompanyUserId(),log.getFsUserId());
                                 }
 
                                 if (log.getExpiryTime() == null) {

+ 4 - 0
fs-service/src/main/java/com/fs/store/param/h5/FsUserPageListParam.java

@@ -84,6 +84,10 @@ public class FsUserPageListParam implements Serializable {
 
     @ApiModelProperty(value = "状态 0小黑屋 1正常 2拉黑")
     private Integer status;
+    /**
+     * 电话是否隐藏中间部分
+     */
+    private Boolean isHidePhoneMiddle = Boolean.TRUE;
 
 
 }

+ 2 - 0
fs-service/src/main/resources/application-druid-fby.yml

@@ -166,3 +166,5 @@ token:
 openIM:
     secret: openIM123
     userID: imAdmin
+    url: https://web.im.fbylive.com/api
+#    prefix: fby

+ 4 - 2
fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml

@@ -316,7 +316,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         reward_type,
         sop_id,
         camp_period_time,
-        project
+        project,
+        period_id
         )
         VALUES
         <foreach collection="watchLogs" item="log" separator=",">
@@ -336,7 +337,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{log.rewardType},
             #{log.sopId},
             #{log.campPeriodTime},
-            #{log.project}
+            #{log.project},
+            #{log.periodId}
             )
         </foreach>
         ON DUPLICATE KEY UPDATE

+ 22 - 0
fs-service/src/main/resources/mapper/course/FsUserCompanyUserMapper.xml

@@ -171,5 +171,27 @@
         group by fucu.project_id
     </select>
 
+    <select id="selectFsUserCompanyUserByIds" resultType="FsUserCompanyUser">
+        SELECT
+            *
+        FROM
+            fs_user_company_user
+        <where>
+            <if test="param.tagIds != null and param.tagIds.size() > 0">
+            id IN (
+            SELECT DISTINCT
+            up.user_company_user_id
+            FROM fs_user_project_tag up
+            WHERE up.tag_id IN
+            <foreach collection="param.tagIds" item="tagId" separator="," open="(" close=")">
+                #{tagId}
+            </foreach>
+            )
+            </if>
+            <if test="param.companyUserId != null">
+                and fs_user_company_user.company_user_id = #{param.companyUserId}
+            </if>
+        </where>
+    </select>
 
 </mapper>

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

@@ -13,11 +13,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="createTime"    column="create_time"    />
         <result property="orderId"    column="order_id"    />
         <result property="msgContentType"    column="msg_content_type"    />
+        <result property="companyUserAccount"    column="company_user_account"    />
         <result property="msgKey"    column="msg_key"    />
     </resultMap>
 
     <sql id="selectFsInquiryOrderMsgVo">
-        select msg_id, msg_type, from_account, to_account, content, create_time, order_id,msg_content_type,msg_key from fs_inquiry_order_msg
+        select msg_id, msg_type, from_account, to_account, content, create_time, order_id,msg_content_type,msg_key,company_user_account from fs_inquiry_order_msg
     </sql>
 
     <select id="selectFsInquiryOrderMsgList" parameterType="FsInquiryOrderMsg" resultMap="FsInquiryOrderMsgResult">
@@ -28,6 +29,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="toAccount != null  and toAccount != ''"> and to_account = #{toAccount}</if>
             <if test="content != null  and content != ''"> and content = #{content}</if>
             <if test="orderId != null "> and order_id = #{orderId}</if>
+            <if test="companyUserAccount != null "> and company_user_account = #{companyUserAccount}</if>
         </where>
     </select>
 
@@ -47,6 +49,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="orderId != null">order_id,</if>
             <if test="msgContentType != null">msg_content_type,</if>
             <if test="msgKey != null">msg_key,</if>
+            <if test="companyUserAccount != null">company_user_account,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="msgType != null">#{msgType},</if>
@@ -57,6 +60,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="orderId != null">#{orderId},</if>
             <if test="msgContentType != null">#{msgContentType},</if>
             <if test="msgKey != null">#{msgKey},</if>
+            <if test="companyUserAccount != null">#{companyUserAccount},</if>
          </trim>
     </insert>
 
@@ -71,6 +75,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="orderId != null">order_id = #{orderId},</if>
             <if test="msgContentType != null">msg_content_type = #{msgContentType},</if>
             <if test="msgKey != null">msg_key = #{msgKey},</if>
+            <if test="companyUserAccount != null">company_user_account = #{companyUserAccount},</if>
         </trim>
         where msg_id = #{msgId}
     </update>

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

@@ -227,7 +227,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         fs_user_company_user.is_repeat_fans,
         fs_user_company_user.project_id,
         fs_user_company_user.`status`,
-        fs_user_company_user.remark,
+        ifnull(fs_user_company_user.remark, fs_user.remark) as remark,
         fs_user_company_user.id as userCompanyUserId,
         fs_user_company_user.create_time
         FROM

+ 138 - 0
fs-service/src/main/resources/mapper/im/ImSendLogMapper.xml

@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.im.mapper.ImSendLogMapper">
+
+    <resultMap type="ImSendLog" id="ImSendLogResult">
+        <result property="logId"    column="log_id"    />
+        <result property="sendId"    column="send_id"    />
+        <result property="recvId"    column="recv_id"    />
+        <result property="sendTitle"    column="send_title"    />
+        <result property="planSendTime"    column="plan_send_time"    />
+        <result property="actualSendTime"    column="actual_send_time"    />
+        <result property="sendType"    column="send_type"    />
+        <result property="paramJson"    column="param_json"    />
+        <result property="status"    column="status"    />
+        <result property="resultMessage"    column="result_message"    />
+        <result property="exceptionInfo"    column="exception_info"    />
+        <result property="createTime"    column="create_time"    />
+    </resultMap>
+
+    <sql id="selectImSendLogVo">
+        select log_id, send_id, recv_id, send_title, plan_send_time, actual_send_time, send_type, param_json, status, result_message, exception_info, create_time from im_send_log
+    </sql>
+
+    <select id="selectImSendLogList" parameterType="ImSendLog" resultMap="ImSendLogResult">
+        <include refid="selectImSendLogVo"/>
+        <where>
+            <if test="sendId != null  and sendId != ''"> and send_id = #{sendId}</if>
+            <if test="recvId != null  and recvId != ''"> and recv_id = #{recvId}</if>
+            <if test="sendTitle != null  and sendTitle != ''"> and send_title = #{sendTitle}</if>
+            <if test="planSendTime != null "> and plan_send_time = #{planSendTime}</if>
+            <if test="actualSendTime != null "> and actual_send_time = #{actualSendTime}</if>
+            <if test="sendType != null "> and send_type = #{sendType}</if>
+            <if test="paramJson != null  and paramJson != ''"> and param_json = #{paramJson}</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="resultMessage != null  and resultMessage != ''"> and result_message = #{resultMessage}</if>
+            <if test="exceptionInfo != null  and exceptionInfo != ''"> and exception_info = #{exceptionInfo}</if>
+        </where>
+    </select>
+
+    <select id="selectImSendLogByLogId" parameterType="Long" resultMap="ImSendLogResult">
+        <include refid="selectImSendLogVo"/>
+        where log_id = #{logId}
+    </select>
+
+    <insert id="insertImSendLog" parameterType="ImSendLog" useGeneratedKeys="true" keyProperty="logId">
+        insert into im_send_log
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="sendId != null">send_id,</if>
+            <if test="recvId != null">recv_id,</if>
+            <if test="sendTitle != null">send_title,</if>
+            <if test="planSendTime != null">plan_send_time,</if>
+            <if test="actualSendTime != null">actual_send_time,</if>
+            <if test="sendType != null">send_type,</if>
+            <if test="paramJson != null">param_json,</if>
+            <if test="status != null">status,</if>
+            <if test="resultMessage != null">result_message,</if>
+            <if test="exceptionInfo != null">exception_info,</if>
+            <if test="createTime != null">create_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="sendId != null">#{sendId},</if>
+            <if test="recvId != null">#{recvId},</if>
+            <if test="sendTitle != null">#{sendTitle},</if>
+            <if test="planSendTime != null">#{planSendTime},</if>
+            <if test="actualSendTime != null">#{actualSendTime},</if>
+            <if test="sendType != null">#{sendType},</if>
+            <if test="paramJson != null">#{paramJson},</if>
+            <if test="status != null">#{status},</if>
+            <if test="resultMessage != null">#{resultMessage},</if>
+            <if test="exceptionInfo != null">#{exceptionInfo},</if>
+            <if test="createTime != null">#{createTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateImSendLog" parameterType="ImSendLog">
+        update im_send_log
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="sendId != null">send_id = #{sendId},</if>
+            <if test="recvId != null">recv_id = #{recvId},</if>
+            <if test="sendTitle != null">send_title = #{sendTitle},</if>
+            <if test="planSendTime != null">plan_send_time = #{planSendTime},</if>
+            <if test="actualSendTime != null">actual_send_time = #{actualSendTime},</if>
+            <if test="sendType != null">send_type = #{sendType},</if>
+            <if test="paramJson != null">param_json = #{paramJson},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="resultMessage != null">result_message = #{resultMessage},</if>
+            <if test="exceptionInfo != null">exception_info = #{exceptionInfo},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+        </trim>
+        where log_id = #{logId}
+    </update>
+
+    <delete id="deleteImSendLogByLogId" parameterType="Long">
+        delete from im_send_log where log_id = #{logId}
+    </delete>
+
+    <delete id="deleteImSendLogByLogIds" parameterType="String">
+        delete from im_send_log where log_id in
+        <foreach item="logId" collection="array" open="(" separator="," close=")">
+            #{logId}
+        </foreach>
+    </delete>
+
+    <insert id="insertImSendLogBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="logId">
+        insert into im_send_log (
+            send_id,
+            recv_id,
+            send_title,
+            plan_send_time,
+            actual_send_time,
+            send_type,
+            param_json,
+            status,
+            result_message,
+            exception_info,
+            create_time
+    )
+        VALUES
+        <foreach collection="sendLogs" item="log" separator=",">
+            (
+            #{log.sendId},
+            #{log.recvId},
+            #{log.sendTitle},
+            #{log.planSendTime},
+            #{log.actualSendTime},
+            #{log.sendType},
+            #{log.paramJson},
+            #{log.status},
+            #{log.resultMessage},
+            #{log.exceptionInfo},
+            #{log.createTime}
+            )
+        </foreach>
+    </insert>
+
+</mapper>

+ 2 - 2
fs-user-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -5,6 +5,7 @@ import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.baidubce.appbuilder.model.dataset.DocumentListResponse;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.ai.service.IBaiduAIService;
 import com.fs.ai.vo.BaiduAIMsgResultVO;
 import com.fs.app.param.SignParam;
@@ -350,8 +351,7 @@ public class CommonController {
 
 	@GetMapping(value = "/testInquiryOrder")
 	@ApiOperation("测试开药结束")
-	public R testInquiryOrder()
-	{
+	public R testInquiryOrder() throws JsonProcessingException {
 		FsInquiryOrderFinishParam param=new FsInquiryOrderFinishParam();
 		param.setOrderId(740653l);
 		param.setDoctorId(113l);

+ 36 - 25
fs-user-app/src/main/java/com/fs/app/controller/FollowController.java

@@ -2,6 +2,8 @@ package com.fs.app.controller;
 
 
 import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.app.annotation.Login;
 import com.fs.app.controller.AppBaseController;
 import com.fs.common.core.domain.R;
@@ -17,6 +19,7 @@ import com.fs.his.vo.FsDiseaseListUVO;
 import com.fs.his.vo.FsFollowListUVO;
 import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -48,6 +51,8 @@ public class FollowController extends AppBaseController {
     private IFsUserService userService;
     @Autowired
     IImService imService;
+    @Autowired
+    private OpenIMService openIMService;
     Logger logger= LoggerFactory.getLogger(getClass());
 
     @Login
@@ -77,8 +82,7 @@ public class FollowController extends AppBaseController {
     @Login
     @ApiOperation("提交随访")
     @PostMapping("/doFollow")
-    public R doFollow(HttpServletRequest request, @Validated @RequestBody FsFollow param)
-    {
+    public R doFollow(HttpServletRequest request, @Validated @RequestBody FsFollow param) throws JsonProcessingException {
         FsFollow follow=followService.selectFsFollowByFollowId(param.getFollowId());
         if(follow.getWriteStatus()!=0){
             return R.error("非法操作");
@@ -86,36 +90,24 @@ public class FollowController extends AppBaseController {
         param.setWriteStatus(1);
         param.setUpdateTime(new Date());
         if(followService.updateFsFollow(param)>0){
-            MsgDTO msgDTO=new MsgDTO();
-            msgDTO.setTo_Account("D-"+follow.getDoctorId());
-            msgDTO.setFrom_Account("U-"+follow.getUserId());
             MsgCustomDTO customDTO1=new MsgCustomDTO();
             customDTO1.setType("follow");
             customDTO1.setImType(2);
             customDTO1.setOrderId(follow.getStoreOrderId().toString());
             customDTO1.setFollowId(follow.getFollowId().toString());
-            msgDTO.setCloudCustomData(JSONUtil.toJsonStr(customDTO1));
-
-            List<MsgDataDTO> msgs=new ArrayList<>();
-            MsgDataDTO msg=new MsgDataDTO();
-            String ext=JSONUtil.toJsonStr(follow);
+            ObjectMapper objectMapper = new ObjectMapper();
             String followId=follow.getFollowId().toString();
-            msg.setMsgContent(new MsgDataFormatDTO("follow",ext,followId));
-            msg.setMsgType("TIMCustomElem");//TIMCustomElem
-            msgs.add(msg);
-            msgDTO.setMsgBody(msgs);
-            MsgResponseDTO msgResponseDTO = imService.sendMsg(msgDTO);
-            logger.info("用户发送随访:"+msgResponseDTO);
+            String ex = objectMapper.writeValueAsString(customDTO1);
+            OpenImResponseDTO openImResponseDTO = openIMService.sendUtilUserToDoctor("U" + follow.getUserId(), "D" + follow.getDoctorId(), 110, "follow", "", "", followId, "", ex);
+
+            //MsgResponseDTO msgResponseDTO = imService.sendMsg(msgDTO);
+            //logger.info("用户发送随访:"+msgResponseDTO);
             userService.followAddIntegral(follow.getUserId(),follow.getFollowId());
 
             //发送给用户
-            MsgDTO msgDTO2=new MsgDTO();
-            MsgCustomDTO customDTO2=new MsgCustomDTO();
-            customDTO2.setType("startDrugReport");
-            customDTO2.setImType(2);
-            customDTO2.setOrderId(follow.getStoreOrderId().toString());
-            customDTO2.setFollowId(follow.getFollowId().toString());
-            msgDTO2.setCloudCustomData(JSONUtil.toJsonStr(customDTO2));
+            //MsgDTO msgDTO2=new MsgDTO();
+
+            /*msgDTO2.setCloudCustomData(JSONUtil.toJsonStr(customDTO2));
             msgDTO2.setFrom_Account("D-"+follow.getDoctorId());
             msgDTO2.setTo_Account("U-"+follow.getUserId());
             List<MsgDataDTO> msgs2=new ArrayList<>();
@@ -123,8 +115,27 @@ public class FollowController extends AppBaseController {
             msg2.setMsgType("TIMTextElem");
             msg2.setMsgContent(new MsgDataFormatDTO("您好,我是您的专属药师,您有任何问题都可以先留言给我,请耐心等待一下,用户咨询量较多,我会按咨询顺序一一回复哒~"));
             msgs2.add(msg2);
-            msgDTO2.setMsgBody(msgs2);
-            imService.sendMsg(msgDTO2);
+            msgDTO2.setMsgBody(msgs2);*/
+            //imService.sendMsg(msgDTO2);
+            /*OpenImMsgDTO openImMsgDTO1 = new OpenImMsgDTO();
+            openImMsgDTO1.setSendID("D"+follow.getDoctorId().toString());
+            openImMsgDTO1.setRecvID("U"+follow.getUserId().toString());
+            openImMsgDTO1.setContentType(101);
+            openImMsgDTO1.setSenderPlatformID(5);
+            openImMsgDTO1.setSessionType(1);
+            openImMsgDTO1.setEx(ex1);
+            OpenImMsgDTO.Content content1 = new OpenImMsgDTO.Content();
+            openImMsgDTO1.setContent(content1);*/
+            //content1.setContent("您好,我是您的专属药师,您有任何问题都可以先留言给我,请耐心等待一下,用户咨询量较多,我会按咨询顺序一一回复哒~");
+            MsgCustomDTO customDTO2=new MsgCustomDTO();
+            customDTO2.setType("startDrugReport");
+            customDTO2.setImType(2);
+            customDTO2.setOrderId(follow.getStoreOrderId().toString());
+            customDTO2.setFollowId(follow.getFollowId().toString());
+            String ex1 = objectMapper.writeValueAsString(customDTO2);
+            openIMService.sendUtil("D"+follow.getDoctorId(),"U"+follow.getUserId(),101,
+                    "","","您好,我是您的专属药师,您有任何问题都可以先留言给我,请耐心等待一下,用户咨询量较多,我会按咨询顺序一一回复哒~","","",ex1);
+            //openIMService.openIMSendMsg(openImMsgDTO1);
             return R.ok("操作成功");
 
         }

+ 10 - 0
fs-user-app/src/main/java/com/fs/app/controller/WxUserController.java

@@ -23,6 +23,7 @@ import com.fs.his.mapper.FsUserLoginLogMapper;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.service.IFsUserWxService;
 import com.fs.his.utils.ConfigUtil;
+import com.fs.im.service.OpenIMService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import io.netty.util.internal.StringUtil;
@@ -80,6 +81,8 @@ public class WxUserController extends AppBaseController{
     @Autowired
     private IFsUserWxService userWxService;
 
+    @Autowired
+    private OpenIMService openIMService;
 
 
     /**
@@ -387,4 +390,11 @@ public class WxUserController extends AppBaseController{
         return R.ok("授权成功");
     }
 
+    @Login
+    @ApiOperation("注册IM账号和添加好友关系")
+    @PostMapping("/bindingImFriend")
+    public void createAndBindingImFriend(@RequestParam Long companyUserId) {
+        openIMService.checkAndImportFriend(companyUserId, getUserId());
+    }
+
 }

+ 25 - 2
fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

@@ -2,12 +2,15 @@ package com.fs.app.controller.course;
 
 
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.app.annotation.UserOperationLog;
 import com.fs.app.controller.AppBaseController;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.app.annotation.Login;
+import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.course.param.FsCourseLinkCreateParam;
 import com.fs.course.param.FsCourseQuestionAnswerUParam;
 import com.fs.course.param.FsCourseSendRewardUParam;
 import com.fs.course.param.FsUserCourseVideoFinishUParam;
@@ -19,12 +22,13 @@ import com.fs.course.vo.FsUserCourseVideoH5VO;
 import com.fs.course.vo.newfs.FsUserCourseVideoLinkDetailsVO;
 import com.fs.his.domain.FsUser;
 import com.fs.his.enums.FsUserOperationEnum;
-import com.fs.his.service.IFsUserService;
-import com.fs.system.service.ISysConfigService;
+import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.service.OpenIMService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -49,6 +53,12 @@ public class CourseFsUserController extends AppBaseController {
     @Autowired
     private IFsCourseQuestionBankService questionBankService;
 
+    @Autowired
+    private OpenIMService openIMService;
+
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
+
 
 
     @Login
@@ -130,5 +140,18 @@ public class CourseFsUserController extends AppBaseController {
         logger.error("zyp \n【h5看课中途报错】:{}",msg);
     }
 
+    @ApiOperation("会员批量发送课程消息")
+    @PostMapping("/batchSendCourse")
+    public OpenImResponseDTO batchSendCourse(@RequestBody BatchSendCourseDTO batchSendCourseDTO) throws JsonProcessingException {
+        // 生成看课短链
+        FsCourseLinkCreateParam fsCourseLinkCreateParam = new FsCourseLinkCreateParam();
+        BeanUtils.copyProperties(batchSendCourseDTO, fsCourseLinkCreateParam);
+        R courseSortLink = fsUserCourseService.createCourseSortLink(fsCourseLinkCreateParam);
+        String url = courseSortLink.get("url").toString();
+        batchSendCourseDTO.setUrl(url);
+
+        return openIMService.batchSendCourse(batchSendCourseDTO);
+    }
+
 
 }