zx 2 months ago
parent
commit
25de67873c
25 changed files with 1728 additions and 0 deletions
  1. 147 0
      fs-ad-api/src/main/resources/application-druid-myhk.yml
  2. 100 0
      fs-common/src/main/java/com/fs/common/core/domain/ResponseResult.java
  3. 20 0
      fs-common/src/main/java/com/fs/common/enums/BizResponseEnum.java
  4. 148 0
      fs-qwhook-msg/src/main/resources/application-druid-jz.yml
  5. 130 0
      fs-qwhook-sop/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  6. 148 0
      fs-qwhook-sop/src/main/resources/application-druid-jz.yml
  7. 130 0
      fs-qwhook/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  8. 103 0
      fs-service/src/main/java/com/fs/ad/controller/AdAccountController.java
  9. 18 0
      fs-service/src/main/java/com/fs/course/param/FsCourseLinkRoomParam.java
  10. 26 0
      fs-service/src/main/java/com/fs/course/param/newfs/FsUserCourseListParam.java
  11. 38 0
      fs-service/src/main/java/com/fs/course/param/newfs/UserCourseVideoPageParam.java
  12. 50 0
      fs-service/src/main/java/com/fs/course/vo/newfs/FsCourseAnalysisCountVO.java
  13. 43 0
      fs-service/src/main/java/com/fs/course/vo/newfs/FsCourseAnalysisVO.java
  14. 18 0
      fs-service/src/main/java/com/fs/course/vo/newfs/FsUserCourseListVO.java
  15. 37 0
      fs-service/src/main/java/com/fs/course/vo/newfs/FsUserCourseVideoDetailsVO.java
  16. 47 0
      fs-service/src/main/java/com/fs/course/vo/newfs/FsUserCourseVideoPageListVO.java
  17. 19 0
      fs-service/src/main/java/com/fs/course/vo/newfs/FsUserVideoListVO.java
  18. 38 0
      fs-service/src/main/java/com/fs/course/vo/newfs/FsUserVideoQuestionVO.java
  19. 68 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserWatchMapper.java
  20. 74 0
      fs-service/src/main/java/com/fs/his/service/IFsUserWatchService.java
  21. 196 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserWatchServiceImpl.java
  22. 10 0
      fs-service/src/main/java/com/fs/qw/param/QwTagSearchParam.java
  23. 13 0
      fs-service/src/main/java/com/fs/sop/vo/QwCreateLinkByAppVO.java
  24. 9 0
      fs-service/src/main/java/com/fs/watch/domain/vo/WatchDeviceInfoAndUserIdVo.java
  25. 98 0
      fs-service/src/main/resources/mapper/his/FsUserWatchMapper.xml

+ 147 - 0
fs-ad-api/src/main/resources/application-druid-myhk.yml

@@ -0,0 +1,147 @@
+# 数据源配置
+spring:
+    # redis 配置
+    redis:
+        host: 172.27.0.6
+        port: 6379
+        # 数据库索引
+        database: 0
+        # 密码
+        password: myhk888777666.
+        # 连接超时时间
+        timeout: 10s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+    datasource:
+        clickhouse:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
+            url: jdbc:clickhouse://1.14.104.71:8123/sop_test?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
+            username: rt_2024
+            password: Yzx_19860213
+            initialSize: 10
+            maxActive: 100
+            minIdle: 10
+            maxWait: 6000
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://172.27.0.17:3306/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: myhk888777666.
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    enabled: false
+                    url:
+                    username:
+                    password:
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://172.27.0.17:3306/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: myhk888777666.
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: voice-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+

+ 100 - 0
fs-common/src/main/java/com/fs/common/core/domain/ResponseResult.java

