Преглед на файлове

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
#	fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
zx преди 2 седмици
родител
ревизия
76500eeeba
променени са 59 файла, в които са добавени 1511 реда и са изтрити 248 реда
  1. 8 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyController.java
  2. 6 0
      fs-common/src/main/java/com/fs/common/core/domain/BaseEntity.java
  3. 4 5
      fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  4. 63 19
      fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
  5. 31 6
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  6. 15 0
      fs-company/src/main/java/com/fs/company/controller/CompanyController.java
  7. 18 0
      fs-company/src/main/java/com/fs/company/controller/CompanyUserController.java
  8. 168 53
      fs-company/src/main/java/com/fs/qw/QwUserController.java
  9. 10 0
      fs-company/src/main/resources/application-druid.yml
  10. 2 1
      fs-company/src/main/resources/application.yml
  11. 2 1
      fs-qw-task/src/main/java/com/fs/FsQwTaskApplication.java
  12. 11 1
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  13. 2 2
      fs-qw-task/src/main/resources/application.yml
  14. 10 0
      fs-service-system/src/main/java/com/fs/company/domain/CompanyUser.java
  15. 18 1
      fs-service-system/src/main/java/com/fs/company/mapper/CompanyMapper.java
  16. 5 2
      fs-service-system/src/main/java/com/fs/company/mapper/CompanyUserMapper.java
  17. 4 0
      fs-service-system/src/main/java/com/fs/company/service/ICompanyService.java
  18. 7 0
      fs-service-system/src/main/java/com/fs/company/service/ICompanyUserService.java
  19. 6 0
      fs-service-system/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  20. 22 0
      fs-service-system/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
  21. 20 0
      fs-service-system/src/main/java/com/fs/company/vo/CompanyNameVO.java
  22. 2 1
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java
  23. 9 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java
  24. 3 1
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseService.java
  25. 8 1
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
  26. 25 29
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java
  27. 4 4
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java
  28. 11 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  29. 61 0
      fs-service-system/src/main/java/com/fs/course/vo/FsCourseAnalysisVO.java
  30. 21 0
      fs-service-system/src/main/java/com/fs/course/vo/newfs/FsUserCourseListVO.java
  31. 16 0
      fs-service-system/src/main/java/com/fs/his/vo/CitysAreaVO.java
  32. 1 1
      fs-service-system/src/main/java/com/fs/qw/domain/QwExternalContact.java
  33. 28 0
      fs-service-system/src/main/java/com/fs/qw/dto/QwUserByToolDTO.java
  34. 8 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  35. 10 0
      fs-service-system/src/main/java/com/fs/qw/param/QwCloudIPByCompanyParam.java
  36. 2 0
      fs-service-system/src/main/java/com/fs/qw/service/IQwUserService.java
  37. 17 15
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  38. 66 0
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  39. 2 5
      fs-service-system/src/main/java/com/fs/sop/mapper/SopUserLogsInfoMapper.java
  40. 1 0
      fs-service-system/src/main/java/com/fs/sop/params/SendUserLogsInfoMsgParam.java
  41. 10 0
      fs-service-system/src/main/java/com/fs/sop/params/SopUserLogsInfoByIdsHasUserIdParam.java
  42. 32 20
      fs-service-system/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java
  43. 13 8
      fs-service-system/src/main/java/com/fs/store/mapper/FsCityMapper.java
  44. 16 17
      fs-service-system/src/main/java/com/fs/store/mapper/FsUserMapper.java
  45. 12 8
      fs-service-system/src/main/java/com/fs/store/service/IFsCityService.java
  46. 11 2
      fs-service-system/src/main/java/com/fs/store/service/IFsUserService.java
  47. 16 8
      fs-service-system/src/main/java/com/fs/store/service/impl/FsCityServiceImpl.java
  48. 84 29
      fs-service-system/src/main/java/com/fs/store/service/impl/FsUserServiceImpl.java
  49. 34 0
      fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserCountVO.java
  50. 27 0
      fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserRankingVO.java
  51. 62 0
      fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserStatisticsVO.java
  52. 19 0
      fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserSummaryCountTagVO.java
  53. 25 0
      fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserSummaryCountVO.java
  54. 9 2
      fs-service-system/src/main/resources/mapper/company/CompanyUserMapper.xml
  55. 1 1
      fs-service-system/src/main/resources/mapper/course/FsUserCourseMapper.xml
  56. 12 0
      fs-service-system/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml
  57. 9 5
      fs-service-system/src/main/resources/mapper/sop/SopUserLogsInfoMapper.xml
  58. 267 0
      fs-service-system/src/main/resources/mapper/store/FsUserMapper.xml
  59. 125 0
      fs-user-app/src/main/java/com/fs/app/controller/CourseH5Controller.java

+ 8 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyController.java

@@ -26,6 +26,7 @@ import com.fs.company.vo.CompanyVoiceCallerListVO;
 import com.fs.core.security.LoginUser;
 import com.fs.core.security.SecurityUtils;
 import com.fs.core.web.service.TokenService;
+import com.fs.his.vo.OptionsVO;
 import lombok.Synchronized;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -246,5 +247,12 @@ public class CompanyController extends BaseController
         return R.ok("提交成功,等待审核");
     }
 
+    @GetMapping("/allList")
+    public TableDataInfo allList()
+    {
+        List<OptionsVO> list = companyService.selectAllCompanyList();
+        return getDataTable(list);
+    }
+
 
 }

+ 6 - 0
fs-common/src/main/java/com/fs/common/core/domain/BaseEntity.java

@@ -4,6 +4,8 @@ import java.io.Serializable;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
+
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
@@ -17,6 +19,7 @@ public class BaseEntity implements Serializable
     private static final long serialVersionUID = 1L;
 
     /** 搜索值 */
+    @TableField(exist = false)
     private String searchValue;
 
     /** 创建者 */
@@ -38,13 +41,16 @@ public class BaseEntity implements Serializable
 
     /** 开始时间 */
     @JsonIgnore
+    @TableField(exist = false)
     private String beginTime;
 
     /** 结束时间 */
     @JsonIgnore
+    @TableField(exist = false)
     private String endTime;
 
     /** 请求参数 */
+    @TableField(exist = false)
     private Map<String, Object> params;
 
     public String getSearchValue()

+ 4 - 5
fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java

@@ -1,12 +1,10 @@
 package com.fs.app.controller;
 
-import com.alibaba.fastjson.JSON;
 import com.fs.app.annotation.Login;
 import com.fs.app.vo.CompanySubUserVO;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.bean.BeanUtils;
 import com.fs.company.domain.CompanyUser;
-import com.fs.company.param.BaseQueryParam;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.service.IFsCourseRedPacketLogService;
 import com.fs.course.service.IFsCourseWatchLogService;