@@ -0,0 +1,100 @@
+package com.fs.common.core.domain;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fs.common.enums.BizResponseEnum;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+
+/**
+ * @author 曹丽芹
+ */
+@Getter
+@Setter
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ResponseResult<T> {
+    /**
+     * 自定义异常码
+     */
+    private Integer code;
+
+    /**
+     * 自定义异常信息
+     */
+    private String msg;
+
+    /**
+     * 响应数据
+     */
+    private T data;
+
+    /**
+     * 额外数据
+     */
+    private Object ext;
+
+    public ResponseResult() {
+    }
+
+    public ResponseResult(Integer code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public ResponseResult(BizResponseEnum exceptionEnum) {
+        this.code = exceptionEnum.getCode();
+        this.msg = exceptionEnum.getMsg();
+    }
+
+    public ResponseResult(Integer code, String msg, Object ext) {
+        this.code = code;
+        this.msg = msg;
+        this.ext = ext;
+    }
+
+    public ResponseResult(BizResponseEnum exceptionEnum, Object ext) {
+        this.code = exceptionEnum.getCode();
+        this.msg = exceptionEnum.getMsg();
+        this.ext = ext;
+    }
+
+    public ResponseResult(Integer code, String msg, T data, Object ext) {
+        this.code = code;
+        this.msg = msg;
+        this.data = data;
+        this.ext = ext;
+    }
+
+    public static <T> ResponseResult<T> ok() {
+        return ResponseResult.ok(null);
+    }
+
+    public static <T> ResponseResult<T> ok(T data) {
+        ResponseResult<T> ok = new ResponseResult<>();
+        ok.setCode(200);
+        ok.setMsg("success");
+        ok.setData(data);
+        return ok;
+    }
+
+    public static <T> ResponseResult<T> fail(BizResponseEnum exceptionEnum) {
+        return ResponseResult.fail(exceptionEnum, null);
+    }
+
+    public static <T> ResponseResult<T> fail(BizResponseEnum exceptionEnum, Object ext) {
+        ResponseResult<T> ok = new ResponseResult<>();
+        ok.setCode(exceptionEnum.getCode());
+        ok.setMsg(exceptionEnum.getMsg());
+        ok.setExt(ext);
+        return ok;
+    }
+
+    public static <T> ResponseResult<T> fail(Integer code, String msg) {
+        ResponseResult<T> ok = new ResponseResult<>();
+        ok.setCode(code);
+        ok.setMsg(msg);
+        return ok;
+    }
+}

+ 20 - 0
fs-common/src/main/java/com/fs/common/enums/BizResponseEnum.java

@@ -0,0 +1,20 @@
+package com.fs.common.enums;
+
+import lombok.Getter;
+
+@Getter
+public enum BizResponseEnum {
+    SUCCESS(200, "操作成功"),
+    FAIL(500, "操作失败"),
+    PARAM_ERROR(400, "参数错误"),
+    DATA_NOT_EXIST(1002, "数据不存在");
+
+    private final Integer code;
+    private final String msg;
+
+    BizResponseEnum(Integer code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+}

+ 148 - 0
fs-qwhook-msg/src/main/resources/application-druid-jz.yml

@@ -0,0 +1,148 @@
+# 数据源配置
+spring:
+    # redis 配置
+    redis:
+        # 地址
+        host: 127.0.0.1
+        # 端口,默认为6379
+        port: 6379
+        # 数据库索引
+        database: 0
+        # 密码
+        password:
+        # 连接超时时间
+        timeout: 20s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+    datasource:
+        #        clickhouse:
+        #            type: com.alibaba.druid.pool.DruidDataSource
+        #            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
+        #            url: jdbc:clickhouse://cc-2vc8zzo26w0l7m2l6.public.clickhouse.ads.aliyuncs.com/sop?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
+        #            username: rt_2024
+        #            password: Yzx_19860213
+        #            initialSize: 10
+        #            maxActive: 100
+        #            minIdle: 10
+        #            maxWait: 6000
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://192.168.0.137:3306/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    enabled: false
+                    url:
+                    username:
+                    password:
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://192.168.0.137:3306/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: test-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 130 - 0
fs-qwhook-sop/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -0,0 +1,130 @@
+package com.fs.app.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.course.domain.FsUserCourse;
+import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.param.FsCourseLinkRoomParam;
+import com.fs.course.param.newfs.FsUserCourseListParam;
+import com.fs.course.param.newfs.UserCourseVideoPageParam;
+import com.fs.course.service.IFsCourseLinkService;
+import com.fs.course.service.IFsUserCourseService;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.vo.newfs.FsUserCourseListVO;
+import com.fs.course.vo.newfs.FsUserCourseVideoDetailsVO;
+import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
+import com.fs.course.vo.newfs.FsUserVideoListVO;
+import com.fs.qw.domain.QwUser;
+import com.fs.qw.service.IQwUserService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+
+@Api("课程库相关接口")
+@RestController
+@RequestMapping("/apis/app/fs/course")
+@Slf4j
+public class FsUserCourseVideoController {
+
+    @Autowired
+    private IFsUserCourseVideoService fsUserCourseVideoService;
+
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
+
+    @Autowired
+    private IQwUserService qwUserService;
+
+    @GetMapping("/pageList")
+    @ApiOperation("课程分页列表")
+    public ResponseResult<PageInfo<FsUserCourseVideoPageListVO>> list(UserCourseVideoPageParam param) {
+        QwUser qwUser = qwUserService.getByQwUserIdAndCorId(param.getQwUserId(), param.getCorpId());
+        if (qwUser==null||qwUser.getCompanyId()==null){
+            return ResponseResult.fail(500,"无权限");
+        }
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setCompanyId(qwUser.getCompanyId());
+        List<FsUserCourseVideoPageListVO> list = fsUserCourseVideoService.pageListCourseVideo(param);
+        PageInfo<FsUserCourseVideoPageListVO> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @ApiOperation("课程视频详情")
+    @GetMapping(value = "/videoDetails")
+    public ResponseResult<FsUserCourseVideoDetailsVO> getVideoDetails(Long videoId) {
+        return fsUserCourseVideoService.getVideoDetails(videoId);
+    }
+
+    @GetMapping("/courseList")
+    @ApiOperation("获取课程下拉列表")
+    public ResponseResult<PageInfo<FsUserCourseListVO>> getAllCourseList(FsUserCourseListParam param) {
+        QwUser qwUser = qwUserService.getByQwUserIdAndCorId(param.getQwUserId(), param.getCorpId());
+        if (qwUser==null||qwUser.getCompanyId()==null){
+            return ResponseResult.fail(500,"无权限");
+        }
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setCompanyId(qwUser.getCompanyId());
+        List<FsUserCourseListVO> fsUserCourseList = fsUserCourseService.getFsUserCourseList(param);
+        PageInfo<FsUserCourseListVO> pageInfo = new PageInfo<>(fsUserCourseList);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @GetMapping("/videoList")
+    @ApiOperation("获取视频下拉列表")
+    public ResponseResult<PageInfo<FsUserVideoListVO>> getAllVideoList(UserCourseVideoPageParam param) {
+        QwUser qwUser = qwUserService.getByQwUserIdAndCorId(param.getQwUserId(), param.getCorpId());
+        if (qwUser==null||qwUser.getCompanyId()==null){
+            return ResponseResult.fail(500,"无权限");
+        }
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setCompanyId(qwUser.getCompanyId());
+        List<FsUserVideoListVO> listCourseVideo = fsUserCourseVideoService.getListCourseVideo(param);
+        PageInfo<FsUserVideoListVO> result = new PageInfo<>(listCourseVideo);
+        return ResponseResult.ok(result);
+    }
+
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
+
+    @GetMapping("/createRoomLink")
+    @ApiOperation("创建发群链接")
+    public R createRoomLink(FsCourseLinkRoomParam param) {
+        QwUser qwUser = qwUserService.getByQwUserIdAndCorId(param.getQwUserId(), param.getCorpId());
+        if (qwUser==null||qwUser.getCompanyId()==null){
+            return R.error("无权限");
+        }
+        FsCourseLinkCreateParam createParam = new FsCourseLinkCreateParam();
+        createParam.setCourseId(param.getCourseId());
+        createParam.setVideoId(param.getVideoId());
+        createParam.setCorpId(param.getCorpId());
+        createParam.setCompanyUserId(qwUser.getCompanyUserId());
+        createParam.setCompanyId(qwUser.getCompanyId());
+        createParam.setQwUserId(qwUser.getId().toString());
+        String linkUrl;
+        R createLink = courseLinkService.createRoomLinkUrl(createParam);
+        if (createLink.get("code").equals(500)){
+            return R.error("链接生成失败!");
+        }
+        linkUrl = (String) createLink.get("url");
+
+        FsUserCourse course = fsUserCourseService.selectFsUserCourseByCourseId(param.getCourseId());
+
+        JSONObject news = new JSONObject(true); // true 表示保持字段顺序
+        news.put("link", linkUrl);
+        news.put("title", course.getCourseName());
+        news.put("desc", param.getTitle());
+        news.put("imgUrl", course.getImgUrl());
+        return R.ok().put("news",news);
+    }
+
+}

+ 148 - 0
fs-qwhook-sop/src/main/resources/application-druid-jz.yml

@@ -0,0 +1,148 @@
+# 数据源配置
+spring:
+    # redis 配置
+    redis:
+        # 地址
+        host: 127.0.0.1
+        # 端口,默认为6379
+        port: 6379
+        # 数据库索引
+        database: 0
+        # 密码
+        password:
+        # 连接超时时间
+        timeout: 20s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+    datasource:
+        #        clickhouse:
+        #            type: com.alibaba.druid.pool.DruidDataSource
+        #            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
+        #            url: jdbc:clickhouse://cc-2vc8zzo26w0l7m2l6.public.clickhouse.ads.aliyuncs.com/sop?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
+        #            username: rt_2024
+        #            password: Yzx_19860213
+        #            initialSize: 10
+        #            maxActive: 100
+        #            minIdle: 10
+        #            maxWait: 6000
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://192.168.0.137:3306/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    enabled: false
+                    url:
+                    username:
+                    password:
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://192.168.0.137:3306/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: test-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 130 - 0
fs-qwhook/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -0,0 +1,130 @@
+package com.fs.app.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.course.domain.FsUserCourse;
+import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.param.FsCourseLinkRoomParam;
+import com.fs.course.param.newfs.FsUserCourseListParam;
+import com.fs.course.param.newfs.UserCourseVideoPageParam;
+import com.fs.course.service.IFsCourseLinkService;
+import com.fs.course.service.IFsUserCourseService;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.vo.newfs.FsUserCourseListVO;
+import com.fs.course.vo.newfs.FsUserCourseVideoDetailsVO;
+import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
+import com.fs.course.vo.newfs.FsUserVideoListVO;
+import com.fs.qw.domain.QwUser;
+import com.fs.qw.service.IQwUserService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+
+@Api("课程库相关接口")
+@RestController
+@RequestMapping("/app/fs/course")
+@Slf4j
+public class FsUserCourseVideoController {
+
+    @Autowired
+    private IFsUserCourseVideoService fsUserCourseVideoService;
+
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
+
+    @Autowired
+    private IQwUserService qwUserService;
+
+    @GetMapping("/pageList")
+    @ApiOperation("课程分页列表")
+    public ResponseResult<PageInfo<FsUserCourseVideoPageListVO>> list(UserCourseVideoPageParam param) {
+        QwUser qwUser = qwUserService.getByQwUserIdAndCorId(param.getQwUserId(), param.getCorpId());
+        if (qwUser==null||qwUser.getCompanyId()==null){
+            return ResponseResult.fail(500,"无权限");
+        }
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setCompanyId(qwUser.getCompanyId());
+        List<FsUserCourseVideoPageListVO> list = fsUserCourseVideoService.pageListCourseVideo(param);
+        PageInfo<FsUserCourseVideoPageListVO> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @ApiOperation("课程视频详情")
+    @GetMapping(value = "/videoDetails")
+    public ResponseResult<FsUserCourseVideoDetailsVO> getVideoDetails(Long videoId) {
+        return fsUserCourseVideoService.getVideoDetails(videoId);
+    }
+
+    @GetMapping("/courseList")
+    @ApiOperation("获取课程下拉列表")
+    public ResponseResult<PageInfo<FsUserCourseListVO>> getAllCourseList(FsUserCourseListParam param) {
+        QwUser qwUser = qwUserService.getByQwUserIdAndCorId(param.getQwUserId(), param.getCorpId());
+        if (qwUser==null||qwUser.getCompanyId()==null){
+            return ResponseResult.fail(500,"无权限");
+        }
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setCompanyId(qwUser.getCompanyId());
+        List<FsUserCourseListVO> fsUserCourseList = fsUserCourseService.getFsUserCourseList(param);
+        PageInfo<FsUserCourseListVO> pageInfo = new PageInfo<>(fsUserCourseList);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @GetMapping("/videoList")
+    @ApiOperation("获取视频下拉列表")
+    public ResponseResult<PageInfo<FsUserVideoListVO>> getAllVideoList(UserCourseVideoPageParam param) {
+        QwUser qwUser = qwUserService.getByQwUserIdAndCorId(param.getQwUserId(), param.getCorpId());
+        if (qwUser==null||qwUser.getCompanyId()==null){
+            return ResponseResult.fail(500,"无权限");
+        }
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setCompanyId(qwUser.getCompanyId());
+        List<FsUserVideoListVO> listCourseVideo = fsUserCourseVideoService.getListCourseVideo(param);
+        PageInfo<FsUserVideoListVO> result = new PageInfo<>(listCourseVideo);
+        return ResponseResult.ok(result);
+    }
+
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
+
+    @GetMapping("/createRoomLink")
+    @ApiOperation("创建发群链接")
+    public R createRoomLink(FsCourseLinkRoomParam param) {
+        QwUser qwUser = qwUserService.getByQwUserIdAndCorId(param.getQwUserId(), param.getCorpId());
+        if (qwUser==null||qwUser.getCompanyId()==null){
+            return R.error("无权限");
+        }
+        FsCourseLinkCreateParam createParam = new FsCourseLinkCreateParam();
+        createParam.setCourseId(param.getCourseId());
+        createParam.setVideoId(param.getVideoId());
+        createParam.setCorpId(param.getCorpId());
+        createParam.setCompanyUserId(qwUser.getCompanyUserId());
+        createParam.setCompanyId(qwUser.getCompanyId());
+        createParam.setQwUserId(qwUser.getId().toString());
+        String linkUrl;
+        R createLink = courseLinkService.createRoomLinkUrl(createParam);
+        if (createLink.get("code").equals(500)){
+            return R.error("链接生成失败!");
+        }
+        linkUrl = (String) createLink.get("url");
+
+        FsUserCourse course = fsUserCourseService.selectFsUserCourseByCourseId(param.getCourseId());
+
+        JSONObject news = new JSONObject(true); // true 表示保持字段顺序
+        news.put("link", linkUrl);
+        news.put("title", course.getCourseName());
+        news.put("desc", param.getTitle());
+        news.put("imgUrl", course.getImgUrl());
+        return R.ok().put("news",news);
+    }
+
+}

+ 103 - 0
fs-service/src/main/java/com/fs/ad/controller/AdAccountController.java

@@ -0,0 +1,103 @@
+package com.fs.ad.controller;
+
+import java.util.List;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.ad.domain.AdAccount;
+import com.fs.ad.service.IAdAccountService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 推广账户Controller
+ * 
+ * @author fs
+ * @date 2025-03-04
+ */
+@RestController
+@RequestMapping("/ad/adAccount")
+public class AdAccountController extends BaseController
+{
+    @Autowired
+    private IAdAccountService adAccountService;
+
+    /**
+     * 查询推广账户列表
+     */
+    @PreAuthorize("@ss.hasPermi('ad:adAccount:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(AdAccount adAccount)
+    {
+        startPage();
+        List<AdAccount> list = adAccountService.selectAdAccountList(adAccount);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出推广账户列表
+     */
+    @PreAuthorize("@ss.hasPermi('ad:adAccount:export')")
+    @Log(title = "推广账户", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(AdAccount adAccount)
+    {
+        List<AdAccount> list = adAccountService.selectAdAccountList(adAccount);
+        ExcelUtil<AdAccount> util = new ExcelUtil<AdAccount>(AdAccount.class);
+        return util.exportExcel(list, "推广账户数据");
+    }
+
+    /**
+     * 获取推广账户详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('ad:adAccount:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(adAccountService.selectAdAccountById(id));
+    }
+
+    /**
+     * 新增推广账户
+     */
+    @PreAuthorize("@ss.hasPermi('ad:adAccount:add')")
+    @Log(title = "推广账户", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody AdAccount adAccount)
+    {
+        return toAjax(adAccountService.insertAdAccount(adAccount));
+    }
+
+    /**
+     * 修改推广账户
+     */
+    @PreAuthorize("@ss.hasPermi('ad:adAccount:edit')")
+    @Log(title = "推广账户", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody AdAccount adAccount)
+    {
+        return toAjax(adAccountService.updateAdAccount(adAccount));
+    }
+
+    /**
+     * 删除推广账户
+     */
+    @PreAuthorize("@ss.hasPermi('ad:adAccount:remove')")
+    @Log(title = "推广账户", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(adAccountService.deleteAdAccountByIds(ids));
+    }
+}

+ 18 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseLinkRoomParam.java

@@ -0,0 +1,18 @@
+package com.fs.course.param;
+
+import lombok.Data;
+
+@Data
+public class FsCourseLinkRoomParam {
+
+    private Long videoId;
+
+    private String qwUserId;
+
+    private String corpId;
+
+    private Long courseId;
+
+    private String title;//视频标题
+
+}

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

@@ -0,0 +1,26 @@
+package com.fs.course.param.newfs;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class FsUserCourseListParam {
+
+    @ApiModelProperty(value = "页码,默认为1", required = true)
+    private Integer pageNum = 1;
+
+    @ApiModelProperty(value = "页大小,默认为10", required = true)
+    private Integer pageSize = 10;
+
+    @ApiModelProperty(value = "模糊搜索,通过视频名称来匹配")
+    private String keyword;
+
+    @ApiModelProperty(value = "公司id,不用传")
+    private Long companyId;
+
+    private String corpId;
+
+    private String qwUserId;
+
+
+}

+ 38 - 0
fs-service/src/main/java/com/fs/course/param/newfs/UserCourseVideoPageParam.java

@@ -0,0 +1,38 @@
+package com.fs.course.param.newfs;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel
+public class UserCourseVideoPageParam implements Serializable {
+
+    @ApiModelProperty(value = "页码,默认为1", required = true)
+    private Integer pageNum = 1;
+
+    @ApiModelProperty(value = "页大小,默认为10", required = true)
+    private Integer pageSize = 10;
+
+    @ApiModelProperty(value = "模糊搜索,通过视频名称来匹配")
+    private String keyword;
+
+    @ApiModelProperty(value = "课程id")
+    private Long courseId;
+
+    @ApiModelProperty(value = "公司id")
+    private Long companyId;
+
+    private String corpId;
+
+    private String qwUserId;
+
+
+
+//    @ApiModelProperty(value = "视频状态 1:草稿,2:待审核,3:发布")
+//    private Long status;
+
+}

+ 50 - 0
fs-service/src/main/java/com/fs/course/vo/newfs/FsCourseAnalysisCountVO.java

@@ -0,0 +1,50 @@
+package com.fs.course.vo.newfs;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel
+public class FsCourseAnalysisCountVO implements Cloneable {
+
+    @ApiModelProperty(value = "关联视频id")
+    private Long videoId;
+
+    @ApiModelProperty(value = "观看人数")
+    private int courseWatchNum;
+
+    @ApiModelProperty(value = "完播人数")
+    private int courseCompleteNum;
+
+    @ApiModelProperty(value = "完播率")
+    private BigDecimal completeRate;
+
+    @ApiModelProperty(value = "答题红包数")
+    private int redPacketNum;
+
+    @ApiModelProperty(value = "答题红包金额")
+    private BigDecimal redPacketAmount;
+
+    @ApiModelProperty(value = "答题人数")
+    private int answerNum;
+
+    @ApiModelProperty(value = "正确人数")
+    private int answerRightNum;
+
+    @ApiModelProperty(value = "正确率")
+    private BigDecimal answerRightRate;
+
+    @Override
+    public FsCourseAnalysisCountVO clone() {
+        FsCourseAnalysisCountVO countVO;
+        try {
+            countVO = (FsCourseAnalysisCountVO) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError();
+        }
+        return countVO;
+    }
+}

+ 43 - 0
fs-service/src/main/java/com/fs/course/vo/newfs/FsCourseAnalysisVO.java

@@ -0,0 +1,43 @@
+package com.fs.course.vo.newfs;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel
+public class FsCourseAnalysisVO implements Cloneable{
+
+    @ApiModelProperty(value = "课程id")
+    private Long courseId;
+
+    @ApiModelProperty(value = "课程名称")
+    private String courseName;
+
+    @ApiModelProperty(value = "关联视频id")
+    private Long videoId;
+
+    @ApiModelProperty(value = "视频标题")
+    private String title;
+
+    @ApiModelProperty(value = "视频封面")
+    private String thumbnail;
+
+    @ApiModelProperty(value = "统计信息")
+    private FsCourseAnalysisCountVO countVO;
+
+    /**
+     * 深度克隆
+     */
+    @Override
+    public FsCourseAnalysisVO clone() {
+        FsCourseAnalysisVO vo;
+        try {
+            vo = (FsCourseAnalysisVO) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError();
+        }
+        vo.countVO = countVO.clone();
+        return vo;
+    }
+}

+ 18 - 0
fs-service/src/main/java/com/fs/course/vo/newfs/FsUserCourseListVO.java

@@ -0,0 +1,18 @@
+package com.fs.course.vo.newfs;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 课程下拉列表 对象(需要分页)
+ */
+@Data
+public class FsUserCourseListVO {
+
+    @ApiModelProperty(value = "课程id")
+    private Long courseId;
+
+    @ApiModelProperty(value = "课程名称")
+    private String courseName;
+
+}

+ 37 - 0
fs-service/src/main/java/com/fs/course/vo/newfs/FsUserCourseVideoDetailsVO.java

@@ -0,0 +1,37 @@
+package com.fs.course.vo.newfs;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@ApiModel
+public class FsUserCourseVideoDetailsVO {
+
+    @ApiModelProperty(value = "课程视频id")
+    private Long videoId;
+
+    @ApiModelProperty(value = "视频标题")
+    private String title;
+
+    @ApiModelProperty(value = "视频描述")
+    private String description;
+
+    @ApiModelProperty(value = "视频URL")
+    private String videoUrl;
+
+    @ApiModelProperty(value = "视频缩略图")
+    private String thumbnail;
+
+    @ApiModelProperty(value = "视频时长")
+    private Long duration;
+
+    @ApiModelProperty(value = "课程ID")
+    private Long courseId;
+
+    @ApiModelProperty(value = "题库内容")
+    private List<FsUserVideoQuestionVO> questionBankList;
+
+}

+ 47 - 0
fs-service/src/main/java/com/fs/course/vo/newfs/FsUserCourseVideoPageListVO.java

@@ -0,0 +1,47 @@
+package com.fs.course.vo.newfs;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FsUserCourseVideoPageListVO extends BaseEntity {
+
+    @ApiModelProperty(value = "课程视频id")
+    private Long videoId;
+
+    @ApiModelProperty(value = "视频标题")
+    private String title;
+
+    @ApiModelProperty(value = "视频描述")
+    private String description;
+
+    @ApiModelProperty(value = "视频缩略图")
+    private String thumbnail;
+
+    @ApiModelProperty(value = "视频时长")
+    private Long duration;
+
+    @ApiModelProperty(value = "课程ID")
+    private Long courseId;
+
+    @ApiModelProperty(value = "视频状态 1:草稿,2:待审核,3:发布")
+    private Long status;
+
+    @ApiModelProperty(value = "课程排序")
+    private Long courseSort;
+
+    @ApiModelProperty(value = "课程名称")
+    private String courseName;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(value = "创建时间")
+    private Date createTime;
+
+
+}

+ 19 - 0
fs-service/src/main/java/com/fs/course/vo/newfs/FsUserVideoListVO.java

@@ -0,0 +1,19 @@
+package com.fs.course.vo.newfs;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 视频下拉列表 对象(需要分页)
+ *
+ */
+@Data
+public class FsUserVideoListVO {
+
+    @ApiModelProperty(value = "视频id")
+    private Long videoId;
+
+    @ApiModelProperty(value = "视频名称")
+    private String title;
+
+}

+ 38 - 0
fs-service/src/main/java/com/fs/course/vo/newfs/FsUserVideoQuestionVO.java

@@ -0,0 +1,38 @@
+package com.fs.course.vo.newfs;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 课程视频题库
+ */
+@Data
+@ApiModel
+public class FsUserVideoQuestionVO {
+
+    @ApiModelProperty(value = "id")
+    private Long id;
+
+    @ApiModelProperty(value = "问题")
+    private String title;
+
+    @ApiModelProperty(value = "排序")
+    private Long sort;
+
+    @ApiModelProperty(value = "类别,1-单选,2-多选 ")
+    private Long type;
+
+    @ApiModelProperty(value = "状态")
+    private Long status;
+
+    @ApiModelProperty(value = "问题类别")
+    private String questionType;
+
+    @ApiModelProperty(value = "选项")
+    private String question;
+
+    @ApiModelProperty(value = "答案")
+    private String answer;
+
+}

+ 68 - 0
fs-service/src/main/java/com/fs/his/mapper/FsUserWatchMapper.java

@@ -0,0 +1,68 @@
+package com.fs.his.mapper;
+
+import java.util.List;
+import com.fs.his.domain.FsUserWatch;
+
+/**
+ * 用户腕Mapper接口
+ *
+ * @author fs
+ * @date 2024-11-14
+ */
+public interface FsUserWatchMapper
+{
+    /**
+     * 查询用户腕
+     *
+     * @param id 用户腕主键
+     * @return 用户腕
+     */
+    public FsUserWatch selectFsUserWatchById(Long id);
+
+    /**
+     * 查询用户腕列表
+     *
+     * @param fsUserWatch 用户腕
+     * @return 用户腕集合
+     */
+    public List<FsUserWatch> selectFsUserWatchList(FsUserWatch fsUserWatch);
+
+    /**
+     * 新增用户腕
+     *
+     * @param fsUserWatch 用户腕
+     * @return 结果
+     */
+    public int insertFsUserWatch(FsUserWatch fsUserWatch);
+
+    /**
+     * 修改用户腕
+     *
+     * @param fsUserWatch 用户腕
+     * @return 结果
+     */
+    public int updateFsUserWatch(FsUserWatch fsUserWatch);
+
+    /**
+     * 删除用户腕
+     *
+     * @param id 用户腕主键
+     * @return 结果
+     */
+    public int deleteFsUserWatchById(Long id);
+
+    /**
+     * 批量删除用户腕
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteFsUserWatchByIds(Long[] ids);
+
+    /**
+     * 根据userId查询最新得一条记录
+     * @param userId 用户id
+     * @return FsUserWatch
+     */
+    FsUserWatch getLastByUserId(Long userId);
+}

+ 74 - 0
fs-service/src/main/java/com/fs/his/service/IFsUserWatchService.java

@@ -0,0 +1,74 @@
+package com.fs.his.service;
+
+import java.util.List;
+
+import com.fs.his.domain.FsStoreOrder;
+import com.fs.his.domain.FsUserWatch;
+
+/**
+ * 用户腕Service接口
+ *
+ * @author fs
+ * @date 2024-11-14
+ */
+public interface IFsUserWatchService
+{
+    /**
+     * 查询用户腕
+     *
+     * @param id 用户腕主键
+     * @return 用户腕
+     */
+    public FsUserWatch selectFsUserWatchById(Long id);
+
+    /**
+     * 查询用户腕列表
+     *
+     * @param fsUserWatch 用户腕
+     * @return 用户腕集合
+     */
+    public List<FsUserWatch> selectFsUserWatchList(FsUserWatch fsUserWatch);
+
+    /**
+     * 新增用户腕
+     *
+     * @param fsUserWatch 用户腕
+     * @return 结果
+     */
+    public int insertFsUserWatch(FsUserWatch fsUserWatch);
+
+    /**
+     * 修改用户腕
+     *
+     * @param fsUserWatch 用户腕
+     * @return 结果
+     */
+    public int updateFsUserWatch(FsUserWatch fsUserWatch);
+
+    /**
+     * 批量删除用户腕
+     *
+     * @param ids 需要删除的用户腕主键集合
+     * @return 结果
+     */
+    public int deleteFsUserWatchByIds(Long[] ids);
+
+    /**
+     * 删除用户腕信息
+     *
+     * @param id 用户腕主键
+     * @return 结果
+     */
+    public int deleteFsUserWatchById(Long id);
+
+
+
+    void addUserWatch(FsStoreOrder order);
+
+    /**
+     * 根据userId查询最新得一条记录
+     * @param userId 用户id
+     * @return FsUserWatch
+     */
+    FsUserWatch getLastByUserId(Long userId);
+}

+ 196 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserWatchServiceImpl.java

@@ -0,0 +1,196 @@
+package com.fs.his.service.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.his.domain.FsPackage;
+import com.fs.his.domain.FsStoreOrder;
+import com.fs.his.domain.FsStoreOrderItem;
+import com.fs.his.dto.FsPackagePruductDTO;
+import com.fs.his.dto.FsStoreOrderItemDTO;
+import com.fs.his.mapper.FsStoreMapper;
+import com.fs.his.mapper.FsStoreOrderItemMapper;
+import com.fs.his.service.IFsPackageOrderService;
+import com.fs.watch.domain.WatchFsUser;
+import com.fs.watch.domain.WatchMedicationTask;
+import com.fs.watch.service.WatchMedicationTaskService;
+import com.fs.watch.service.WatchUserService;
+import com.fs.wx.utils.JsonUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import com.fs.his.mapper.FsUserWatchMapper;
+import com.fs.his.domain.FsUserWatch;
+import com.fs.his.service.IFsUserWatchService;
+
+/**
+ * 用户腕Service业务层处理
+ *
+ * @author fs
+ * @date 2024-11-14
+ */
+@Service
+public class FsUserWatchServiceImpl implements IFsUserWatchService
+{
+    @Autowired
+    private FsUserWatchMapper fsUserWatchMapper;
+    @Autowired
+    private FsStoreOrderItemMapper fsStoreOrderItemMapper;
+    @Autowired
+    private WatchUserService watchUserService;
+    @Autowired
+    @Lazy
+    private IFsPackageOrderService fsPackageOrderService;
+    @Autowired
+    private WatchMedicationTaskService watchMedicationTaskService;
+    /**
+     * 查询用户腕
+     *
+     * @param id 用户腕主键
+     * @return 用户腕
+     */
+    @Override
+    public FsUserWatch selectFsUserWatchById(Long id)
+    {
+        return fsUserWatchMapper.selectFsUserWatchById(id);
+    }
+
+    /**
+     * 查询用户腕列表
+     *
+     * @param fsUserWatch 用户腕
+     * @return 用户腕
+     */
+    @Override
+    public List<FsUserWatch> selectFsUserWatchList(FsUserWatch fsUserWatch)
+    {
+        return fsUserWatchMapper.selectFsUserWatchList(fsUserWatch);
+    }
+
+    /**
+     * 新增用户腕
+     *
+     * @param fsUserWatch 用户腕
+     * @return 结果
+     */
+    @Override
+    public int insertFsUserWatch(FsUserWatch fsUserWatch)
+    {
+        fsUserWatch.setCreateTime(DateUtils.getNowDate());
+        return fsUserWatchMapper.insertFsUserWatch(fsUserWatch);
+    }
+
+    /**
+     * 修改用户腕
+     *
+     * @param fsUserWatch 用户腕
+     * @return 结果
+     */
+    @Override
+    public int updateFsUserWatch(FsUserWatch fsUserWatch)
+    {
+        fsUserWatch.setUpdateTime(DateUtils.getNowDate());
+        return fsUserWatchMapper.updateFsUserWatch(fsUserWatch);
+    }
+
+    /**
+     * 批量删除用户腕
+     *
+     * @param ids 需要删除的用户腕主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserWatchByIds(Long[] ids)
+    {
+        return fsUserWatchMapper.deleteFsUserWatchByIds(ids);
+    }
+
+    /**
+     * 删除用户腕信息
+     *
+     * @param id 用户腕主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserWatchById(Long id)
+    {
+        return fsUserWatchMapper.deleteFsUserWatchById(id);
+    }
+
+    @Async
+    @Override
+    public void addUserWatch(FsStoreOrder order) {
+
+        List<FsStoreOrderItem> fsStoreOrderItems = fsStoreOrderItemMapper.selectFsStoreOrderItemListByOrderId(order.getOrderId());
+        for (FsStoreOrderItem fsStoreOrderItem : fsStoreOrderItems) {
+            String jsonInfo = fsStoreOrderItem.getJsonInfo();
+            FsStoreOrderItemDTO fsStoreOrderItemDTO = JSON.parseObject(jsonInfo, FsStoreOrderItemDTO.class);
+            //腕表条码
+            if ("1014704".equals(fsStoreOrderItemDTO.getBarCode())){
+                FsUserWatch fsUserWatch = new FsUserWatch();
+                fsUserWatch.setUserId(order.getUserId());
+                fsUserWatch.setCompanyId(order.getCompanyId());
+                fsUserWatch.setCompanyUserId(order.getCompanyUserId());
+                fsUserWatch.setDoctorId(order.getDoctorId());
+                fsUserWatch.setStoreOrderId(order.getOrderId());
+                fsUserWatch.setStatus(1);//已发货
+                fsUserWatch.setCreateTime(DateUtils.getNowDate());
+                fsUserWatchMapper.insertFsUserWatch(fsUserWatch);
+            }
+
+            //2.新增用药提醒任务
+            //2.1判断该用户是否已经绑腕表
+            WatchFsUser watchFsUser = watchUserService.selectWatchFsUserById(order.getUserId());
+            if (watchFsUser !=null && StringUtils.isNotBlank(watchFsUser.getDeviceId())){
+                //已绑定则新增用药提醒任务
+                //2.2判断是否包含套餐 ,只有套餐有用法
+                if (order.getPackageOrderId()!=null) {
+                    //查询是否含有改套餐订单以及套餐信息
+                    FsPackage fsPackage = fsPackageOrderService.selectFsPackageByOrderId(order.getPackageOrderId());
+                    if (fsPackage!=null && StringUtils.isNotBlank(fsPackage.getProductJson())){
+                        List<FsPackagePruductDTO> fsPackageProductDTOS = JSONObject.parseArray(fsPackage.getProductJson(), FsPackagePruductDTO.class);
+                        if (fsPackageProductDTOS!=null && !fsPackageProductDTOS.isEmpty()){
+                            //添加任务
+                            ArrayList<WatchMedicationTask> tasks = new ArrayList<>();
+                            for (FsPackagePruductDTO dto : fsPackageProductDTOS) {
+                                if(dto.getUsageFrequencyUnit()!=null){
+                                    WatchMedicationTask watchMedicationTask = new WatchMedicationTask();
+                                    watchMedicationTask.setPackageOrderId(order.getPackageOrderId());
+                                    watchMedicationTask.setPackageId(fsPackage.getPackageId());
+                                    watchMedicationTask.setProductId(dto.getProductId());
+                                    watchMedicationTask.setProductName(dto.getProductName());
+                                    watchMedicationTask.setUsageFrequencyUnit(dto.getUsageFrequencyUnit());
+                                    //默认提醒三天
+                                    watchMedicationTask.setRequiredNum(dto.getUsageFrequencyUnit()*3);
+                                    watchMedicationTask.setActualNum(0);
+                                    watchMedicationTask.setCreateTime(new Date());
+                                    watchMedicationTask.setUserId(order.getUserId());
+                                    watchMedicationTask.setStatus(0);
+                                    tasks.add(watchMedicationTask);
+                                }
+                            }
+                            watchMedicationTaskService.insertList(tasks);
+                        }
+                    }
+                }
+            }
+
+        }
+    }
+
+    /**
+     * 根据userId查询最新得一条记录
+     * @param userId 用户id
+     * @return FsUserWatch
+     */
+    @Override
+    public FsUserWatch getLastByUserId(Long userId) {
+        return fsUserWatchMapper.getLastByUserId(userId);
+    }
+}

+ 10 - 0
fs-service/src/main/java/com/fs/qw/param/QwTagSearchParam.java

@@ -0,0 +1,10 @@
+package com.fs.qw.param;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class QwTagSearchParam {
+    List<String> tagIds;
+}

+ 13 - 0
fs-service/src/main/java/com/fs/sop/vo/QwCreateLinkByAppVO.java

@@ -0,0 +1,13 @@
+package com.fs.sop.vo;
+
+import lombok.Data;
+
+@Data
+public class QwCreateLinkByAppVO {
+
+    //卡片跳转得短链
+    private String sortLink ;
+
+    //发送app消息的链接
+    private String appMsgLink;
+}

+ 9 - 0
fs-service/src/main/java/com/fs/watch/domain/vo/WatchDeviceInfoAndUserIdVo.java

@@ -0,0 +1,9 @@
+package com.fs.watch.domain.vo;
+
+import com.fs.watch.domain.WatchDeviceInfo;
+import lombok.Data;
+
+@Data
+public class WatchDeviceInfoAndUserIdVo extends WatchDeviceInfo {
+    private Long userId;
+}

+ 98 - 0
fs-service/src/main/resources/mapper/his/FsUserWatchMapper.xml

@@ -0,0 +1,98 @@
+<?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.his.mapper.FsUserWatchMapper">
+
+    <resultMap type="FsUserWatch" id="FsUserWatchResult">
+        <result property="id"    column="id"    />
+        <result property="userId"    column="user_id"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="companyUserId"    column="company_user_id"    />
+        <result property="doctorId"    column="doctor_id"    />
+        <result property="storeOrderId"    column="store_order_id"    />
+        <result property="status"    column="status"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectFsUserWatchVo">
+        select id, user_id, company_id, company_user_id, doctor_id, store_order_id, status, create_time, update_time from fs_user_watch
+    </sql>
+
+    <select id="selectFsUserWatchList" parameterType="FsUserWatch" resultMap="FsUserWatchResult">
+        <include refid="selectFsUserWatchVo"/>
+        <where>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
+            <if test="doctorId != null "> and doctor_id = #{doctorId}</if>
+            <if test="storeOrderId != null "> and store_order_id = #{storeOrderId}</if>
+            <if test="status != null "> and status = #{status}</if>
+        </where>
+        order by create_time desc
+    </select>
+
+    <select id="selectFsUserWatchById" parameterType="Long" resultMap="FsUserWatchResult">
+        <include refid="selectFsUserWatchVo"/>
+        where id = #{id}
+    </select>
+
+    <select id="getLastByUserId" resultType="com.fs.his.domain.FsUserWatch">
+        <include refid="selectFsUserWatchVo"></include>
+        where user_id = #{userId}
+        order by create_time limit 1
+    </select>
+
+    <insert id="insertFsUserWatch" parameterType="FsUserWatch">
+        insert into fs_user_watch
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="companyId != null">company_id,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="doctorId != null">doctor_id,</if>
+            <if test="storeOrderId != null">store_order_id,</if>
+            <if test="status != null">status,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="doctorId != null">#{doctorId},</if>
+            <if test="storeOrderId != null">#{storeOrderId},</if>
+            <if test="status != null">#{status},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateFsUserWatch" parameterType="FsUserWatch">
+        update fs_user_watch
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
+            <if test="doctorId != null">doctor_id = #{doctorId},</if>
+            <if test="storeOrderId != null">store_order_id = #{storeOrderId},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsUserWatchById" parameterType="Long">
+        delete from fs_user_watch where id = #{id}
+    </delete>
+
+    <delete id="deleteFsUserWatchByIds" parameterType="String">
+        delete from fs_user_watch where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>