@@ -38,9 +36,10 @@ public class CompanyUserController extends AppBaseController {
     @Login
     @ApiOperation("下级用户列表")
     @GetMapping("/getCompanyUserList")
-    public R getCompanyUserList(@RequestParam BaseQueryParam baseParam) {
-        log.debug("下级用户列表 param:{}", JSON.toJSONString(baseParam));
-        PageHelper.startPage(baseParam.getPageNum(), baseParam.getPageSize());
+    public R getCompanyUserList(@RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                                @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        log.debug("下级用户列表 pageNum: {}, pageSize: {}", pageNum, pageSize);
+        PageHelper.startPage(pageNum, pageSize);
         List<CompanyUser> companyUsers = companyUserService.selectCompanySubUserList(Long.parseLong(getUserId()));
         PageInfo<CompanyUser> page = new PageInfo<>(companyUsers);
 

+ 63 - 19
fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java

@@ -12,10 +12,7 @@ import com.fs.company.vo.CompanyQueryVo;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.param.h5.TagListParam;
 import com.fs.store.service.IFsUserService;
-import com.fs.store.vo.h5.CompanyUserTagListVO;
-import com.fs.store.vo.h5.FsUserPageListVO;
-import com.fs.store.vo.h5.UserDetailsVO;
-import com.fs.store.vo.h5.UserListPageVO;
+import com.fs.store.vo.h5.*;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -24,6 +21,9 @@ import io.swagger.annotations.ApiParam;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -59,7 +59,7 @@ public class FsUserController extends AppBaseController {
 
     @GetMapping("/allCompanyUser")
     @ApiOperation("获取所有公司销售")
-    public ResponseResult<List<CompanyUser>> getAllCompanyUser(){
+    public ResponseResult<List<CompanyUser>> getAllCompanyUser() {
         List<CompanyUser> companyUsers = companyUserService.selectAllCompanyUserByParentId(Long.parseLong(getUserId()));
         return ResponseResult.ok(companyUsers);
     }
@@ -76,7 +76,7 @@ public class FsUserController extends AppBaseController {
     @GetMapping("/details")
     @ApiOperation("用户会员详情")
     public ResponseResult<UserDetailsVO> getUserDetails(@ApiParam(value = "用户id", required = true) @RequestParam Long userId,
-                                                                   @ApiParam(value = "时间tab,不传表示查询全部,分别是:今天、昨天、前天、近七天", required = true) @RequestParam(required = false) String dateTag) {
+                                                        @ApiParam(value = "时间tab,不传表示查询全部,分别是:今天、昨天、前天、近七天", required = true) @RequestParam(required = false) String dateTag) {
         UserDetailsVO userDetails = fsUserService.getUserDetails(Long.parseLong(getUserId()), userId, dateTag);
         return ResponseResult.ok(userDetails);
     }
@@ -108,21 +108,65 @@ public class FsUserController extends AppBaseController {
         return ResponseResult.ok(r);
     }
 
-    /**
-     * 统计经销商会员总数和群管总数
-     */
     @Login
-    @ApiOperation("统计经销商会员总数和群管总数")
-    @PostMapping("/getCompanyUserAndUserCount")
-    public R getCompanyUserAndUserCount(@RequestBody CompanyQueryVo companyQueryVo)  {
-        ValidateUtil.validate(companyQueryVo.getStartTime(), companyQueryVo.getEndTime());
-        if (companyQueryVo.getType()==0){
-            companyQueryVo.setCompanyId(getCompanyId());
-        }else {
-            companyQueryVo.setCompanyUserId(Long.parseLong(getUserId()));
+    @GetMapping("/firstPage/summaryCount")
+    @ApiOperation("首页数据-顶部汇总统计")
+    public ResponseResult<FsUserSummaryCountVO> userSummaryCount() {
+        long userId = Long.parseLong(getUserId());
+        return ResponseResult.ok(fsUserService.userSummaryCount(userId));
+    }
+
+    @GetMapping("/firstPage/statistics")
+    @ApiOperation("首页数据-课程/答题/红包统计")
+    public ResponseResult<FsUserStatisticsVO> userStatistics(@ApiParam(value = "开始时间", required = true) @RequestParam String startTime,
+                                                             @ApiParam(value = "结束时间", required = true) @RequestParam String endTime) {
+        long userId = Long.parseLong(getUserId());
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String nowDate = dateFormat.format(new Date());
+        /*---------- 如果传入的日期是今天 ----------*/
+        FsUserStatisticsVO vo = fsUserService.userStatistics(userId, startTime, endTime, null, null);
+        if (nowDate.compareTo(startTime) > 0 && nowDate.compareTo(endTime) < 0) {
+            String yesterday = LocalDate.now().minusDays(1).toString();
+            FsUserStatisticsVO fsUserStatisticsVO = fsUserService.userStatistics(userId, yesterday + " 00:00:00", yesterday + " 23:59:59", null, null);
+            vo.setYesterdayVO(fsUserStatisticsVO);
         }
-        Map<String, Integer> companyUserAndUserCount = fsUserService.getCompanyUserAndUserCount(companyQueryVo);
-        return R.ok().put("data",companyUserAndUserCount);
+        return ResponseResult.ok(vo);
+    }
+
+    @GetMapping("/statistics/details")
+    @ApiOperation("数据统计详情-课程/答题/红包统计")
+    public ResponseResult<FsUserStatisticsVO> userStatisticsDetails(@ApiParam(value = "课程id") @RequestParam(required = false) String courseId,
+                                                                    @ApiParam(value = "视频id") @RequestParam(required = false) String videoId) {
+        long userId = Long.parseLong(getUserId());
+        FsUserStatisticsVO fsUserStatisticsVO = fsUserService.userStatisticsDetails(userId, courseId, videoId);
+        return ResponseResult.ok(fsUserStatisticsVO);
     }
 
+    @GetMapping("/firstPage/userRanking")
+    @ApiOperation("首页数据/详情-销售排行榜统计")
+    public ResponseResult<FsUserRankingVO> userRanking(
+            @ApiParam(value = "开始时间") @RequestParam String startTime,
+            @ApiParam(value = "结束时间") @RequestParam String endTime,
+            @ApiParam(value = "课程id") @RequestParam(required = false) String courseId,
+            @ApiParam(value = "视频id") @RequestParam(required = false) String videoId,
+            @ApiParam(value = "asc-正序,desc-倒序", required = true) @RequestParam String order,
+            @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam(required = false) Integer type
+            ) {
+        long userId = Long.parseLong(getUserId());
+        return ResponseResult.ok(fsUserService.userRanking(userId, startTime, endTime, courseId, videoId, order, type));
+    }
+
+    @GetMapping("/firstPage/courseRanking")
+    @ApiOperation("首页数据/详情-课程排行榜统计")
+    public ResponseResult<FsUserRankingVO> courseRanking(
+            @ApiParam(value = "开始时间") @RequestParam String startTime,
+            @ApiParam(value = "结束时间") @RequestParam String endTime,
+            @ApiParam(value = "课程id") @RequestParam(required = false) String courseId,
+            @ApiParam(value = "视频id") @RequestParam(required = false) String videoId,
+            @ApiParam(value = "asc-正序,desc-倒序", required = true) @RequestParam String order,
+            @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam(required = false) Integer type
+    ) {
+        long userId = Long.parseLong(getUserId());
+        return ResponseResult.ok();
+    }
 }

+ 31 - 6
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -1,11 +1,14 @@
 package com.fs.app.controller;
 
+import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.course.param.newfs.FsUserCourseListParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
 import com.fs.course.service.IFsUserCourseService;
 import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.vo.FsCourseAnalysisVO;
 import com.fs.course.vo.FsUserCourseListPVO;
+import com.fs.course.vo.newfs.FsUserCourseListVO;
 import com.fs.course.vo.newfs.FsUserCourseVideoDetailsVO;
 import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
 import com.github.pagehelper.PageHelper;
@@ -16,7 +19,9 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 
 @Api("课程库相关接口")
@@ -29,12 +34,12 @@ public class FsUserCourseVideoController extends AppBaseController {
     private IFsUserCourseVideoService fsUserCourseVideoService;
 
     @Autowired
-    private IFsUserCourseService  fsUserCourseService;
+    private IFsUserCourseService fsUserCourseService;
 
     @GetMapping("/pageList")
     @ApiOperation("课程分页列表")
     public ResponseResult<PageInfo<FsUserCourseVideoPageListVO>> list(UserCourseVideoPageParam param) {
-        PageHelper.startPage(param.getPageNum(),param.getPageSize());
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
         List<FsUserCourseVideoPageListVO> list = fsUserCourseVideoService.pageListCourseVideo(param);
         PageInfo<FsUserCourseVideoPageListVO> pageInfo = new PageInfo<>(list);
         return ResponseResult.ok(pageInfo);
@@ -48,11 +53,31 @@ public class FsUserCourseVideoController extends AppBaseController {
 
     @GetMapping("/courseList")
     @ApiOperation("获取课程下拉列表")
-    public ResponseResult<PageInfo<FsUserCourseListPVO>> getAllCompanyUser(FsUserCourseListParam param){
-        PageHelper.startPage(param.getPageNum(),param.getPageSize());
+    public ResponseResult<PageInfo<FsUserCourseListVO>> getAllCompanyUser(FsUserCourseListParam param) {
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
         param.setCompanyId(getCompanyId());
-        List<FsUserCourseListPVO> fsUserCourseList = fsUserCourseService.getFsUserCourseList(param);
-        PageInfo<FsUserCourseListPVO> pageInfo = new PageInfo<>(fsUserCourseList);
+        List<FsUserCourseListVO> fsUserCourseList = fsUserCourseService.getFsUserCourseList(param);
+        PageInfo<FsUserCourseListVO> pageInfo = new PageInfo<>(fsUserCourseList);
         return ResponseResult.ok(pageInfo);
     }
+
+    @ApiOperation("课程分析")
+    @GetMapping("/courseAnalysis")
+    public R courseAnalysis(@RequestParam(required = false) Long courseId,
+                            @RequestParam(required = false) Integer status,
+                            @RequestParam(required = false, value = "true") Boolean isToday,
+                            @RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                            @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+
+        Map<String,Object> params = new HashMap<>();
+        params.put("courseId", courseId);
+        params.put("companyId", getCompanyId());
+        params.put("isToday", isToday);
+        params.put("status", status);
+
+        // TODO: 查询课程
+        PageHelper.startPage(pageNum, pageSize);
+        List<FsCourseAnalysisVO> list = fsUserCourseVideoService.getCourseAnalysisByMap(params);
+        return R.ok().put("data", new PageInfo<>(list));
+    }
 }

+ 15 - 0
fs-company/src/main/java/com/fs/company/controller/CompanyController.java

@@ -7,9 +7,11 @@ import com.fs.common.utils.ServletUtils;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanySms;
 import com.fs.company.domain.CompanyVoice;
+import com.fs.company.param.CompanyParam;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanySmsService;
 import com.fs.company.service.ICompanyVoiceService;
+import com.fs.company.vo.CompanyNameVO;
 import com.fs.core.security.LoginUser;
 import com.fs.core.web.service.TokenService;
 import com.fs.his.vo.OptionsVO;
@@ -38,6 +40,19 @@ public class CompanyController extends BaseController
     @Autowired
     private TokenService tokenService;
 
+
+    /**
+     * 查询企业列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyParam param)
+    {
+        startPage();
+        List<CompanyNameVO> list = companyService.selectCompanyNameVOList(param);
+        return getDataTable(list);
+    }
+
+
     @GetMapping(value = "/getCompanyInfo")
     public R getCompanyInfo()
     {

+ 18 - 0
fs-company/src/main/java/com/fs/company/controller/CompanyUserController.java

@@ -9,6 +9,7 @@ import com.fs.common.core.domain.R;
 
 import com.fs.common.utils.DomainUtil;
 import com.fs.company.domain.*;
+import com.fs.company.param.CompanyUserAreaParam;
 import com.fs.company.param.CompanyUserQwParam;
 import com.fs.company.service.*;
 import com.fs.company.vo.CompanyUserQwListVO;
@@ -325,4 +326,21 @@ public class CompanyUserController extends BaseController
         return R.ok().put("data",list);
     }
 
+    /**
+     * 获取区域
+     */
+    @GetMapping("/getCitysAreaList")
+    public R getCitysAreaList(){
+        return R.ok().put("data",companyUserService.getCitysAreaList());
+    }
+
+    /**
+     * 批量修改 销售的所属区域(临时的)
+     */
+    @PostMapping("/updateCompanyUserAreaList")
+    public R updateCompanyUserAreaList(@RequestBody CompanyUserAreaParam param)
+    {
+        return companyUserService.updateCompanyUserAreaList(param);
+    }
+
 }

+ 168 - 53
fs-company/src/main/java/com/fs/qw/QwUserController.java

@@ -3,17 +3,23 @@ package com.fs.qw;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.constant.Constants;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.exception.user.UserPasswordNotMatchException;
+import com.fs.common.utils.MessageUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.company.service.ICompanyUserService;
+import com.fs.core.manager.AsyncManager;
+import com.fs.core.manager.factory.AsyncFactory;
 import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.mapper.FastGptRoleMapper;
 import com.fs.core.security.LoginUser;
@@ -38,10 +44,18 @@ import com.fs.voice.utils.StringUtil;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
 import org.springframework.web.bind.annotation.*;
 
+import javax.annotation.Resource;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * 企微用户Controller
@@ -78,6 +92,10 @@ public class QwUserController extends BaseController
 
     @Autowired
     private QwApiService qwApiService;
+
+    @Resource
+    private AuthenticationManager authenticationManager;
+
     /**
      * 查询企微员工列表
      */
@@ -95,8 +113,8 @@ public class QwUserController extends BaseController
 
 
     /**
-    * 直接授权key
-    */
+     * 直接授权key
+     */
     @PreAuthorize("@ss.hasPermi('qw:user:authAppKey')")
     @PostMapping("/authAppKey")
     public R authAppKey(@RequestBody QwUser param){
@@ -114,8 +132,8 @@ public class QwUserController extends BaseController
 
 
     /**
-    * 登录企业微信(发起登录)
-    */
+     * 登录企业微信(发起登录)
+     */
     @PreAuthorize("@ss.hasPermi('qw:user:login')")
     @PostMapping("/loginQwCode")
     public R loginQwCode(@RequestBody QwLoginParam loginParam){
@@ -140,8 +158,8 @@ public class QwUserController extends BaseController
     }
 
     /**
-    * 登录企业微信(传输验证信息)
-    */
+     * 登录企业微信(传输验证信息)
+     */
     @PreAuthorize("@ss.hasPermi('qw:user:login')")
     @PostMapping("/loginQwCodeMsg")
     public R loginQwCodeMsg(@RequestBody QwLoginParam loginParam){
@@ -167,8 +185,8 @@ public class QwUserController extends BaseController
 //    }
 //
     /**
-    * 查询企业微信登录状态
-    */
+     * 查询企业微信登录状态
+     */
     @PreAuthorize("@ss.hasPermi('qw:user:login')")
     @PostMapping("/getLoginQwStatus")
     public R getLoginQwStatus(@RequestBody QwLoginParam loginParam){
@@ -181,27 +199,62 @@ public class QwUserController extends BaseController
     }
 
     /**
-    * 企业微信员工账号 绑定 云主机
-    */
+     * 企业微信员工账号 绑定 云主机
+     */
     @PreAuthorize("@ss.hasPermi('qw:user:loginIp')")
+    @Log(title = "绑定 云主机", businessType = BusinessType.INSERT)
     @GetMapping("/qwBindCloudHost/{appKey}")
     public R qwBindCloudHost(@PathVariable("appKey") String appKey){
         return qwUserService.qwBindCloudHost(appKey);
     }
 
     /**
-    * 获取云主机的账密
+     * 获取云主机的账密
      *
-    */
+     */
     @PreAuthorize("@ss.hasPermi('qw:user:cloudAP')")
     @PostMapping("/selectCloudAP")
     public R selectCloudAP(@RequestBody QwCloudAPParam param) throws Exception {
         return qwUserService.selectCloudAP(param);
     }
+
+    /**
+     * 根据销售账号密码 获取 他的所有企业微信账号以及云主机和账号密码
+     */
+    @PostMapping("/selectCloudByCompany")
+    public R selectCloudByCompany(@RequestBody QwCloudIPByCompanyParam param) throws Exception {
+
+        // 用户验证
+        Authentication authentication = null;
+        try
+        {
+            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
+            authentication = authenticationManager
+                    .authenticate(new UsernamePasswordAuthenticationToken(param.getCompanyAdmin(), param.getCompanyPassWord()));
+        }
+        catch (Exception e)
+        {
+            if (e instanceof BadCredentialsException)
+            {
+                AsyncManager.me().execute(AsyncFactory.recordLogininfor(0l,param.getCompanyAdmin(), Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
+                throw new UserPasswordNotMatchException();
+            }
+            else
+            {
+                AsyncManager.me().execute(AsyncFactory.recordLogininfor(0l,param.getCompanyAdmin(), Constants.LOGIN_FAIL, e.getMessage()));
+                throw new ServiceException(e.getMessage());
+            }
+        }
+        LoginUser loginUser=(LoginUser) authentication.getPrincipal();
+
+        return qwUserService.selectCloudByCompany(loginUser.getUser().getCompanyId(),loginUser.getUser().getUserId());
+    }
+
+
     /**
      * 企业微信员工账号 绑定 云主机
      */
-//    @PreAuthorize("@ss.hasPermi('qw:user:loginIp')")
+    @PreAuthorize("@ss.hasPermi('qw:user:bindIp')")
     @GetMapping("/qwBindCloudHostByIp/{appKey}/{IP}")
     public R qwBindCloudHostByIp(@PathVariable("appKey") String appKey,@PathVariable("IP") String IP){
         return qwUserService.qwBindCloudHostByIp(appKey,IP);
@@ -211,6 +264,7 @@ public class QwUserController extends BaseController
      * 企业微信员工账号 解除绑定 云主机
      */
     @PreAuthorize("@ss.hasPermi('qw:user:loginIpOut')")
+    @Log(title = "解除绑定 云主机", businessType = BusinessType.UPDATE)
     @GetMapping("/qwUnbindCloudHost/{appKey}")
     public R qwUnbindCloudHost(@PathVariable("appKey") String appKey){
         return qwUserService.qwUnbindCloudHost(appKey);
@@ -277,8 +331,8 @@ public class QwUserController extends BaseController
         return util.exportExcel(list, "企微用户数据");
     }
     /**
-    * 查询企微员工列表-用于员工管理绑定
-    */
+     * 查询企微员工列表-用于员工管理绑定
+     */
     @GetMapping("/getQwUserList")
     public R getQwUserList()
     {
@@ -318,8 +372,8 @@ public class QwUserController extends BaseController
         return AjaxResult.success(qwUserService.selectQwUserVOById(id));
     }
     /**
-    * 批量查询 企微用户详细信息
-    */
+     * 批量查询 企微用户详细信息
+     */
     @GetMapping(value = "/getInfo/{ids}")
     public AjaxResult getInfoByIds(@PathVariable("ids") Long[] ids)
     {
@@ -364,7 +418,22 @@ public class QwUserController extends BaseController
         }
         return R.ok();
     }
-
+    @RepeatSubmit
+    @PreAuthorize("@ss.hasPermi('qw:user:sync')")
+    @Log(title = "企微用户", businessType = BusinessType.INSERT)
+    @PostMapping("syncName/{corpId}")
+    public R syncName(@PathVariable String corpId)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Company company = loginUser.getCompany();
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(company.getCompanyId());
+        for (String string : strings) {
+            if (string.equals(corpId)){
+                qwUserService.syncQwUserName(string);
+            }
+        }
+        return R.ok();
+    }
     /**
      * 绑定AI客服
      */
@@ -380,11 +449,11 @@ public class QwUserController extends BaseController
         FastGptRole role = fastGptRoleMapper.selectFastGptRoleByRoleId(param.getFastGptRoleId());
 
         if (role.getBindCorpId()!=null){
-           if (role.getBindCorpId().equals(param.getCorpId())){
-               qwUserService.updateQwUser(qwUser);
-           }else {
-               return R.error("该角色已绑定其他企业");
-           }
+            if (role.getBindCorpId().equals(param.getCorpId())){
+                qwUserService.updateQwUser(qwUser);
+            }else {
+                return R.error("该角色已绑定其他企业");
+            }
         }else {
             int i = qwUserService.updateQwUser(qwUser);
 
@@ -421,48 +490,83 @@ public class QwUserController extends BaseController
     @Log(title = "企微用户", businessType = BusinessType.UPDATE)
     @PutMapping("/bindQwUser")
     @RepeatSubmit
-    public R bindQwUser(@RequestBody QwUserBingParam qwUser)
+    public R bindQwUser(@RequestBody QwUserBingParam qwUserParam)
     {
-        CompanyUser companyUser = companyUserService.selectCompanyUserById(qwUser.getCompanyUserId());
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(qwUserParam.getCompanyUserId());
+
+        String qwUserIdCompanyStr = companyUser.getQwUserId();
+        Set<String> qwUserIdCompanySet = new HashSet<>();
+        if (!StringUtil.strIsNullOrEmpty(qwUserIdCompanyStr)) {
+            String[] qwUserId = qwUserIdCompanyStr.split(",");
+            qwUserIdCompanySet = Arrays.stream(qwUserId)
+                    .filter(id -> !id.isEmpty())
+                    .collect(Collectors.toSet());
+        }
+
         if (companyUser!=null){
 
             //选择的企业微信账号为“”
-            if (StringUtil.strIsNullOrEmpty(qwUser.getId())&&qwUser.getCompanyUserId()!=null){
+            if (StringUtil.strIsNullOrEmpty(qwUserParam.getId())&&qwUserParam.getCompanyUserId()!=null){
                 //制空企业微信的绑定
-                qwUserService.updateUserByUserId(qwUser.getCompanyUserId());
+                qwUserService.updateUserByUserId(qwUserParam.getCompanyUserId());
 
                 //制空销售的绑定
                 companyUserMapper.updateCompanyUserByNullQwUserID(companyUser.getUserId());
 
             }else {
-                String id = qwUser.getId();
-                String[] split = id.split(",");
-                for (String s : split) {
-                    QwUser qu= qwUserService.selectQwUserById(Long.parseLong(s));
-                    if (qu.getCompanyUserId()!=null&&!qu.getCompanyUserId().equals(qwUser.getCompanyUserId())){
+                String idParam = qwUserParam.getId();
+                String[] splitParam = idParam.split(",");
+                Set<String> splitParamSet = Arrays.stream(splitParam)
+                        .filter(s -> !s.isEmpty())
+                        .collect(Collectors.toSet());
+
+                // 找出在 qwUserIdCompanySet 中存在但在 splitParamSet 中不存在的元素
+                Set<String> difference = new HashSet<>(qwUserIdCompanySet);
+                difference.removeAll(splitParamSet);
+
+                //制空绑定
+                if (!difference.isEmpty()){
+                    difference.forEach(id->{
+                        qwUserService.updateUnBindUserById(id);
+                    });
+
+                }
+
+
+                for (String paramId : splitParamSet) {
+                    QwUser qu= qwUserService.selectQwUserById(Long.parseLong(paramId));
+                    if (qu.getCompanyUserId()!=null&&!qu.getCompanyUserId().equals(qwUserParam.getCompanyUserId())){
                         return R.error( qu.getQwUserName()+"已经被其他人绑定,请先解绑");
                     }
-                }
-                //制空绑定
-                qwUserService.updateUserByUserId(qwUser.getCompanyUserId());
-                CompanyUser user = new CompanyUser();
-                user.setQwUserId(qwUser.getId());
-                user.setUserId(companyUser.getUserId());
-                user.setQwStatus(1);
-                companyUserMapper.updateCompanyUser(user);
-                for (String s : split) {
+
+
+                    CompanyUser user = new CompanyUser();
+                    user.setQwUserId(qwUserParam.getId());
+                    user.setUserId(companyUser.getUserId());
+                    user.setQwStatus(1);
+                    companyUserMapper.updateCompanyUser(user);
+
+
                     QwUser qw = new QwUser();
-                    qw.setCompanyUserId(qwUser.getCompanyUserId());
-                    qw.setId(Long.parseLong(s));
+                    qw.setCompanyUserId(qwUserParam.getCompanyUserId());
+                    qw.setId(Long.parseLong(paramId));
                     qw.setStatus(1);
                     qw.setCompanyId(companyUser.getCompanyId());
                     qwUserService.updateQwUser(qw);
-                    QwUser qu= qwUserService.selectQwUserById(Long.parseLong(s));
 
-                    qwExternalContactMapper.updateBindUserByQwUser(qu.getCorpId(),qu.getQwUserId(),companyUser.getCompanyId(),companyUser.getUserId());
+                }
+
+                //同步最后单独跑/先不异步了pp
+                for (String paramId : splitParamSet){
+
+                    //如果销售没绑定过,全刷,否则只同步新的增加的
+                    if (StringUtil.strIsNullOrEmpty(qwUserIdCompanyStr)){
+                        updateAndSyncQwUser(paramId, companyUser);
 
-                    //根据企微账号和公司id 同步企业微信账号(游标初始为空)
-                    syncMyQwExternalContact(qu,null);
+                    }
+                    else if (!StringUtil.strIsNullOrEmpty(qwUserIdCompanyStr) && !qwUserIdCompanySet.contains(paramId)){
+                        updateAndSyncQwUser(paramId, companyUser);
+                    }
                 }
             }
 
@@ -474,6 +578,17 @@ public class QwUserController extends BaseController
 
     }
 
+
+    private void updateAndSyncQwUser(String paramId, CompanyUser companyUser) {
+
+        QwUser qu= qwUserService.selectQwUserById(Long.parseLong(paramId));
+
+        qwExternalContactMapper.updateBindUserByQwUser(qu.getCorpId(),qu.getQwUserId(),companyUser.getCompanyId(),companyUser.getUserId());
+
+        //根据企微账号和公司id 同步企业微信账号(游标初始为空)
+        syncMyQwExternalContact(qu,null);
+    }
+
     /** 修改企微用户的欢迎语 */
     @PostMapping("/weclomeQwUser")
     public R weclomeQwUser(@RequestBody QwUser qwUser) throws Exception {
@@ -487,7 +602,7 @@ public class QwUserController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('qw:user:remove')")
     @Log(title = "企微用户", businessType = BusinessType.DELETE)
-	@DeleteMapping("/{ids}")
+    @DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)
     {
         return toAjax(qwUserService.deleteQwUserByIds(ids));
@@ -495,8 +610,8 @@ public class QwUserController extends BaseController
 
 
     /**
-    * 获取企业微信用户列表
-    */
+     * 获取企业微信用户列表
+     */
     @GetMapping("/qwUserList/{corpId}")
     public TableDataInfo qwUserList(@PathVariable String corpId)
     {
@@ -591,9 +706,9 @@ public class QwUserController extends BaseController
             return R.error("同步失败:"+list.getErrmsg());
         }
 
-       if (!StringUtil.strIsNullOrEmpty(list.getNext_cursor())){
-           syncMyQwExternalContact(qwUser,list.getNext_cursor());
-       }
+        if (!StringUtil.strIsNullOrEmpty(list.getNext_cursor())){
+            syncMyQwExternalContact(qwUser,list.getNext_cursor());
+        }
         return R.ok();
     }
 }

+ 10 - 0
fs-company/src/main/resources/application-druid.yml

@@ -125,3 +125,13 @@ spring:
                     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

+ 2 - 1
fs-company/src/main/resources/application.yml

@@ -43,7 +43,8 @@ spring:
     # 国际化资源文件路径
     basename: i18n/messages
   profiles:
-    active: dev
+#    active: dev
+    active: druid
     include: config
   # 文件上传
   servlet:

+ 2 - 1
fs-qw-task/src/main/java/com/fs/FsQwTaskApplication.java

@@ -4,6 +4,7 @@ import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.transaction.annotation.EnableTransactionManagement;
 
 /**
@@ -12,7 +13,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
 @EnableTransactionManagement
 @EnableAsync
-//@EnableScheduling
+@EnableScheduling
 public class FsQwTaskApplication
 {
     public static void main(String[] args){

+ 11 - 1
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java

@@ -271,6 +271,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         long startTimeMillis = System.currentTimeMillis();
         log.info("====== 开始选择和处理 SOP 用户日志 ======");
 
+        CourseConfig config;
+        synchronized(configLock) {
+            config = cachedCourseConfig;
+        }
         List<SopUserLogsVo> sopUserLogsVos = sopUserLogsMapper.selectSopUserLogsListByTime();
         if (sopUserLogsVos.isEmpty()) {
             log.info("没有需要处理的 SOP 用户日志。");
@@ -303,7 +307,13 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     Long companyUserId = Long.parseLong(userKey[1]);
                     CompanyUser companyUser = companyUserList.stream().filter(cu -> Objects.equals(cu.getUserId(), companyUserId)).findFirst().orElse(null);
                     if (Objects.nonNull(companyUser)) {
-                        s.setDomain(companyUser.getDomain());
+                        if (StringUtils.isNotBlank(companyUser.getDomain())) {
+                            s.setDomain(companyUser.getDomain());
+                        } else {
+                            s.setDomain(config.getRealLinkDomainName());
+                        }
+                    } else {
+                        s.setDomain(config.getRealLinkDomainName());
                     }
                 })
                 .collect(Collectors.groupingBy(SopUserLogsVo::getSopId));

+ 2 - 2
fs-qw-task/src/main/resources/application.yml

@@ -44,7 +44,7 @@ spring:
     basename: i18n/messages
   profiles:
 #    active: druid-yjf
-    active: druid
+    active: dev
     include: config
   # 文件上传
   servlet:
@@ -69,7 +69,7 @@ token:
   expireTime: 180
 mybatis-plus:
   # 搜索指定包别名
-  typeAliasesPackage: com.fs.**.domain,com.fs.**.bo
+  typeAliasesPackage: com.fs.**.domain,com.fs.**.bo,com.fs.**.vo
   # 配置mapper的扫描,找到所有的mapper.xml映射文件
   mapperLocations: classpath*:/mapper/**/*.xml
   configLocation: classpath:mybatis/mybatis-config.xml

+ 10 - 0
fs-service-system/src/main/java/com/fs/company/domain/CompanyUser.java

@@ -141,6 +141,16 @@ public class CompanyUser extends BaseEntity
     /** 域名 */
     private String domain;
 
+    private String addressId;
+
+    public String getAddressId() {
+        return addressId;
+    }
+
+    public void setAddressId(String addressId) {
+        this.addressId = addressId;
+    }
+
     @NotBlank(message = "二级域名不能为空")
     @Pattern(regexp = "^(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,})|(?:\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}))$", message = "域名格式不正确")
     public String getDomain() {

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

@@ -5,6 +5,7 @@ import java.util.List;
 import com.fs.company.domain.Company;
 import com.fs.company.param.CompanyParam;
 import com.fs.company.vo.CompanyCrmVO;
+import com.fs.company.vo.CompanyNameVO;
 import com.fs.company.vo.CompanyVO;
 import com.fs.his.vo.OptionsVO;
 import org.apache.ibatis.annotations.Param;
@@ -76,7 +77,6 @@ public interface CompanyMapper
             "<if test = 'maps.companyName != null and  maps.companyName !=\"\"     '> " +
             "and c.company_name like CONCAT('%',#{maps.companyName},'%') " +
             "</if>" +
-
             "<if test = 'maps.status != null   '> " +
             "and c.status = #{maps.status}" +
             "</if>" +
@@ -84,6 +84,23 @@ public interface CompanyMapper
             "</script>"})
     List<CompanyVO> selectCompanyVOList(@Param("maps")CompanyParam param);
 
+    @Select({"<script> " +
+            "select c.* " +
+            "from company c " +
+            "where is_del=0 " +
+            "<if test = 'maps.companyId != null  '> " +
+            "and c.company_id = #{maps.companyId}" +
+            "</if>" +
+            "<if test = 'maps.companyName != null and  maps.companyName !=\"\"     '> " +
+            "and c.company_name like CONCAT('%',#{maps.companyName},'%') " +
+            "</if>" +
+            "<if test = 'maps.status != null   '> " +
+            "and c.status = #{maps.status}" +
+            "</if>" +
+            "order by c.company_id asc " +
+            "</script>"})
+    List<CompanyNameVO> selectCompanyNameVOList(@Param("maps")CompanyParam param);
+
     @Select({"<script> " +
             "select c.*,a.api_name as voice_api_name,u.user_name " +
             " ,(select count(1) from crm_customer cc where c.company_id= cc.company_id and date_format(cc.update_time,'%y%m%d') = date_format(now(),'%y%m%d') ) as now_day_customer_count " +

+ 5 - 2
fs-service-system/src/main/java/com/fs/company/mapper/CompanyUserMapper.java

@@ -252,7 +252,7 @@ public interface CompanyUserMapper
      * @param ids 销售ID集合
      * @return  list
      */
-    List<CompanyUser> selectCompanyUserByIds(Set<Long> ids);
+    List<CompanyUser> selectCompanyUserByIds(@Param("ids") Set<Long> ids);
 
     List<CompanyUser> selectAllCompanyUserByParentId(Long parentId);
 
@@ -260,8 +260,11 @@ public interface CompanyUserMapper
      * 批量审核用户
      * @param userIds 用户ID集合
      */
-    void auditUsers(List<Long> userIds);
+    void auditUsers(@Param("userIds") List<Long> userIds);
 
     List<CompanyUser> getAllUserListLimit(@Param("companyId") Long companyId,
                                           @Param("keywords") String keywords);
+
+    @Select("select domain from company_user where user_id = #{userId}")
+    String selectDomainByUserId(Long userId);
 }

+ 4 - 0
fs-service-system/src/main/java/com/fs/company/service/ICompanyService.java

@@ -8,6 +8,7 @@ import com.fs.common.core.domain.R;
 import com.fs.company.domain.Company;
 import com.fs.company.param.CompanyParam;
 import com.fs.company.vo.CompanyCrmVO;
+import com.fs.company.vo.CompanyNameVO;
 import com.fs.company.vo.CompanyVO;
 import com.fs.store.domain.FsStoreOrder;
 import com.fs.store.domain.FsStorePayment;
@@ -72,6 +73,9 @@ public interface ICompanyService
 
     List<CompanyVO> selectCompanyVOList(CompanyParam param);
 
+    List<CompanyNameVO> selectCompanyNameVOList(CompanyParam param);
+
+
     List<CompanyCrmVO> selectCompanyCrmDayCountList(CompanyParam param);
 
     Integer selectCompanyCount();

+ 7 - 0
fs-service-system/src/main/java/com/fs/company/service/ICompanyUserService.java

@@ -1,11 +1,14 @@
 package com.fs.company.service;
 
+import com.fs.common.core.domain.R;
 import com.fs.company.domain.CompanyUser;
+import com.fs.company.param.CompanyUserAreaParam;
 import com.fs.company.param.CompanyUserQwParam;
 import com.fs.company.vo.CompanyQwUserByIdsVo;
 import com.fs.company.vo.CompanyUserQwListVO;
 import com.fs.company.vo.CompanyUserVO;
 import com.fs.company.vo.DocCompanyUserVO;
+import com.fs.his.vo.CitysAreaVO;
 import com.fs.qw.vo.CompanyUserQwVO;
 import com.fs.qw.vo.QwUserVO;
 import com.fs.his.vo.OptionsVO;;
@@ -151,4 +154,8 @@ public interface ICompanyUserService {
     List<CompanyUser> selectCompanySubUserList(Long companyUserId);
 
     List<CompanyUser> getAllUserListLimit(Long companyId, String keywords);
+
+    List<CitysAreaVO> getCitysAreaList();
+
+    R updateCompanyUserAreaList(CompanyUserAreaParam param);
 }

+ 6 - 0
fs-service-system/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -10,6 +10,7 @@ import com.fs.company.domain.*;
 import com.fs.company.mapper.*;
 import com.fs.company.param.CompanyParam;
 import com.fs.company.vo.CompanyCrmVO;
+import com.fs.company.vo.CompanyNameVO;
 import com.fs.company.vo.CompanyVO;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.store.config.StoreConfig;
@@ -208,6 +209,11 @@ public class CompanyServiceImpl implements ICompanyService
         return companyMapper.selectCompanyVOList(param);
     }
 
+    @Override
+    public List<CompanyNameVO> selectCompanyNameVOList(CompanyParam param) {
+        return companyMapper.selectCompanyNameVOList(param);
+    }
+
     @Override
     public List<CompanyCrmVO> selectCompanyCrmDayCountList(CompanyParam param) {
         return companyMapper.selectCompanyCrmDayCountList(param);

+ 22 - 0
fs-service-system/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java

@@ -1,17 +1,21 @@
 package com.fs.company.service.impl;
 
 import com.fs.common.annotation.DataScope;
+import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.*;
 import com.fs.company.mapper.*;
+import com.fs.company.param.CompanyUserAreaParam;
 import com.fs.company.param.CompanyUserQwParam;
 import com.fs.company.param.CompanyVoiceCallerParam;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.vo.*;
+import com.fs.his.vo.CitysAreaVO;
 import com.fs.qw.vo.CompanyUserQwVO;
 import com.fs.qw.vo.QwUserVO;
 import com.fs.his.vo.OptionsVO;;
+import com.fs.store.service.IFsCityService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -45,6 +49,9 @@ public class CompanyUserServiceImpl implements ICompanyUserService
     @Autowired
     private CompanyVoiceCallerMapper callerMapper;
 
+    @Autowired
+    public IFsCityService iFsCityService;
+
     /**
      * 查询物业公司管理员信息
      *
@@ -407,6 +414,21 @@ public class CompanyUserServiceImpl implements ICompanyUserService
         return companyUserMapper.getAllUserListLimit(companyId,keywords);
     }
 
+    @Override
+    public List<CitysAreaVO> getCitysAreaList() {
+        return iFsCityService.getCitysArea();
+    }
+
+    @Override
+    public R updateCompanyUserAreaList(CompanyUserAreaParam param) {
+        int i = companyUserMapper.updateCompanyUserAreaList(param);
+        if (i > 0) {
+            return R.ok("操作成功");
+        }else {
+            return R.error("操作失败");
+        }
+    }
+
     /**
      * 批量审核用户
      * @param userIds 用户ID集合

+ 20 - 0
fs-service-system/src/main/java/com/fs/company/vo/CompanyNameVO.java

@@ -0,0 +1,20 @@
+package com.fs.company.vo;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+@Data
+public class CompanyNameVO {
+
+    /** ID */
+    private Long companyId;
+
+    /** 企业名 */
+    @Excel(name = "企业名")
+    private String companyName;
+
+    /** 状态 */
+    @Excel(name = "状态")
+    private Integer status;
+
+}

+ 2 - 1
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java

@@ -7,6 +7,7 @@ import com.fs.course.param.FsUserCourseListUParam;
 import com.fs.course.param.FsUserCourseParam;
 import com.fs.course.param.newfs.FsUserCourseListParam;
 import com.fs.course.vo.*;
+import com.fs.course.vo.newfs.FsUserCourseListVO;
 import com.fs.his.vo.OptionsVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
@@ -228,6 +229,6 @@ public interface FsUserCourseMapper
     @Select("select course_id ,img_url   from fs_user_course where is_del = 0 and is_private = 1")
     List<FsUserCourse> selectFsUserCourseAllCourseByQw();
 
-    List<FsUserCourseListPVO> getFsUserCourseList(FsUserCourseListParam param);
+    List<FsUserCourseListVO> getFsUserCourseList(FsUserCourseListParam param);
 
 }

+ 9 - 0
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java

@@ -1,10 +1,13 @@
 package com.fs.course.mapper;
 
 import java.util.List;
+import java.util.Map;
+
 import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.param.FsUserCourseVideoListUParam;
 import com.fs.course.param.FsUserCourseVideoParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
+import com.fs.course.vo.FsCourseAnalysisVO;
 import com.fs.course.vo.FsUserCourseVideoListUVO;
 import com.fs.course.vo.FsUserCourseVideoVO;
 import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
@@ -164,4 +167,10 @@ public interface FsUserCourseVideoMapper
 
     List<FsUserCourseVideoPageListVO> selectFsUserCourseVideoPageList(UserCourseVideoPageParam param);
 
+    /**
+     * 获取课程分析数据
+     * @param params 参数
+     * @return list
+     */
+    List<FsCourseAnalysisVO> getCourseAnalysisByMap(@Param("params") Map<String, Object> params);
 }

+ 3 - 1
fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseService.java

@@ -1,6 +1,7 @@
 package com.fs.course.service;
 
 import java.util.List;
+import java.util.Map;
 
 import com.fs.common.core.domain.R;
 import com.fs.course.domain.FsUserCourse;
@@ -10,6 +11,7 @@ import com.fs.course.param.FsUserCourseListUParam;
 import com.fs.course.param.FsUserCourseParam;
 import com.fs.course.param.newfs.FsUserCourseListParam;
 import com.fs.course.vo.*;
+import com.fs.course.vo.newfs.FsUserCourseListVO;
 import com.fs.his.vo.OptionsVO;
 
 /**
@@ -108,5 +110,5 @@ public interface IFsUserCourseService
      * h5 获取用户课程下拉列表
      * @return
      */
-    List<FsUserCourseListPVO> getFsUserCourseList(FsUserCourseListParam param);
+    List<FsUserCourseListVO> getFsUserCourseList(FsUserCourseListParam param);
 }

+ 8 - 1
fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java

@@ -5,6 +5,7 @@ import com.fs.common.core.domain.ResponseResult;
 import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.param.*;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
+import com.fs.course.vo.FsCourseAnalysisVO;
 import com.fs.course.vo.FsUserCourseVideoListUVO;
 import com.fs.course.vo.newfs.FsUserCourseVideoDetailsVO;
 import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
@@ -12,6 +13,7 @@ import com.fs.course.vo.FsUserCourseVideoQVO;
 import com.fs.course.vo.FsUserCourseVideoVO;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 课堂视频Service接口
@@ -104,5 +106,10 @@ public interface IFsUserCourseVideoService
      */
     ResponseResult<FsUserCourseVideoDetailsVO> getVideoDetails(Long videoId);
 
-    R registerCourse(FsUserCourseRegisterParam param);
+    /**
+     * 获取课程分析数据
+     * @param params 参数
+     * @return list
+     */
+    List<FsCourseAnalysisVO> getCourseAnalysisByMap(Map<String, Object> params);
 }

+ 25 - 29
fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java

@@ -5,6 +5,9 @@ import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCourseDomainName;
 import com.fs.course.domain.FsCourseLink;
@@ -12,14 +15,12 @@ import com.fs.course.domain.FsCourseRealLink;
 import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.mapper.FsCourseDomainNameMapper;
 import com.fs.course.mapper.FsCourseLinkMapper;
-import com.fs.course.mapper.FsCourseSopAppLinkMapper;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
 import com.fs.course.param.FsCourseLinkCreateParam;
 import com.fs.course.service.IFsCourseDomainNameService;
 import com.fs.course.service.IFsCourseLinkService;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwUserMapper;
-import com.fs.qw.vo.QwSopCourseFinishTempSetting;
 import com.fs.system.service.ISysConfigService;
 import lombok.Synchronized;
 import lombok.extern.slf4j.Slf4j;
@@ -51,10 +52,6 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
 {
     @Autowired
     private FsCourseLinkMapper fsCourseLinkMapper;
-
-    @Autowired
-    private  FsCourseSopAppLinkMapper fsCourseSopAppLinkMapper;
-
     @Autowired
     private ISysConfigService configService;
 
@@ -64,6 +61,8 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
     private QwUserMapper qwUserMapper;
     @Autowired
     private FsUserCourseVideoMapper courseVideoMapper;
+    @Autowired
+    private CompanyUserMapper companyUserMapper;
 
 
     /**
@@ -141,7 +140,6 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
 
     private static final String realLink = "/courseh5/pages/course/learning?course=";
     private static final String miniappRealLink = "/pages_course/video?course=";
-    private static final String appRealLink = "/pages/courseAnswer/index?link=";
 
 
     /**
@@ -179,19 +177,6 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
         Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());
         link.setUpdateTime(updateTime);
 
-//        Date now = new Date();
-//        // 获取明天的日期
-//        Date tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
-//        // 设置时间为0点
-//        Date tomorrowMidnight = new Date(tomorrow.getYear(), tomorrow.getMonth(), tomorrow.getDate());
-
-//        LocalDateTime now = LocalDateTime.now();
-//        // 增加十天,并设置时间为午夜
-//        LocalDateTime tenDaysLater = now.plusDays(10).withHour(0).withMinute(0).withSecond(0).withNano(0);
-//
-//        // 转换为 Date 类型
-//        Date tenDaysMidnight = Date.from(tenDaysLater.atZone(ZoneId.systemDefault()).toInstant());
-
         // 设置过期时间
         link.setUpdateTime(updateTime);
 
@@ -202,8 +187,8 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
             String course = JSON.toJSONString(courseMap);
             link.setRealLink(realLink+course);
             if (fsCourseLinkMapper.updateFsCourseLink(link)>0){
-                FsCourseDomainName fsCourseDomainName = fsCourseDomainNameMapper.selectFsCourseDomainNameByRandom();
-                String sortLink = "https://"+fsCourseDomainName.getDomainName() + "/s/" + link.getLink();
+                String domainName = getDomainName(param.getCompanyUserId(), config);
+                String sortLink = domainName + "/s/" + link.getLink();
                 return R.ok("短链生成").put("url",sortLink);
             }
         }
@@ -245,8 +230,8 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
         calendar.add(Calendar.DAY_OF_MONTH, expireDays);
         link.setUpdateTime(calendar.getTime());
         if (fsCourseLinkMapper.insertFsCourseLink(link)>0){
-            FsCourseDomainName fsCourseDomainName = fsCourseDomainNameMapper.selectFsCourseDomainNameByRandom();
-            String sortLink = "https://"+fsCourseDomainName.getDomainName() + "/s/" + link.getLink();
+            String domainName = getDomainName(param.getCompanyUserId(), config);
+            String sortLink = domainName + "/s/" + link.getLink();
             return R.ok("短链生成").put("url",sortLink);
         }
         return R.error("短链生成失败!");
@@ -254,7 +239,6 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
 
     @Override
     public String createLinkUrlWcmini(FsCourseLinkCreateParam param) {
-
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
 
@@ -278,6 +262,7 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
         return link.getRealLink();
     }
 
+
     public FsCourseLink createFsCourseLink(String corpId, Date sendTime,Long courseId,Long videoId, String qwUserId,
                                            Long companyUserId, Long companyId,Long externalId,Integer type){
         // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
@@ -297,6 +282,14 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
         return link;
     }
 
+    private String getDomainName(Long companyUserId,CourseConfig config){
+        String domainName = companyUserMapper.selectDomainByUserId(companyUserId);
+        if (StringUtils.isEmpty(domainName)){
+            domainName = config.getRealLinkDomainName();
+        }
+        return domainName;
+    }
+
     private Date createUpdateTime(Integer expiresDays, Date sendTime, CourseConfig config){
 
         Integer expireDays = (expiresDays == null || expiresDays == 0)
@@ -335,7 +328,6 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
 
             // 从数据库中查找短链对应的真实链接
             FsCourseLink courseLink = fsCourseLinkMapper.selectFsCourseLinkByLink(sLink);
-
             if (courseLink != null) {
                 // 获取当前时间
                 Date currentTime = new Date();
@@ -354,9 +346,13 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
                         return R.error("真实链接不存在").put("realLink", sLink);
                     }
                     log.info("链接仍然有效: {}", sLink);
-                    String json = configService.selectConfigByKey("course.config");
-                    CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-                    String realLink = config.getRealLinkDomainName()+courseLink.getRealLink();
+                    String domainName = companyUserMapper.selectDomainByUserId(courseLink.getCompanyUserId());
+                    if (StringUtils.isEmpty(domainName)){
+                        String json = configService.selectConfigByKey("course.config");
+                        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+                        domainName = config.getRealLinkDomainName();
+                    }
+                    String realLink = domainName+courseLink.getRealLink();
                     return R.ok().put("realLink",realLink );
                 }
             } else {

+ 4 - 4
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java

@@ -17,13 +17,13 @@ import com.fs.course.param.FsUserCourseParam;
 import com.fs.course.param.newfs.FsUserCourseListParam;
 import com.fs.course.service.IFsUserCourseService;
 import com.fs.course.vo.*;
+import com.fs.course.vo.newfs.FsUserCourseListVO;
 import com.fs.his.vo.OptionsVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
+
+import java.util.*;
 
 /**
  * 课程Service业务层处理
@@ -365,7 +365,7 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
     }
 
     @Override
-    public List<FsUserCourseListPVO> getFsUserCourseList(FsUserCourseListParam param) {
+    public List<FsUserCourseListVO> getFsUserCourseList(FsUserCourseListParam param) {
         return fsUserCourseMapper.getFsUserCourseList(param);
     }
 }

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

@@ -16,6 +16,7 @@ import com.fs.course.mapper.*;
 import com.fs.course.param.*;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
 import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.vo.FsCourseAnalysisVO;
 import com.fs.course.vo.FsUserCourseVideoListUVO;
 import com.fs.course.vo.FsUserCourseVideoQVO;
 import com.fs.course.vo.FsUserCourseVideoVO;
@@ -740,6 +741,16 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         return ResponseResult.ok(fsUserCourseVideoDetailsVO);
     }
 
+    /**
+     * 获取课程分析数据
+     * @param params 参数
+     * @return list
+     */
+    @Override
+    public List<FsCourseAnalysisVO> getCourseAnalysisByMap(Map<String, Object> params) {
+        return fsUserCourseVideoMapper.getCourseAnalysisByMap(params);
+    }
+
     @Autowired
     SopUserLogsInfoMapper sopUserLogsInfoMapper;
 

+ 61 - 0
fs-service-system/src/main/java/com/fs/course/vo/FsCourseAnalysisVO.java

@@ -0,0 +1,61 @@
+package com.fs.course.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class FsCourseAnalysisVO {
+    /**
+     * 课程ID
+     */
+    private Long courseId;
+    /**
+     * 课程名称
+     */
+    private String courseName;
+    /**
+     * 标题
+     */
+    private String title;
+    /**
+     * 封面
+     */
+    private String imgUrl;
+    /**
+     * 课程描述
+     */
+    private String description;
+    /**
+     * 观看人数
+     */
+    private Long viewUsers;
+    /**
+     * 完播人数
+     */
+    private Long finishUsers;
+    /**
+     * 完播率
+     */
+    private Long finishRate;
+    /**
+     * 答题红包数
+     */
+    private Long redPacketCount;
+    /**
+     * 答题红包金额
+     */
+    private BigDecimal redPacketAmount;
+    /**
+     * 答题人数
+     */
+    private Long answerUsers;
+    /**
+     * 正确人数
+     */
+    private Long correctUsers;
+    /**
+     * 正确率
+     */
+    private Long correctRate;
+}

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

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

+ 16 - 0
fs-service-system/src/main/java/com/fs/his/vo/CitysAreaVO.java

@@ -0,0 +1,16 @@
+package com.fs.his.vo;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+@Data
+public class CitysAreaVO {
+    /** 地址编码 */
+    @Excel(name = "地址编码")
+    private String cityId;
+
+    /** 地区名称 */
+    @Excel(name = "地区名称")
+    private String cityName;
+
+}

+ 1 - 1
fs-service-system/src/main/java/com/fs/qw/domain/QwExternalContact.java

@@ -120,6 +120,6 @@ public class QwExternalContact extends BaseEntity
     private Date firstTime;
 
     private Integer lastWatchTime;
-    private Integer repeat;
+    private Integer isRepeat;
 
 }

+ 28 - 0
fs-service-system/src/main/java/com/fs/qw/dto/QwUserByToolDTO.java

@@ -0,0 +1,28 @@
+package com.fs.qw.dto;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+@Data
+public class QwUserByToolDTO {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 企微用户id */
+    @Excel(name = "企微用户id")
+    private String qwUserId;
+
+    /** 企微用户名 */
+    @Excel(name = "企微用户名")
+    private String qwUserName;
+
+    /**
+     * 地址
+     */
+    private  String loginCodeUrl;
+
+
+    private String apAdmin;
+
+    private String apPassword;
+}

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

@@ -2,6 +2,7 @@ package com.fs.qw.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.qw.domain.QwUser;
+import com.fs.qw.dto.QwUserByToolDTO;
 import com.fs.qw.param.QwFsServerBindParam;
 import com.fs.qw.param.QwUserListParam;
 import com.fs.qw.param.QwUserParam;
@@ -335,4 +336,11 @@ public interface QwUserMapper extends BaseMapper<QwUser>
 
     @Select("select company_id from qw_user where corp_id = #{corpId} and qw_user_id = #{qwUserid}")
     Long getQwUserCompanyId(@Param("corpId") String corpId, @Param("qwUserid") String qwUserid);
+
+    @Select("select qw_user_id,qw_user_name,login_code_url from qw_user" +
+            " where company_id=#{companyId} " +
+            "and company_user_id =#{userId} " +
+            "and is_del=0 " +
+            "and login_code_url is not null ")
+    public List<QwUserByToolDTO> selectQwUserListByTools(@Param("companyId") Long companyId , @Param("userId") Long userId);
 }

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

@@ -0,0 +1,10 @@
+package com.fs.qw.param;
+
+
+import lombok.Data;
+
+@Data
+public class QwCloudIPByCompanyParam {
+    public String companyAdmin;
+    public String companyPassWord;
+}

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

@@ -146,4 +146,6 @@ public interface IQwUserService
      * @return  QwUser
      */
     QwUser getByQwUserIdAndCorId(String qwUserId, String corpId);
+
+    R selectCloudByCompany(Long companyId, Long userId);
 }

+ 17 - 15
fs-service-system/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java

@@ -23,21 +23,18 @@ import com.fs.course.param.FsCourseLinkCreateParam;
 import com.fs.course.service.IFsCourseLinkService;
 import com.fs.crm.domain.CrmCustomer;
 import com.fs.crm.mapper.CrmCustomerMapper;
-import com.fs.fastGpt.param.SendHookAIParam;
-import com.fs.qw.param.newparam.ExternalContactPageListParam;
-import com.fs.qw.vo.newvo.ExternalContactDetailsVO;
-import com.fs.qw.vo.newvo.ExternalContactListVO;
-import com.fs.qw.vo.newvo.ExternalContactNumVO;
-import com.fs.qw.vo.newvo.ExternalContactPageVO;
-import com.fs.store.domain.FsUser;
-import com.fs.store.mapper.FsUserMapper;
 import com.fs.qw.domain.*;
 import com.fs.qw.mapper.*;
 import com.fs.qw.param.*;
+import com.fs.qw.param.newparam.ExternalContactPageListParam;
 import com.fs.qw.result.QwExternalContactLogVo;
 import com.fs.qw.result.QwExternalContactVo;
 import com.fs.qw.service.*;
 import com.fs.qw.vo.*;
+import com.fs.qw.vo.newvo.ExternalContactDetailsVO;
+import com.fs.qw.vo.newvo.ExternalContactListVO;
+import com.fs.qw.vo.newvo.ExternalContactNumVO;
+import com.fs.qw.vo.newvo.ExternalContactPageVO;
 import com.fs.qwApi.Result.QwOpenidResult;
 import com.fs.qwApi.domain.*;
 import com.fs.qwApi.domain.inner.*;
@@ -58,8 +55,8 @@ import com.fs.store.mapper.FsUserMapper;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.service.ISysConfigService;
 import com.fs.voice.utils.StringUtil;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.rocketmq.spring.core.RocketMQTemplate;
-//import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
@@ -88,6 +85,7 @@ import java.util.stream.Stream;
  * @author fs
  * @date 2024-06-20
  */
+@Slf4j
 @Service
 public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactMapper, QwExternalContact> implements IQwExternalContactService {
 
@@ -1219,10 +1217,10 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
             List<QwExternalContact> externalContactList = qwExternalContactMapper.selectList(new QueryWrapper<QwExternalContact>().eq("external_user_id", externalUserID).eq("corp_id", corpId));
             if(!externalContactList.isEmpty()){
                 externalContactList.forEach(e -> {
-                    e.setRepeat(1);
+                    e.setIsRepeat(1);
                 });
                 qwExternalContactService.updateBatchById(externalContactList);
-                qwExternalContact.setRepeat(1);
+                qwExternalContact.setIsRepeat(1);
             }
         }
 
@@ -1243,9 +1241,9 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         if (isNewQwExternalContact) {
             qwExternalContact.setName("待同步客户");
             qwExternalContact.setGender(0);
-            qwExternalContactMapper.insertQwExternalContact(qwExternalContact);
+            qwExternalContactMapper.insert(qwExternalContact);
         } else {
-            qwExternalContactMapper.updateQwExternalContact(qwExternalContact);
+            qwExternalContactMapper.updateById(qwExternalContact);
         }
 
         QwExternalContact contact = qwExternalContact;
@@ -1518,6 +1516,7 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
                 String tagRemark = null;
                 //根据qwUser 匹配 分时段的标签
                 QwAutoTags qwAutoTags = qwAutoTagsMapper.selectQwAutoTagsByIdJSON(corpId, qwUser.getId());
+                log.info("自动打标签方案{}", qwAutoTags);
                 if (qwAutoTags != null) {
                     //标签日志记录
                     qwAutoTagsLogs.setAutoTagId(qwAutoTags.getId());
@@ -1543,11 +1542,12 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
                         String startTime = rulesTags.getStartTime();
                         String endTime = rulesTags.getEndTime();
                         String remarks = rulesTags.getRemarks();
-
+                        log.info("自动打标签开始时间:{} 到 {}, time:{} 到 {}", startDate, endDate, startTime, endTime);
                         // 检查今天是否在 week 集合中
 //                        boolean isTodayInWeek = week.contains(todayIndex);
                         boolean isToday = false;
-                        if(startDate == null || endDate == null){
+                        if(startDate != null && endDate != null){
+                            log.info("进入日期判断");
                             isToday = !nowDate.isBefore(startDate) && !nowDate.isAfter(endDate);
                         }
                         // 转换时间字符串为 LocalTime
@@ -1555,6 +1555,8 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
                         LocalTime end = LocalTime.parse(endTime.equals("24:00") ? "23:59:59" : endTime);
                         // 检查当前时间是否在 startTime 和 endTime 之间
                         boolean isNowInTimeRange = !now.isBefore(start) && !now.isAfter(end);
+                        log.info("是否满足时间打标签:天:{}, 时:{}", isToday, isNowInTimeRange);
+                        log.info("是否每天{}", rulesTags.getEveryDay());
                         // 如果当前时间和日期匹配规则,将 tagsItem 添加到 combinedTagsSet 中
                         if (((rulesTags.getEveryDay() != null && rulesTags.getEveryDay() == 1) || isToday) && isNowInTimeRange) {
 

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

@@ -10,6 +10,7 @@ import com.fs.his.config.FsSysConfig;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.qw.domain.QwCompany;
 import com.fs.qw.domain.QwUser;
+import com.fs.qw.dto.QwUserByToolDTO;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.param.*;
 import com.fs.qw.result.QwFsServerBindResult;
@@ -885,6 +886,71 @@ public class QwUserServiceImpl implements IQwUserService
         return qwUserMapper.getQwUserCompanyId(corpId,qwUserid);
     }
 
+    @Override
+    public R selectCloudByCompany(Long companyId, Long userId) {
+        List<QwUserByToolDTO> qwUserByToolDTOS = qwUserMapper.selectQwUserListByTools(companyId, userId);
+        qwUserByToolDTOS.forEach(item->{
+            try {
+                R r = GetIPAdminAndPassWord(item.getLoginCodeUrl());
+                Object data = r.get("data");
+                String jsonString = JSON.toJSONString(data);
+                QwCloudAPVO apvo = JSON.parseObject(jsonString, QwCloudAPVO.class);
+
+                item.setApAdmin(apvo.getApAdmin());
+                item.setApPassword(apvo.getApPassword());
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        return R.ok().put("data",qwUserByToolDTOS);
+    }
+
+    public R GetIPAdminAndPassWord(String ipAddress ) throws Exception {
+
+        String bodyKey = HttpRequest.get("http://watch.ylrzcloud.com/prod-api/server/getKey?serverIp="+ipAddress)
+                .execute().body();
+
+//        String bodyKey = HttpRequest.get("http://192.168.10.155:8111/server/getKey?serverIp="+param.getIpAddress())
+//                .execute().body();
+
+        QwFsServerBindResult resultDecode = JSON.parseObject(bodyKey, QwFsServerBindResult.class);
+
+        PrivateKey privateKey = getPrivateKeyFromString(resultDecode.getMsg());
+
+        String signature = RSASignatureUtils.sign(ipAddress+ "ylrz321!#@1", privateKey );
+
+        QwCloudAPParamB paramB=new QwCloudAPParamB();
+        paramB.setServerIp(ipAddress);
+        paramB.setSignature(signature);
+
+        String body = HttpRequest.post("http://watch.ylrzcloud.com/prod-api/server/getAccount")
+                .body(JSON.toJSONString(paramB), "application/json;charset=UTF-8")
+                .execute().body();
+//        String body = HttpRequest.post("http://192.168.10.155:8111/server/getAccount")
+//                .body(JSON.toJSONString(paramB), "application/json;charset=UTF-8")
+//                .execute().body();
+
+        QwFsServerAPResult result = JSON.parseObject(body, QwFsServerAPResult.class);
+
+        if (result.getCode()==200){
+            //账号 解密
+            String apAdmin = RSAUtils.decrypt(result.getData().serverAccount, privateKey);
+
+            //密码
+            String apPassword = RSAUtils.decrypt(result.getData().serverPassword, privateKey);
+
+            QwCloudAPVO apvo=new QwCloudAPVO();
+            apvo.setApAdmin(apAdmin);
+            apvo.setApPassword(apPassword);
+
+            return  R.ok().put("data",apvo);
+
+        }else {
+            return  R.error("获取账号密码失败,请重试!!错误码:"+result.getMsg());
+        }
+    }
+
     /**
      *  url转临时文件filr
      */

+ 2 - 5
fs-service-system/src/main/java/com/fs/sop/mapper/SopUserLogsInfoMapper.java

@@ -3,10 +3,7 @@ package com.fs.sop.mapper;
 import com.fs.common.annotation.DataSource;
 import com.fs.common.enums.DataSourceType;
 import com.fs.sop.domain.SopUserLogsInfo;
-import com.fs.sop.params.BatchSopUserLogsInfoParamU;
-import com.fs.sop.params.DeleteQwSopParam;
-import com.fs.sop.params.SopUserLogsInfoDelParam;
-import com.fs.sop.params.SopUserLogsInfoParam;
+import com.fs.sop.params.*;
 import com.fs.sop.vo.SopUserLogsInfoVOE;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
@@ -167,7 +164,7 @@ public interface SopUserLogsInfoMapper {
     public List<SopUserLogsInfo> selectSopUserLogsInfoByIds(@Param("ids") String[] ids);
 
     @DataSource(DataSourceType.SOP)
-    public List<SopUserLogsInfo> selectSopUserLogsInfoByIdsHasUserId(@Param("ids") String[] ids);
+    public List<SopUserLogsInfo> selectSopUserLogsInfoByIdsHasUserId(SopUserLogsInfoByIdsHasUserIdParam param);
 
     /**
      * 修改sopUserLogsInfo

+ 1 - 0
fs-service-system/src/main/java/com/fs/sop/params/SendUserLogsInfoMsgParam.java

@@ -17,4 +17,5 @@ public class SendUserLogsInfoMsgParam {
     private String userIdParam;
     private String startTime;
     private String corpId;
+    private Integer isRegister;
 }

+ 10 - 0
fs-service-system/src/main/java/com/fs/sop/params/SopUserLogsInfoByIdsHasUserIdParam.java

@@ -0,0 +1,10 @@
+package com.fs.sop.params;
+
+import lombok.Data;
+
+@Data
+public class SopUserLogsInfoByIdsHasUserIdParam {
+
+    private String[] ids;
+    private Integer isRegister;
+}

+ 32 - 20
fs-service-system/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.StringUtils;
+import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCourseDomainName;
 import com.fs.course.domain.FsCourseLink;
@@ -28,10 +29,7 @@ import com.fs.sop.domain.SopUserLogsInfo;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.mapper.QwSopMapper;
 import com.fs.sop.mapper.SopUserLogsInfoMapper;
-import com.fs.sop.params.BatchSopUserLogsInfoParam;
-import com.fs.sop.params.BatchSopUserLogsInfoParamU;
-import com.fs.sop.params.SendUserLogsInfoMsgParam;
-import com.fs.sop.params.SopUserLogsParamByDate;
+import com.fs.sop.params.*;
 import com.fs.sop.service.IQwSopService;
 import com.fs.sop.service.ISopUserLogsInfoService;
 import com.fs.sop.service.ISopUserLogsService;
@@ -113,6 +111,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
     @Autowired
     private QwExternalContactMapper qwExternalContactMapper;
 
+    @Autowired
+    private CompanyUserMapper companyUserMapper;
+
     @Override
     public void save(SopUserLogsInfo sopUserLogsInfo) {
         sopUserLogsInfoMapper.insertSopUserLogsInfo(sopUserLogsInfo);
@@ -360,7 +361,11 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
         List<FastGptChatReplaceWords> words = fastGptChatReplaceWordsMapper.selectAllFastGptChatReplaceWords();
 
 //        List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoByIds(param.getIds());
-        List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoByIdsHasUserId(param.getIds());
+        SopUserLogsInfoByIdsHasUserIdParam userIdParam=new SopUserLogsInfoByIdsHasUserIdParam();
+        userIdParam.setIds(param.getIds());
+        userIdParam.setIsRegister(param.getIsRegister());
+
+        List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoByIdsHasUserId(userIdParam);
 
         String[] userKey = param.getUserIdParam().split("\\|");
         String qwUserId = userKey[0].trim();
@@ -380,6 +385,13 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
             return R.error().put("msg","课程默认配置为空,请联系管理员");
         }
 
+        //域名
+        String domainName = companyUserMapper.selectDomainByUserId(Long.parseLong(companyUserId));
+        if (StringUtils.isEmpty(domainName)){
+            domainName = config.getRealLinkDomainName();
+        }
+        String finalDomainName = domainName;
+
 //        if (StringUtils.isEmpty(config.getMiniprogramPage())){
 //            return R.error().put("msg","课程默认配置小程序路径为空,请联系管理员");
 //        }
@@ -435,7 +447,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                             addWatchLogIfNeeded(param.getSopId(), param.getVideoId(), param.getCourseId(),item.getFsUserId(), qwUserId, companyUserId, companyId, item.getExternalId(),param.getStartTime(),createTime );
 
                             String sortLink = generateShortLink(st, param.getCorpId(), createTime, param.getCourseId(), param.getVideoId(),
-                                    qwUserId, companyUserId, companyId, item.getExternalId(),config);
+                                    qwUserId, companyUserId, companyId,finalDomainName, item.getExternalId(),config);
 
                             if (StringUtils.isNotEmpty(sortLink)) {
                                 if ("3".equals(st.getContentType())) {
@@ -585,7 +597,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
     private String generateShortLink(QwSopCourseFinishTempSetting.Setting setting, String corpId, Date sendTime,
                                      Integer courseId, Integer videoId, String qwUserId,
-                                     String companyUserId, String companyId, Long externalId,CourseConfig config) {
+                                     String companyUserId, String companyId,String domainName, Long externalId,CourseConfig config) {
 
         // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
 //        FsCourseLink link = new FsCourseLink();
@@ -625,7 +637,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
         link.setUpdateTime(updateTime);
 
-        String sortLink = createLink(link);
+        String sortLink = createLink(link,domainName);
 
         //存短链-
         fsCourseLinkMapper.insertFsCourseLink(link);
@@ -700,19 +712,19 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
 
 
-    private String createLink(FsCourseLink link){
+    private String createLink(FsCourseLink link,String domain){
 
-        List<FsCourseDomainName> cachedDomainNames = fsCourseDomainNameMapper.selectAllDomainNames();
-        if (cachedDomainNames == null || cachedDomainNames.isEmpty()) {
-            log.error("一键生成短链-域名出错-No domain names available for short link generation.");
-            return "";
-        }
-
-        int randomIndex = ThreadLocalRandom.current().nextInt(cachedDomainNames.size());
-
-        // 从缓存中随机选择一个域名
-        FsCourseDomainName fsCourseDomainName = cachedDomainNames.get(randomIndex);
-        String sortLink = "https://" + fsCourseDomainName.getDomainName() + "/s/" + link.getLink();
+//        List<FsCourseDomainName> cachedDomainNames = fsCourseDomainNameMapper.selectAllDomainNames();
+//        if (cachedDomainNames == null || cachedDomainNames.isEmpty()) {
+//            log.error("一键生成短链-域名出错-No domain names available for short link generation.");
+//            return "";
+//        }
+//
+//        int randomIndex = ThreadLocalRandom.current().nextInt(cachedDomainNames.size());
+//
+//        // 从缓存中随机选择一个域名
+//        FsCourseDomainName fsCourseDomainName = cachedDomainNames.get(randomIndex);
+        String sortLink = domain + "/s/" + link.getLink();
 
         return sortLink;
     }

+ 13 - 8
fs-service-system/src/main/java/com/fs/store/mapper/FsCityMapper.java

@@ -1,20 +1,22 @@
 package com.fs.store.mapper;
 
 import java.util.List;
+
+import com.fs.his.vo.CitysAreaVO;
 import com.fs.store.domain.FsCity;
 import org.apache.ibatis.annotations.Select;
 
 /**
  * 城市Mapper接口
- * 
+ *
  * @author fs
  * @date 2022-03-15
  */
-public interface FsCityMapper 
+public interface FsCityMapper
 {
     /**
      * 查询城市
-     * 
+     *
      * @param id 城市ID
      * @return 城市
      */
@@ -22,7 +24,7 @@ public interface FsCityMapper
 
     /**
      * 查询城市列表
-     * 
+     *
      * @param fsCity 城市
      * @return 城市集合
      */
@@ -30,7 +32,7 @@ public interface FsCityMapper
 
     /**
      * 新增城市
-     * 
+     *
      * @param fsCity 城市
      * @return 结果
      */
@@ -38,7 +40,7 @@ public interface FsCityMapper
 
     /**
      * 修改城市
-     * 
+     *
      * @param fsCity 城市
      * @return 结果
      */
@@ -46,7 +48,7 @@ public interface FsCityMapper
 
     /**
      * 删除城市
-     * 
+     *
      * @param id 城市ID
      * @return 结果
      */
@@ -54,11 +56,14 @@ public interface FsCityMapper
 
     /**
      * 批量删除城市
-     * 
+     *
      * @param ids 需要删除的数据ID
      * @return 结果
      */
     public int deleteFsCityByIds(Long[] ids);
     @Select("select * from fs_city where is_show=1")
     List<FsCity> selectFsCitys();
+
+    @Select("SELECT city_id,name as city_name FROM fs_city where parent_id=0 ")
+    List<CitysAreaVO> getCitysArea();
 }

+ 16 - 17
fs-service-system/src/main/java/com/fs/store/mapper/FsUserMapper.java

@@ -15,9 +15,7 @@ import com.fs.store.vo.FSUserVO;
 
 import com.fs.store.vo.FsCompanyUserListQueryVO;
 import com.fs.store.vo.FsUserTuiVO;
-import com.fs.store.vo.h5.FsUserPageListVO;
-import com.fs.store.vo.h5.UserDetailsVO;
-import com.fs.store.vo.h5.UserListCountVO;
+import com.fs.store.vo.h5.*;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
@@ -239,19 +237,20 @@ public interface FsUserMapper
 
     void transferCompanyUser(FsUserTransferParamDTO param);
 
-    /**
-     * 公司fsUser数
-     * @param companyId 公司ID
-     * @return count
-     */
-    @Select("select count(fu.user_id) from fs_user fu where fu.company_id = #{companyId}")
-    int selectCountByCompanyId(@Param("companyId") Long companyId);
+    FsUserSummaryCountVO countUserSummary(@Param("userId") Long userId);
+
+    List<FsUserSummaryCountTagVO> countTag(@Param("userId") Long userId);
+
+    Map<String, Long> countUserCourse(@Param("userId") Long userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("courseId")String courseId, @Param("videoId")String videoId);
+
+    Map<String, Long> countUserAnswer(@Param("userId") Long userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("courseId")String courseId, @Param("videoId")String videoId);
+
+    Map<String, Object> countUserRedPacket(@Param("userId") Long userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("courseId")String courseId, @Param("videoId")String videoId);
+
+    Map<String, Long> countCourseDetails(@Param("userId") Long userId, @Param("courseId")String courseId, @Param("videoId")String videoId);
+
+    FsUserRankingVO countUserRanking(@Param("userId") Long userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("courseId")String courseId, @Param("videoId")String videoId, @Param("order")String order);
+
+    FsUserRankingVO countCourseRanking(@Param("userId") Long userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("courseId")String courseId, @Param("videoId")String videoId, @Param("order")String order);
 
-    /**
-     * 公司销售fsUser数
-     * @param companyUserId 销售ID
-     * @return count
-     */
-    @Select("select count(fu.user_id) from fs_user fu where fu.company_user_id = #{companyUserId}")
-    int selectCountByCompanyUserId(@Param("companyUserId") Long companyUserId);
 }

+ 12 - 8
fs-service-system/src/main/java/com/fs/store/service/IFsCityService.java

@@ -1,19 +1,21 @@
 package com.fs.store.service;
 
 import java.util.List;
+
+import com.fs.his.vo.CitysAreaVO;
 import com.fs.store.domain.FsCity;
 
 /**
  * 城市Service接口
- * 
+ *
  * @author fs
  * @date 2022-03-15
  */
-public interface IFsCityService 
+public interface IFsCityService
 {
     /**
      * 查询城市
-     * 
+     *
      * @param id 城市ID
      * @return 城市
      */
@@ -21,7 +23,7 @@ public interface IFsCityService
 
     /**
      * 查询城市列表
-     * 
+     *
      * @param fsCity 城市
      * @return 城市集合
      */
@@ -29,7 +31,7 @@ public interface IFsCityService
 
     /**
      * 新增城市
-     * 
+     *
      * @param fsCity 城市
      * @return 结果
      */
@@ -37,7 +39,7 @@ public interface IFsCityService
 
     /**
      * 修改城市
-     * 
+     *
      * @param fsCity 城市
      * @return 结果
      */
@@ -45,7 +47,7 @@ public interface IFsCityService
 
     /**
      * 批量删除城市
-     * 
+     *
      * @param ids 需要删除的城市ID
      * @return 结果
      */
@@ -53,11 +55,13 @@ public interface IFsCityService
 
     /**
      * 删除城市信息
-     * 
+     *
      * @param id 城市ID
      * @return 结果
      */
     public int deleteFsCityById(Long id);
 
     List<FsCity> selectFsCitys();
+
+    List<CitysAreaVO> getCitysArea();
 }

+ 11 - 2
fs-service-system/src/main/java/com/fs/store/service/IFsUserService.java

@@ -11,7 +11,6 @@ import com.fs.qw.vo.QwFsUserVO;
 import com.fs.store.domain.FsStoreOrder;
 import com.fs.store.domain.FsStoreOrderItem;
 import com.fs.store.domain.FsUser;
-
 import com.fs.store.dto.FsUserTransferParamDTO;
 import com.fs.store.param.SelectCusListPageParam;
 import com.fs.store.param.h5.FsUserPageListParam;
@@ -21,6 +20,9 @@ import com.fs.store.vo.FsUserTuiVO;
 import com.fs.store.vo.h5.FsUserPageListVO;
 import com.fs.store.vo.h5.UserDetailsVO;
 import com.fs.store.vo.h5.UserListPageVO;
+import com.fs.store.vo.h5.*;
+
+import java.util.List;
 
 /**
  * 用户Service接口
@@ -152,5 +154,12 @@ public interface IFsUserService
      */
     Map<String, Long> getUserVipCountByCompanyUserId(Long companyUserId);
 
-    Map<String, Integer> getCompanyUserAndUserCount(CompanyQueryVo companyQueryVo);
+    FsUserSummaryCountVO userSummaryCount(Long userId);
+
+    FsUserStatisticsVO userStatistics(Long userId, String startTime, String endTime, String courseId, String videoId);
+
+    FsUserStatisticsVO userStatisticsDetails(Long userId, String courseId, String videoId);
+
+    FsUserRankingVO userRanking(Long userId, String startTime, String endTime, String courseId, String videoId, String order, Integer type);
+
 }

+ 16 - 8
fs-service-system/src/main/java/com/fs/store/service/impl/FsCityServiceImpl.java

@@ -1,6 +1,9 @@
 package com.fs.store.service.impl;
 
+import java.util.Collections;
 import java.util.List;
+
+import com.fs.his.vo.CitysAreaVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
@@ -10,19 +13,19 @@ import com.fs.store.service.IFsCityService;
 
 /**
  * 城市Service业务层处理
- * 
+ *
  * @author fs
  * @date 2022-03-15
  */
 @Service
-public class FsCityServiceImpl implements IFsCityService 
+public class FsCityServiceImpl implements IFsCityService
 {
     @Autowired
     private FsCityMapper fsCityMapper;
 
     /**
      * 查询城市
-     * 
+     *
      * @param id 城市ID
      * @return 城市
      */
@@ -34,7 +37,7 @@ public class FsCityServiceImpl implements IFsCityService
 
     /**
      * 查询城市列表
-     * 
+     *
      * @param fsCity 城市
      * @return 城市
      */
@@ -46,7 +49,7 @@ public class FsCityServiceImpl implements IFsCityService
 
     /**
      * 新增城市
-     * 
+     *
      * @param fsCity 城市
      * @return 结果
      */
@@ -58,7 +61,7 @@ public class FsCityServiceImpl implements IFsCityService
 
     /**
      * 修改城市
-     * 
+     *
      * @param fsCity 城市
      * @return 结果
      */
@@ -70,7 +73,7 @@ public class FsCityServiceImpl implements IFsCityService
 
     /**
      * 批量删除城市
-     * 
+     *
      * @param ids 需要删除的城市ID
      * @return 结果
      */
@@ -82,7 +85,7 @@ public class FsCityServiceImpl implements IFsCityService
 
     /**
      * 删除城市信息
-     * 
+     *
      * @param id 城市ID
      * @return 结果
      */
@@ -97,4 +100,9 @@ public class FsCityServiceImpl implements IFsCityService
     public List<FsCity> selectFsCitys() {
         return fsCityMapper.selectFsCitys();
     }
+
+    @Override
+    public List<CitysAreaVO> getCitysArea() {
+        return fsCityMapper.getCitysArea();
+    }
 }

+ 84 - 29
fs-service-system/src/main/java/com/fs/store/service/impl/FsUserServiceImpl.java

@@ -1,6 +1,7 @@
 package com.fs.store.service.impl;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -20,8 +21,6 @@ import com.fs.company.service.ICompanyUserService;
 import com.fs.company.vo.CompanyQueryVo;
 import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.vo.QwFsUserVO;
-import com.fs.qw.vo.newvo.ExternalContactNumVO;
-import com.fs.qw.vo.newvo.ExternalContactPageVO;
 import com.fs.store.domain.FsStoreOrder;
 import com.fs.store.domain.FsStoreOrderItem;
 import com.fs.store.domain.FsUserBill;
@@ -42,6 +41,7 @@ import com.fs.store.vo.h5.FsUserPageListVO;
 import com.fs.store.vo.h5.UserDetailsVO;
 import com.fs.store.vo.h5.UserListCountVO;
 import com.fs.store.vo.h5.UserListPageVO;
+import com.fs.store.vo.h5.*;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -401,7 +401,6 @@ public class FsUserServiceImpl implements IFsUserService
         return R.error("清除vip失败");
     }
 
-
     @Override
     public FsUser selectFsUserByUnionid(String unionId) {
         return fsUserMapper.selectFsUserByUnionid(unionId);
@@ -420,10 +419,11 @@ public class FsUserServiceImpl implements IFsUserService
     @Override
     public UserListPageVO getUserNumber(Long userId) {
         List<UserListCountVO> list = fsUserMapper.getUserNumber(userId);
-        Map<String, Integer> map = list.stream().collect(Collectors.toMap(UserListCountVO::getStatus, UserListCountVO::getNum, (v1, v2) -> v1));
+        Map<String, Integer> map = list.stream()
+                .collect(Collectors.toMap(UserListCountVO::getStatus, UserListCountVO::getNum, (v1, v2) -> v1));
         UserListPageVO pageVO = new UserListPageVO();
-        pageVO.setNumber(map.getOrDefault("1",0));
-        pageVO.setBlackNum(map.getOrDefault("0",0));
+        pageVO.setNumber(map.getOrDefault("1", 0));
+        pageVO.setBlackNum(map.getOrDefault("0", 0));
         return pageVO;
     }
 
@@ -445,11 +445,11 @@ public class FsUserServiceImpl implements IFsUserService
     @Override
     public Boolean disabledUser(String[] ids, boolean status) {
         boolean result;
-        if(status){
-            //启用
+        if (status) {
+            // 启用
             result = fsUserMapper.batchUpdateFsUserByIds(ids, 1) > 0;
         } else {
-            //禁用
+            // 禁用
             result = fsUserMapper.batchUpdateFsUserByIds(ids, 0) > 0;
         }
         return result;
@@ -475,28 +475,83 @@ public class FsUserServiceImpl implements IFsUserService
     }
 
     @Override
-    public Map<String, Integer> getCompanyUserAndUserCount(CompanyQueryVo companyQueryVo) {
-        if (companyQueryVo.getCompanyId() == null && companyQueryVo.getCompanyUserId() == null) {
-            return Collections.emptyMap();
+    public FsUserSummaryCountVO userSummaryCount(Long userId) {
+        FsUserSummaryCountVO fsUserSummaryCountVO = fsUserMapper.countUserSummary(userId);
+        List<FsUserSummaryCountTagVO> countTagList = fsUserMapper.countTag(userId);
+        fsUserSummaryCountVO.setTagList(countTagList);
+        return fsUserSummaryCountVO;
+    }
+
+    @Override
+    public FsUserStatisticsVO userStatistics(Long userId, String startTime, String endTime, String courseId, String videoId) {
+        return getUserStatistics(userId, startTime, endTime, courseId, videoId);
+    }
+
+    private FsUserStatisticsVO getUserStatistics(Long userId, String startTime, String endTime, String courseId, String videoId) {
+        FsUserStatisticsVO fsUserStatisticsVO = new FsUserStatisticsVO();
+
+        // 获取课程统计
+        Map<String, Long> couserMap = fsUserMapper.countUserCourse(userId, startTime, endTime, courseId, videoId);
+        if (couserMap != null) {
+            fsUserStatisticsVO.setCourseWatchNum(couserMap.get("courseWatchNum").intValue()).setCourseCompleteNum(couserMap.get("courseCompleteNum").intValue());
+
+            if (couserMap.get("courseCompleteNum") != null && couserMap.get("courseWatchNum") > 0) {
+                int courseCompleteRate = BigDecimal.valueOf(couserMap.get("courseCompleteNum"))
+                        .divide(BigDecimal.valueOf(couserMap.get("courseWatchNum")), 2, RoundingMode.HALF_UP)
+                        .multiply(BigDecimal.valueOf(100))
+                        .intValue();
+                fsUserStatisticsVO.setCourseCompleteRate(courseCompleteRate);
+            }
+        }
+
+        // 获取答题统计
+        Map<String, Long> answerMap = fsUserMapper.countUserAnswer(userId, startTime, endTime, courseId, videoId);
+        if (answerMap != null) {
+            fsUserStatisticsVO.setAnswerNum(answerMap.get("answerNum").intValue()).setAnswerRightNum(answerMap.get("answerRightNum").intValue());
+
+            if (answerMap.get("answerRightNum") != null && answerMap.get("answerNum") > 0) {
+                int answerCompleteRate = BigDecimal.valueOf(answerMap.get("answerRightNum"))
+                        .divide(BigDecimal.valueOf(answerMap.get("answerNum")), 2, RoundingMode.HALF_UP)
+                        .multiply(BigDecimal.valueOf(100))
+                        .intValue();
+                fsUserStatisticsVO.setAnswerRightRate(answerCompleteRate);
+            }
+        }
+
+        // 获取红包统计
+        Map<String, Object> redPacketMap = fsUserMapper.countUserRedPacket(userId, startTime, endTime, courseId, videoId);
+        if(redPacketMap != null && redPacketMap.get("redPacketNum") != null && redPacketMap.get("redPacketAmount") != null) {
+            fsUserStatisticsVO.setRedPacketNum(Integer.parseInt(redPacketMap.get("redPacketNum").toString()))
+                    .setRedPacketAmount(new BigDecimal(redPacketMap.get("redPacketAmount").toString()));
+        }
+        return fsUserStatisticsVO;
+    }
+
+    @Override
+    public FsUserStatisticsVO userStatisticsDetails(Long userId, String courseId, String videoId) {
+        FsUserStatisticsVO userStatisticsVO = getUserStatistics(userId, null, null, courseId, videoId);
+
+        //统计课程数据详情,在查询统计详情的时候需要显示
+        Map<String, Long> courseDetailsMap = fsUserMapper.countCourseDetails(userId, courseId, videoId);
+        if(courseDetailsMap != null && courseDetailsMap.get("courseNum") != null && courseDetailsMap.get("videoNum") != null && courseDetailsMap.get("courseUserNum") != null){
+            userStatisticsVO.setCourseNum(Integer.parseInt(courseDetailsMap.get("courseNum").toString()))
+                    .setVideoNum(Integer.parseInt(courseDetailsMap.get("videoNum").toString()))
+                    .setCourseUserNum(Integer.parseInt(courseDetailsMap.get("courseUserNum").toString()));
         }
+        return userStatisticsVO;
+    }
 
-        HashMap<String, Integer> stringIntegerHashMap = new HashMap<>();
-        if (companyQueryVo.getCompanyId() != null) {
-            Long companyId = companyQueryVo.getCompanyId();
-            //会员人数
-            int count = fsUserMapper.selectCountByCompanyId(companyId);
-            //群管人数
-            Integer companyCount = companyUserService.selectCompanyUserCountByCompanyId(companyId);
-            stringIntegerHashMap.put("companyUserUserCount", count);
-            stringIntegerHashMap.put("companyUserCount", companyCount);
-            return stringIntegerHashMap;
-        } else if (companyQueryVo.getCompanyUserId() != null) {
-            Long companyUserId = companyQueryVo.getCompanyUserId();
-            //会员人数
-            int count = fsUserMapper.selectCountByCompanyUserId(companyUserId);
-            stringIntegerHashMap.put("companyUserUserCount", count);
-            return stringIntegerHashMap;
+    @Override
+    public FsUserRankingVO userRanking(Long userId, String startTime, String endTime, String courseId, String videoId, String order, Integer type) {
+        FsUserRankingVO fsUserRankingVO = null;
+        if(type == 1){
+            //按完播率
+             fsUserRankingVO = fsUserMapper.countUserRanking(userId, startTime, endTime, courseId, videoId, order);
+        }
+        if(type == 2){
+            //按正确率
+             fsUserRankingVO = fsUserMapper.countCourseRanking(userId, startTime, endTime, courseId, videoId, order);
         }
-        return Collections.emptyMap();
+        return fsUserRankingVO;
     }
 }

+ 34 - 0
fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserCountVO.java

@@ -0,0 +1,34 @@
+package com.fs.store.vo.h5;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 用户相关数据统计
+ */
+@Data
+@ApiModel
+public class FsUserCountVO {
+
+    @ApiModelProperty(value = "答题次数")
+    private int answerTime;
+
+    @ApiModelProperty(value = "答题正确次数")
+    private int answerRightTime;
+
+    @ApiModelProperty(value = "答题红包数")
+    private int answerRedPacketTime;
+
+    @ApiModelProperty(value = "答题红包金额")
+    private int answerRedPacketAmount;
+
+    @ApiModelProperty(value = "完播次数")
+    private int completeWatchCount;
+
+    @ApiModelProperty(value = "观看次数")
+    private int watchTimes;
+
+
+
+}

+ 27 - 0
fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserRankingVO.java

@@ -0,0 +1,27 @@
+package com.fs.store.vo.h5;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+
+/**
+ * 首页-销售数据排行榜
+ */
+@Data
+@ApiModel
+@Accessors(chain = true)
+public class FsUserRankingVO {
+
+    @ApiModelProperty(value = "销售名称")
+    private int userName;
+
+    @ApiModelProperty(value = "完播率")
+    private BigDecimal completeRate;
+
+    @ApiModelProperty(value = "正确率")
+    private BigDecimal answerRightRate;
+
+}

+ 62 - 0
fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserStatisticsVO.java

@@ -0,0 +1,62 @@
+package com.fs.store.vo.h5;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+
+/**
+ * 用户数据 汇总统计(首页-课程/答题/红包等数据)
+ */
+@Data
+@ApiModel
+@Accessors(chain = true)
+public class FsUserStatisticsVO {
+
+    @ApiModelProperty(value = "观看人数")
+    private int courseWatchNum;
+
+    @ApiModelProperty(value = "完播人数")
+    private int courseCompleteNum;
+
+    @ApiModelProperty(value = "完播率")
+    private int courseCompleteRate;
+
+
+    @ApiModelProperty(value = "答题人数")
+    private int answerNum;
+
+    @ApiModelProperty(value = "正确人数")
+    private int answerRightNum;
+
+    @ApiModelProperty(value = "正确率")
+    private int answerRightRate;
+
+
+    @ApiModelProperty(value = "答题红包数")
+    private int redPacketNum;
+
+    @ApiModelProperty(value = "答题红包金额")
+    private BigDecimal redPacketAmount;
+
+    @ApiModelProperty(value = "昨日数据,只有当开始时间和结束时间是今日时此对象才有值")
+    private FsUserStatisticsVO yesterdayVO;
+
+//    @ApiModelProperty(value = "新会员奖励数")
+//    private int newUserRedPacketNum;
+//
+//    @ApiModelProperty(value = "新会员奖励金额")
+//    private int newUserRedPacketAmount;
+
+    @ApiModelProperty(value = "详情-课程营期数")
+    private int courseNum;
+
+    @ApiModelProperty(value = "详情-课程数")
+    private int videoNum;
+
+    @ApiModelProperty(value = "详情-课程参与会员")
+    private int courseUserNum;
+
+}

+ 19 - 0
fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserSummaryCountTagVO.java

@@ -0,0 +1,19 @@
+package com.fs.store.vo.h5;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 用户数据 汇总统计(首页-标签)
+ */
+@Data
+@ApiModel
+public class FsUserSummaryCountTagVO {
+
+    @ApiModelProperty(value = "标签统计")
+    private String tagName;
+
+    @ApiModelProperty(value = "相关人数")
+    private int number;
+}

+ 25 - 0
fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserSummaryCountVO.java

@@ -0,0 +1,25 @@
+package com.fs.store.vo.h5;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 用户数据 汇总统计(首页)
+ */
+@Data
+@ApiModel
+public class FsUserSummaryCountVO {
+
+    @ApiModelProperty(value = "会员总数")
+    private int userTotal;
+
+    @ApiModelProperty(value = "今日新增会员")
+    private int todayNewUser;
+
+    @ApiModelProperty(value = "标签统计")
+    private List<FsUserSummaryCountTagVO> tagList;
+
+}

+ 9 - 2
fs-service-system/src/main/resources/mapper/company/CompanyUserMapper.xml

@@ -38,6 +38,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="qwStatus"    column="qw_status"    />
         <result property="domain"    column="domain"    />
         <result property="isAudit"    column="is_audit"    />
+        <result property="addressId"    column="address_id"    />
         <association property="dept"    column="dept_id" javaType="CompanyDept" resultMap="deptResult" />
         <collection  property="roles"   javaType="java.util.List"        resultMap="RoleResult" />
 
@@ -135,6 +136,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="qwStatus != null">qw_status,</if>
             <if test="domain != null">`domain`,</if>
             <if test="isAudit != null">`is_audit`,</if>
+            <if test="addressId != null">`address_id`,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="companyId != null">#{companyId},</if>
@@ -166,6 +168,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="qwStatus != null">#{qwStatus},</if>
             <if test="domain != null">#{domain},</if>
             <if test="isAudit != null">#{isAudit},</if>
+            <if test="addressId != null">#{addressId},</if>
          </trim>
     </insert>
 
@@ -200,6 +203,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="qwStatus != null">qw_status = #{qwStatus},</if>
             <if test="domain != null">`domain` = #{domain},</if>
             <if test="isAudit != null">`is_audit` = #{isAudit},</if>
+            <if test="addressId != null">`address_id` = #{addressId},</if>
         </trim>
         where user_id = #{userId}
     </update>
@@ -234,6 +238,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="qwStatus != null">qw_status = #{qwStatus},</if>
             <if test="domain != null">`domain` = #{domain},</if>
             <if test="isAudit != null">`is_audit` = #{isAudit},</if>
+            <if test="addressId != null">`address_id` = #{addressId},</if>
         </trim>
         where company_id = #{companyId}
     </update>
@@ -264,7 +269,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where u.user_name = #{userName}
     </select>
     <sql id="selectUserVo">
-        select u.user_id,u.company_id,u.qw_user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time,u.id_card, u.remark,u.user_type,u.open_id,u.qr_code_weixin,u.qr_code_wecom,u.jpush_id,u.domain,u.is_audit,
+        select u.user_id,u.company_id,u.qw_user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time,u.id_card, u.remark,u.user_type,u.open_id,u.qr_code_weixin,u.qr_code_wecom,u.jpush_id,u.domain,u.is_audit,u.address_id,
         d.dept_id, d.parent_id, d.dept_name, d.order_num, d.leader, d.status as dept_status,
         r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
         from company_user u
@@ -374,7 +379,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="remark"    column="remark"    />
         <result property="openId"    column="open_id"    />
         <result property="nowDayCustomerCount"    column="now_day_customer_count"    />
+        <result property="domain"    column="domain"    />
         <result property="isAudit"    column="is_audit"    />
+        <result property="addressId"    column="address_id"    />
         <association property="dept"    column="dept_id" javaType="CompanyDept" resultMap="deptResult" />
         <collection  property="roles"   javaType="java.util.List"        resultMap="RoleResult" />
         <collection property="posts" javaType="java.util.ArrayList" ofType="com.fs.company.domain.CompanyPost"
@@ -414,7 +421,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         update company_user
         set is_audit = 1
         where user_id in
-        <foreach collection="ids" open="(" close=")" separator="," item="id">
+        <foreach collection="userIds" open="(" close=")" separator="," item="id">
             #{id}
         </foreach>
     </update>

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

@@ -230,7 +230,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
     </update>
 
-    <select id="getFsUserCourseList" resultType="FsUserCourseListPVO">
+    <select id="getFsUserCourseList" resultType="FsUserCourseListVO">
         SELECT
             c.*,
             cc.cate_name,

+ 12 - 0
fs-service-system/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml

@@ -188,4 +188,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </if>
         order by video.course_sort
     </select>
+
+    <!-- 获取课程分析数据 -->
+    <select id="getCourseAnalysisByMap" resultType="com.fs.course.vo.FsCourseAnalysisVO">
+        select
+            fuc.course_id,
+            fuc.course_name,
+            fucv.title,
+            fuc.img_url,
+            fucv.description
+        from fs_user_course_video fucv
+        left join fs_user_course fuc on fuc.course_id = fucv.course_id
+    </select>
 </mapper>

+ 9 - 5
fs-service-system/src/main/resources/mapper/sop/SopUserLogsInfoMapper.xml

@@ -30,12 +30,16 @@
         where id = #{id}
     </select>
 
-    <select id="selectSopUserLogsInfoByIdsHasUserId" parameterType="java.util.List" resultMap="SopUserLogsInfoResult">
+    <select id="selectSopUserLogsInfoByIdsHasUserId" parameterType="com.fs.sop.params.SopUserLogsInfoByIdsHasUserIdParam" resultMap="SopUserLogsInfoResult">
         <include refid="selectSopUserLogsInfoVo"/>
-        WHERE fs_user_id is not null and fs_user_id !=0 and id IN
-        <foreach collection="ids" item="id" open="(" separator="," close=")">
-            #{id}
-        </foreach>
+        <where>
+            id IN
+            <foreach collection="ids" item="id" open="(" separator="," close=")">
+                #{id}
+            </foreach>
+            <if test="isRegister == 1">and fs_user_id is not null and fs_user_id != 0 </if>
+        </where>
+        order by crt_Time desc
     </select>
 
 

+ 267 - 0
fs-service-system/src/main/resources/mapper/store/FsUserMapper.xml

@@ -610,4 +610,271 @@
         </foreach>
     </update>
 
+    <select id="countUserSummary" resultType="FsUserSummaryCountVO">
+        SELECT (SELECT count(fs_user.user_id)
+                FROM fs_user
+                         LEFT JOIN company_user ON fs_user.company_user_id = company_user.user_id
+                WHERE (
+                          company_user.user_id = #{userId}
+                              OR company_user.parent_id = #{userId}
+                          )) as userTotal,
+               (SELECT count(fs_user.user_id)
+                FROM fs_user
+                         LEFT JOIN company_user ON fs_user.company_user_id = company_user.user_id
+                WHERE (company_user.user_id = #{userId} OR company_user.parent_id = #{userId})
+                  AND to_days(fs_user.create_time) = to_days(now())) as todayNewUser
+    </select>
+
+    <select id="countTag" resultType="FsUserSummaryCountTagVO">
+        SELECT
+            company_tag.tag AS tagName,
+            count( fs_user.user_id ) AS userCount
+        FROM
+            company_tag_user
+                LEFT JOIN fs_user ON fs_user.user_id = company_tag_user.user_id
+                LEFT JOIN company_tag ON FIND_IN_SET( company_tag.tag_id, company_tag_user.tag_ids ) > 0
+                LEFT JOIN company_user ON fs_user.company_user_id = company_user.user_id
+        where (company_user.user_id = #{userId} or company_user.parent_id = #{userId} ) and company_tag.tag_id is not null
+        GROUP BY
+            company_tag.tag_id
+    </select>
+
+    <select id="countUserCourse" resultType="Map">
+        SELECT
+            (
+                SELECT
+                    count( fcc.user_id )
+                FROM
+                    fs_user_course_count fcc
+                        LEFT JOIN fs_user ON fs_user.user_id = fcc.user_id
+                        LEFT JOIN company_user ON fs_user.company_user_id = company_user.user_id
+                        LEFT JOIN fs_user_course_video fcv ON fcv.course_id = fcc.course_id
+        WHERE
+                    ( company_user.user_id = #{userId}  OR company_user.parent_id = #{userId}  )
+                    <if test="startTime != null and startTime !='' ">
+                            and fcc.create_time &gt;= #{startTime}
+                    </if>
+                    <if test="endTime != null and endTime != ''">
+                        and fcc.create_time &lt;= #{endTime}
+                    </if>
+                    <if test="courseId != null and courseId != ''">
+                        AND fcc.course_id = #{courseId}
+                    </if>
+                    <if test="videoId != null and videoId != ''">
+                        AND fcv.video_id = #{videoId}
+                    </if>
+            ) as courseWatchNum,
+            (
+                SELECT
+                    count( fcc.user_id )
+                FROM
+                    fs_user_course_count fcc
+                        LEFT JOIN fs_user ON fs_user.user_id = fcc.user_id
+                        LEFT JOIN company_user ON fs_user.company_user_id = company_user.user_id
+                        LEFT JOIN fs_user_course_video fcv ON fcv.course_id = fcc.course_id
+                WHERE
+                    ( company_user.user_id = #{userId}  OR company_user.parent_id = #{userId}  )
+                  AND fcc.complete_watch_count > 0
+                    <if test="startTime != null and startTime !='' ">
+                        and fcc.create_time &gt;= #{startTime}
+                    </if>
+                    <if test="endTime != null and endTime != ''">
+                        and fcc.create_time &lt;= #{endTime}
+                    </if>
+                    <if test="courseId != null and courseId != ''">
+                        AND fcc.course_id =  #{courseId}
+                    </if>
+                    <if test="videoId != null and videoId != ''">
+                        AND fcv.video_id = #{videoId}
+                    </if>
+            ) as courseCompleteNum
+    </select>
+
+    <select id="countUserAnswer" resultType="Map">
+        SELECT
+        (
+        SELECT
+        count( fs_user.user_id )
+        FROM
+        fs_course_answer_logs
+        LEFT JOIN fs_user ON fs_user.user_id = fs_course_answer_logs.user_id
+        LEFT JOIN company_user ON company_user.user_id = fs_user.company_user_id
+        WHERE
+        ( company_user.user_id = #{userId} OR company_user.parent_id = #{userId} )
+        <if test="startTime != null and startTime !='' ">
+            AND fs_course_answer_logs.create_time &gt;= #{startTime}
+        </if>
+        <if test="endTime != null and endTime != ''">
+            AND fs_course_answer_logs.create_time &lt;= #{endTime}
+        </if>
+        <if test="courseId != null and courseId != ''">
+            AND fs_course_answer_logs.course_id = #{courseId}
+        </if>
+        <if test="videoId != null and videoId != ''">
+            AND fs_course_answer_logs.video_id = #{videoId}
+        </if>
+        ) AS answerNum,
+        (
+        SELECT
+        count( fs_user.user_id )
+        FROM
+        fs_course_answer_logs
+        LEFT JOIN fs_user ON fs_user.user_id = fs_course_answer_logs.user_id
+        LEFT JOIN company_user ON company_user.user_id = fs_user.company_user_id
+        WHERE
+        ( company_user.user_id = #{userId} OR company_user.parent_id = #{userId} )
+        AND fs_course_answer_logs.is_right = 1
+        <if test="startTime != null and startTime !='' ">
+            AND fs_course_answer_logs.create_time &gt;= #{startTime}
+        </if>
+        <if test="endTime != null and endTime != ''">
+            AND fs_course_answer_logs.create_time &lt;= #{endTime}
+        </if>
+        <if test="courseId != null and courseId != ''">
+            AND fs_course_answer_logs.course_id =  #{courseId}
+        </if>
+        <if test="videoId != null and videoId != ''">
+            AND fs_course_answer_logs.video_id = #{videoId}
+        </if>
+        ) AS answerRightNum
+    </select>
+
+    <select id="countUserRedPacket" resultType="Map">
+    SELECT
+        (
+            SELECT
+                count( flog.log_id )
+            FROM
+                fs_course_red_packet_log flog
+                    LEFT JOIN fs_user ON fs_user.user_id = flog.user_id
+                    LEFT JOIN company_user ON company_user.user_id = fs_user.company_user_id
+            WHERE
+                ( company_user.user_id = #{userId}  OR company_user.parent_id = #{userId}  )
+                <if test="startTime != null and startTime !='' ">
+                    AND flog.create_time &gt;= #{startTime}
+                </if>
+                <if test="endTime != null and endTime != ''">
+                    AND flog.create_time &lt;= #{endTime}
+                </if>
+                <if test="courseId != null and courseId != ''">
+                    AND flog.course_id =  #{courseId}
+                </if>
+                <if test="videoId != null and videoId != ''">
+                    AND flog.video_id = #{videoId}
+                </if>
+        ) AS redPacketNum,
+        (
+            SELECT
+                ifnull (sum( flog.amount ), 0)
+            FROM
+                fs_course_red_packet_log flog
+                    LEFT JOIN fs_user ON fs_user.user_id = flog.user_id
+                    LEFT JOIN company_user ON company_user.user_id = fs_user.company_user_id
+            WHERE
+                ( company_user.user_id = #{userId}  OR company_user.parent_id = #{userId}  )
+                <if test="startTime != null and startTime !='' ">
+                    AND flog.create_time &gt;= #{startTime}
+                </if>
+                <if test="endTime != null and endTime != ''">
+                    AND flog.create_time &lt;= #{endTime}
+                </if>
+                <if test="courseId != null and courseId != ''">
+                    AND flog.course_id =  #{courseId}
+                </if>
+                <if test="videoId != null and videoId != ''">
+                    AND flog.video_id = #{videoId}
+                </if>
+        ) AS redPacketAmount
+    </select>
+
+    <select id="countCourseDetails" resultType="Map">
+    select (SELECT count(fc.course_id)
+            FROM fs_user_course fc
+            WHERE fc.is_del = 0
+            <if test="courseId != null and courseId != ''">
+                AND fc.course_id =  #{courseId}
+            </if>
+            ) as courseNum,
+
+           (SELECT count(fcv.video_id)
+            FROM fs_user_course_video fcv
+                     LEFT JOIN fs_user_course fc ON fc.course_id = fcv.course_id
+            <if test="courseId != null and courseId != ''">
+                AND fcv.course_id =  #{courseId}
+            </if>
+            <if test="videoId != null and videoId != ''">
+                AND fcv.video_id = #{videoId}
+            </if>
+            ) as videoNum,
+        ( SELECT count( user_id ) FROM fs_user_course_count fcc
+            LEFT JOIN fs_user_course_video fcv ON fcv.course_id = fcc.course_id
+        <if test="courseId != null and courseId != ''">
+            AND fcc.course_id =  #{courseId}
+        </if>
+        <if test="videoId != null and videoId != ''">
+            AND fcv.video_id = #{videoId}
+        </if>
+         ) as courseUserNum
+    </select>
+
+    <select id="countUserRanking" resultType="FsUserRankingVO">
+        SELECT
+        company_user.nick_name as userName,
+        ifnull(
+            ROUND((COUNT( CASE WHEN fcc.complete_watch_count > 0 THEN 1 END ) / count( fcc.user_id ))*100,2),0
+        ) as completeRate
+        FROM
+        fs_user_course_count fcc
+        LEFT JOIN fs_user ON fs_user.user_id = fcc.user_id
+        LEFT JOIN company_user ON fs_user.company_user_id = company_user.user_id
+        LEFT JOIN fs_user_course_video fcv ON fcv.course_id = fcc.course_id
+        WHERE
+        company_user.parent_id = #{userId}
+        <if test="startTime != null and startTime !='' ">
+            AND fcc.create_time &gt;= #{startTime}
+        </if>
+        <if test="endTime != null and endTime != ''">
+            AND fcc.create_time &lt;= #{endTime}
+        </if>
+        <if test="courseId != null and courseId != ''">
+            AND fcc.course_id =  #{courseId}
+        </if>
+        <if test="videoId != null and videoId != ''">
+            AND fcv.video_id = #{videoId}
+        </if>
+        group by fcc.user_id
+        order by completeRate #{order}
+        limit 20
+    </select>
+
+    <select id="countCourseRanking" resultType="FsUserRankingVO">
+        SELECT
+            company_user.nick_name as userName,
+            ifnull(ROUND(
+                           (COUNT( CASE WHEN fs_course_answer_logs.is_right = 1 THEN 1 END ) / count( fs_user.user_id ))
+                               *100,2),0
+            ) as answerRightRate
+        FROM
+            fs_course_answer_logs
+                LEFT JOIN fs_user ON fs_user.user_id = fs_course_answer_logs.user_id
+                LEFT JOIN company_user ON company_user.user_id = fs_user.company_user_id
+        WHERE
+            company_user.parent_id = #{userId}
+            <if test="startTime != null and startTime !='' ">
+                AND fs_course_answer_logs.create_time &gt;= #{startTime}
+            </if>
+            <if test="endTime != null and endTime != ''">
+                AND fs_course_answer_logs.create_time &lt;= #{endTime}
+            </if>
+            <if test="courseId != null and courseId != ''">
+                AND fs_course_answer_logs.course_id =  #{courseId}
+            </if>
+            <if test="videoId != null and videoId != ''">
+                AND fs_course_answer_logs.video_id = #{videoId}
+            </if>
+        group by fs_user.user_id
+        order by answerRightRate #{order}
+        limit 20
+    </select>
+
 </mapper>

+ 125 - 0
fs-user-app/src/main/java/com/fs/app/controller/CourseH5Controller.java

@@ -0,0 +1,125 @@
+package com.fs.app.controller;
+
+
+import cn.hutool.json.JSONUtil;
+import com.fs.common.core.domain.R;
+import com.fs.course.config.CourseConfig;
+import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.course.param.FsUserCourseVideoFinishUParam;
+import com.fs.course.service.IFsCourseLinkService;
+import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.course.service.IFsUserCourseService;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.vo.FsUserCourseVideoH5DVO;
+import com.fs.course.vo.FsUserCourseVideoH5VO;
+import com.fs.system.service.ISysConfigService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Api("h5课堂接口")
+@RestController
+@RequestMapping(value="/app/course/h5")
+public class CourseH5Controller extends  AppBaseController{
+    Logger logger= LoggerFactory.getLogger(getClass());
+    @Autowired
+    private IFsUserCourseService courseService;
+
+    @Autowired
+    private IFsUserCourseVideoService courseVideoService;
+
+    @Autowired
+    private ISysConfigService configService;
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
+
+    @Autowired
+    private IFsCourseWatchLogService courseWatchLogService;
+
+
+    @ApiOperation("h5课程简介")
+    @GetMapping("/getH5CourseByVideoId")
+    public R getCourseByVideoId(@RequestParam("videoId") Long videoId,HttpServletRequest request)
+    {
+        FsUserCourseVideoH5VO course = courseService.selectFsUserCourseVideoH5VOByVideoId(videoId);
+        return R.ok().put("data",course);
+    }
+
+    @ApiOperation("h5课程详情")
+    @GetMapping("/getH5CourseVideoDetails")
+    public R getCourseVideoDetails(FsUserCourseVideoFinishUParam param)
+    {
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        FsUserCourseVideoH5DVO course = courseService.selectFsUserCourseVideoH5DVOByVideoId(param.getVideoId());
+
+        Long duration = 0L;
+        long tipsTime = 0L;
+        int isFinish = 0;
+        if (param.getLinkType()!=null&&param.getLinkType()==1){
+            return R.ok().put("course",course).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime).put("maxBufferLength",config.getMaxBufferLength());
+        }
+        // 从Redis中获取观看时长
+        String redisKey = "h5user:watch:duration:" + param.getQwUserId()+ ":" + param.getQwExternalId() + ":" + param.getVideoId();
+        String durationStr = redisCache.getCacheObject(redisKey);
+        FsCourseWatchLog log = courseWatchLogService.getWatchCourseVideoH5(param.getVideoId(),param.getQwUserId(),param.getQwExternalId());
+        //redis取不到查库
+        if (durationStr != null) {
+            duration = Long.parseLong(durationStr);
+        }else {
+            duration = log.getDuration();
+        }
+
+        if (course.getDuration()!=null){
+            tipsTime = course.getDuration()/2;
+        }
+        //判断是否完课
+        if (log.getLogType()==2){
+           isFinish=1;
+        }
+
+        //将视频时长也存到redis
+        String videoRedisKey = "h5user:video:duration:" + param.getVideoId();
+        Long videoDuration = redisCache.getCacheObject(videoRedisKey);
+        if (videoDuration==null){
+            redisCache.setCacheObject(videoRedisKey,course.getDuration());
+        }
+
+        return R.ok().put("course",course).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime).put("maxBufferLength",config.getMaxBufferLength()).put("isFinish",isFinish);
+    }
+
+
+
+    @ApiOperation("获取真实链接")
+    @GetMapping("/getRealLink")
+    public R getRealLink(@RequestParam("sortLink")String link)
+    {
+        return courseLinkService.getRealLink(link);
+    }
+
+
+    @ApiOperation("更新看课时长")
+    @PostMapping("/updateWatchDuration")
+    public R updateWatchDuration(@RequestBody FsUserCourseVideoFinishUParam param)
+    {
+        return courseVideoService.updateWatchDuration(param);
+    }
+
+
+    @ApiOperation("获取缓冲流量")
+    @PostMapping("/getInternetTraffic")
+    public R getInternetTraffic(@RequestBody FsUserCourseVideoFinishUParam param) {
+        return courseVideoService.getInternetTraffic(param);
+    }
+
+    @PostMapping("/getErrMsg")
+    public void getErrMsg(@RequestParam("msg") String msg) {
+        logger.error("zyp \n【h5看课中途报错】:{}",msg);
+    }
+
+}