59 次代碼提交 4c0a199600 ... 02574ca485

作者 SHA1 備註 提交日期
  Long 02574ca485 会员关联项目 代码合并 1 天之前
  Long e81d20b4cd Merge branch 'master' into 会员关联项目 1 天之前
  Long db49cd54aa 会员关联项目 代码还原 1 天之前
  Long f873e8a8b2 会员关联项目 代码还原 1 天之前
  yfh e01afd28a9 调整方法冲突问题 1 天之前
  yfh 23b2aed55e Merge remote-tracking branch 'origin/master' into 会员关联项目 1 天之前
  yfh 6acc45ddfd 调整方法冲突问题 1 天之前
  yfh 83ca82479f 恢复代码 2 天之前
  yfh ce17e7db9c Merge branch 'master' into 会员关联项目 2 天之前
  yfh 10c561311c 调整确认更换条件,增加项目名称展示 2 天之前
  yfh af636c4c33 调整校验条件 2 天之前
  yfh a942f8969d 修复bug 2 天之前
  yfh 8c3a0fb765 1、调整云联重粉小程序更换会员归属业务逻辑 2 天之前
  yfh c1bac6dd46 新增累计观看天数和连续观看天数 5 天之前
  yfh 28b9cf0364 新增累计观看天数和连续观看天数 5 天之前
  yfh d048f6f2d5 首页点击详情展示看课人数问题的调整 5 天之前
  yfh 09a5f9ef55 标签下会员列表 1 周之前
  yfh d5ea35eeaf 答题人数,红包信息统计调整 1 周之前
  yfh 58c35efc70 答题人数,红包信息统计 1 周之前
  Long 4716674099 删除问题代码 1 周之前
  yfh c030726552 调整传入数据 1 周之前
  Long e726bde6b9 会员关联项目 公众号配置修改 1 周之前
  yfh 7140550b0a Merge remote-tracking branch 'origin/会员关联项目' into 会员关联项目 1 周之前
  Long d653d6be0c 会员关联项目 最新代码合并 1 周之前
  Long eb6ed7e845 Merge branch 'master' into 会员关联项目 1 周之前
  Long 52e28e439f Merge branch 'master' into 会员关联项目 1 周之前
  yfh b60ac92e22 调整详情统计数据 1 周之前
  yfh cb88ef3c47 Merge remote-tracking branch 'origin/会员关联项目' into 会员关联项目 1 周之前
  Long 0aa15b30dd 会员关联项目 自增问题处理 1 周之前
  yfh 08a78e3438 处理打印日志 1 周之前
  yfh c02ff5232a Merge remote-tracking branch 'origin/会员关联项目' into 会员关联项目 1 周之前
  yfh ce35e70947 最近创建会员的信息 1 周之前
  Long 95d2ddfd7f 会员关联项目 错误处理 1 周之前
  Long 15360d97b4 会员关联项目 合并最新代码 1 周之前
  Long 5b24957b41 Merge remote-tracking branch 'origin/会员关联项目' into 会员关联项目 1 周之前
  yfh 09140cb521 增加默认值 1 周之前
  Long f26c756a4e Merge branch 'master' into 会员关联项目 1 周之前
  Long 552ac9ed0d 会员关联项目 1 周之前
  Long 3ea204b086 会员关联项目 1 周之前
  yfh 5280ddee51 Merge remote-tracking branch 'origin/会员关联项目' into 会员关联项目 1 周之前
  Long ee564cd7b7 会员关联项目 新增注册时间 1 周之前
  yfh 41a48075bc Merge remote-tracking branch 'origin/会员关联项目' into 会员关联项目 1 周之前
  Long 829e805924 会员关联项目 新增注册时间 1 周之前
  yfh 686742a36f 会员数据(小黑屋,会员,黑名单)统计问题 1 周之前
  yfh 40d3b7fefa 批量启用会员调整 1 周之前
  yfh d06c1a2b13 1、新增看课项目未插入问题处理 2 周之前
  Long 65e627f819 会员关联项目 2 周之前
  Long 73ccf43fb9 会员关联项目 2 周之前
  Long 411f6a25d7 Merge branch 'master' into 会员关联项目 2 周之前
  Long d20b50b593 会员关联项目 2 周之前
  Long a1c4f4ec0e 会员关联项目 2 周之前
  Long 85857bfd77 会员关联项目 2 周之前
  Long 5e53ca1739 会员关联项目 3 周之前
  Long b1c2cf98ac 会员关联项目 3 周之前
  Long e5f94e2daf 会员关联项目 3 周之前
  Long 26181b7e32 会员关联项目 3 周之前
  Long c5b42ec08c 会员关联项目 3 周之前
  Long 9f3446f6e3 Merge branch 'refs/heads/master' into 会员关联项目 3 周之前
  Long 82d7df1715 会员关联项目 4 周之前
共有 80 個文件被更改,包括 1803 次插入653 次删除
  1. 3 2
      fs-admin/src/main/java/com/fs/company/controller/CompanyUserController.java
  2. 13 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseVideoController.java
  3. 23 3
      fs-admin/src/main/java/com/fs/store/controller/FsUserController.java
  4. 6 27
      fs-company-app/src/main/java/com/fs/app/controller/AppBaseController.java
  5. 27 10
      fs-company-app/src/main/java/com/fs/app/controller/CompanyTagController.java
  6. 100 18
      fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  7. 45 29
      fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
  8. 1 0
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  9. 14 1
      fs-company-app/src/main/java/com/fs/app/param/CompanyUserChangeApplyParam.java
  10. 5 4
      fs-company-app/src/main/java/com/fs/app/param/CompanyUserParam.java
  11. 17 0
      fs-company-app/src/main/java/com/fs/app/param/FsUserProjectUpdateParam.java
  12. 3 3
      fs-company-app/src/main/java/com/fs/app/param/FsUserTagUpdateParam.java
  13. 0 5
      fs-company-app/src/main/java/com/fs/app/param/FsUserUpdateParam.java
  14. 24 0
      fs-company-app/src/main/java/com/fs/app/param/TagProjectParam.java
  15. 11 9
      fs-company/src/main/java/com/fs/users/controller/FsUserController.java
  16. 1 0
      fs-qw-task/src/main/java/com/fs/app/task/UserCourseWatchCountTask.java
  17. 4 0
      fs-service-system/src/main/java/com/fs/company/domain/CompanyUserChangeApplyUser.java
  18. 15 0
      fs-service-system/src/main/java/com/fs/company/dto/UserProjectDTO.java
  19. 2 0
      fs-service-system/src/main/java/com/fs/company/mapper/CompanyTagMapper.java
  20. 2 0
      fs-service-system/src/main/java/com/fs/company/mapper/CompanyTagUserMapper.java
  21. 2 0
      fs-service-system/src/main/java/com/fs/company/service/ICompanyTagService.java
  22. 16 7
      fs-service-system/src/main/java/com/fs/company/service/ICompanyTagUserService.java
  23. 2 1
      fs-service-system/src/main/java/com/fs/company/service/ICompanyUserChangeApplyService.java
  24. 3 2
      fs-service-system/src/main/java/com/fs/company/service/ICompanyUserService.java
  25. 10 0
      fs-service-system/src/main/java/com/fs/company/service/impl/CompanyTagServiceImpl.java
  26. 51 15
      fs-service-system/src/main/java/com/fs/company/service/impl/CompanyTagUserServiceImpl.java
  27. 18 12
      fs-service-system/src/main/java/com/fs/company/service/impl/CompanyUserChangeApplyServiceImpl.java
  28. 26 1
      fs-service-system/src/main/java/com/fs/company/service/impl/CompanyUserChangeApplyUserServiceImpl.java
  29. 12 4
      fs-service-system/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
  30. 4 0
      fs-service-system/src/main/java/com/fs/company/vo/CompanyTagUserVO.java
  31. 9 0
      fs-service-system/src/main/java/com/fs/company/vo/CompanyUserChangeApplyUserVO.java
  32. 5 0
      fs-service-system/src/main/java/com/fs/course/domain/FsUserCourseVideo.java
  33. 22 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  34. 3 3
      fs-service-system/src/main/java/com/fs/course/param/BatchVideoSvae.java
  35. 4 1
      fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseAddCompanyUserParam.java
  36. 3 1
      fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseBeMemberParam.java
  37. 2 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  38. 8 1
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java
  39. 53 29
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  40. 15 0
      fs-service-system/src/main/java/com/fs/course/vo/FsUserCourseParticipationRecordVO.java
  41. 5 0
      fs-service-system/src/main/java/com/fs/course/vo/newfs/FsUserCourseVideoPageListVO.java
  42. 38 42
      fs-service-system/src/main/java/com/fs/qw/service/impl/CustomerTransferApprovalServiceImpl.java
  43. 4 0
      fs-service-system/src/main/java/com/fs/qw/vo/TransferCustomDTO.java
  44. 13 0
      fs-service-system/src/main/java/com/fs/store/domain/FsUser.java
  45. 34 5
      fs-service-system/src/main/java/com/fs/store/domain/FsUserCompanyUser.java
  46. 14 0
      fs-service-system/src/main/java/com/fs/store/domain/FsUserCourseCount.java
  47. 30 0
      fs-service-system/src/main/java/com/fs/store/domain/FsUserProjectTag.java
  48. 2 1
      fs-service-system/src/main/java/com/fs/store/dto/FsUserTransferParamDTO.java
  49. 16 2
      fs-service-system/src/main/java/com/fs/store/mapper/FsUserCompanyUserMapper.java
  50. 6 6
      fs-service-system/src/main/java/com/fs/store/mapper/FsUserMapper.java
  51. 19 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsUserProjectTagMapper.java
  52. 1 1
      fs-service-system/src/main/java/com/fs/store/param/FsStoreProductAddEditParam.java
  53. 8 3
      fs-service-system/src/main/java/com/fs/store/param/h5/FsUserPageListParam.java
  54. 84 8
      fs-service-system/src/main/java/com/fs/store/service/IFsUserCompanyUserService.java
  55. 33 0
      fs-service-system/src/main/java/com/fs/store/service/IFsUserProjectTagService.java
  56. 8 1
      fs-service-system/src/main/java/com/fs/store/service/IFsUserService.java
  57. 154 11
      fs-service-system/src/main/java/com/fs/store/service/impl/FsUserCompanyUserServiceImpl.java
  58. 2 2
      fs-service-system/src/main/java/com/fs/store/service/impl/FsUserCourseCountServiceImpl.java
  59. 105 0
      fs-service-system/src/main/java/com/fs/store/service/impl/FsUserProjectTagServiceImpl.java
  60. 47 103
      fs-service-system/src/main/java/com/fs/store/service/impl/FsUserServiceImpl.java
  61. 7 0
      fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserPageListVO.java
  62. 3 0
      fs-service-system/src/main/java/com/fs/store/vo/h5/UserListPageVO.java
  63. 6 6
      fs-service-system/src/main/resources/application-config-dev.yml
  64. 36 0
      fs-service-system/src/main/resources/db/upgrade/20250625会员关联项目.sql
  65. 32 0
      fs-service-system/src/main/resources/mapper/company/CompanyTagMapper.xml
  66. 44 6
      fs-service-system/src/main/resources/mapper/company/CompanyTagUserMapper.xml
  67. 5 4
      fs-service-system/src/main/resources/mapper/company/CompanyUserChangeApplyUserMapper.xml
  68. 11 2
      fs-service-system/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  69. 1 0
      fs-service-system/src/main/resources/mapper/course/FsUserCourseMapper.xml
  70. 13 4
      fs-service-system/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml
  71. 0 1
      fs-service-system/src/main/resources/mapper/store/FsStoreProductMapper.xml
  72. 75 1
      fs-service-system/src/main/resources/mapper/store/FsUserCompanyUserMapper.xml
  73. 12 4
      fs-service-system/src/main/resources/mapper/store/FsUserCourseCountMapper.xml
  74. 279 169
      fs-service-system/src/main/resources/mapper/store/FsUserMapper.xml
  75. 29 0
      fs-service-system/src/main/resources/mapper/store/FsUserProjectTagMapper.xml
  76. 15 1
      fs-user-app/src/main/java/com/fs/app/controller/UserController.java
  77. 11 72
      fs-user-app/src/main/java/com/fs/app/controller/WxCompanyUserController.java
  78. 10 10
      fs-user-app/src/main/java/com/fs/app/controller/WxH5MpController.java
  79. 3 0
      fs-user-app/src/main/java/com/fs/app/param/FsUserLoginByMpParam.java
  80. 4 0
      fs-user-app/src/main/java/com/fs/app/param/LoginMaWxParam.java

+ 3 - 2
fs-admin/src/main/java/com/fs/company/controller/CompanyUserController.java

@@ -9,6 +9,7 @@ import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.CompanyUser;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.his.vo.OptionsVO;
 import com.github.pagehelper.PageHelper;
@@ -163,9 +164,9 @@ public class CompanyUserController extends BaseController
     @PreAuthorize("@ss.hasPermi('company:companyUser:change')")
     @Log(title = "更换会员归属", businessType = BusinessType.OTHER)
     @PostMapping("/changeCompanyUser")
-    public AjaxResult changeCompanyUser(@RequestBody List<Long> userIds, @RequestParam Long companyUserId, @RequestParam Long companyId)
+    public AjaxResult changeCompanyUser(@RequestBody List<UserProjectDTO> users, @RequestParam Long companyUserId, @RequestParam Long companyId)
     {
-        return toAjax(companyUserService.changeCompanyUser(userIds, companyUserId, companyId));
+        return toAjax(companyUserService.changeCompanyUser(users, companyUserId, companyId));
     }
 
 

+ 13 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCourseVideoController.java

@@ -10,11 +10,13 @@ import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.core.security.LoginUser;
 import com.fs.core.web.service.TokenService;
+import com.fs.course.domain.FsUserCourse;
 import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
 import com.fs.course.param.BatchRedUpdate;
 import com.fs.course.param.BatchVideoSvae;
 import com.fs.course.param.CourseVideoUpdates;
+import com.fs.course.service.IFsUserCourseService;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.his.vo.OptionsVO;
 import com.github.pagehelper.PageHelper;
@@ -45,6 +47,8 @@ public class FsUserCourseVideoController extends BaseController
 
     @Autowired
     private TokenService tokenService;
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
 
     /**
      * 查询课堂视频列表
@@ -101,6 +105,10 @@ public class FsUserCourseVideoController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         Long userId = loginUser.getUser().getUserId();
         fsUserCourseVideo.setUserId(userId);
+
+        // 设置项目ID
+        FsUserCourse fsUserCourse = fsUserCourseService.selectFsUserCourseByCourseId(fsUserCourseVideo.getCourseId());
+        fsUserCourseVideo.setProjectId(fsUserCourse.getProject());
         return toAjax(fsUserCourseVideoService.insertFsUserCourseVideo(fsUserCourseVideo));
     }
 
@@ -154,6 +162,11 @@ public class FsUserCourseVideoController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         Long userId = loginUser.getUser().getUserId();
         vo.setUserId(userId);
+
+        // 设置项目ID
+        FsUserCourse fsUserCourse = fsUserCourseService.selectFsUserCourseByCourseId(vo.getCourseId());
+        vo.setProjectId(fsUserCourse.getProject());
+
         fsUserCourseVideoService.batchSaveVideo(vo);
         return R.ok();
     }

+ 23 - 3
fs-admin/src/main/java/com/fs/store/controller/FsUserController.java

@@ -1,5 +1,6 @@
 package com.fs.store.controller;
 
+import com.alibaba.fastjson.JSON;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -9,14 +10,16 @@ import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.store.domain.FsUser;
 import com.fs.store.param.h5.FsUserPageListParam;
+import com.fs.store.service.IFsUserCompanyUserService;
 import com.fs.store.service.IFsUserService;
 import com.fs.store.vo.FSUserVO;
 import com.fs.store.vo.h5.FsUserPageListVO;
-import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -31,12 +34,15 @@ import java.util.Map;
  * @author fs
  * @date 2022-03-15
  */
+@Slf4j
 @RestController
 @RequestMapping("/store/user")
 public class FsUserController extends BaseController
 {
     @Autowired
     private IFsUserService fsUserService;
+    @Autowired
+    private IFsUserCompanyUserService userCompanyUserService;
 
     /**
      * 查询用户列表
@@ -53,6 +59,19 @@ public class FsUserController extends BaseController
         return getDataTable(list);
     }
 
+    @PreAuthorize("@ss.hasPermi('store:user:list')")
+    @GetMapping("/listProject")
+    public TableDataInfo listProject(FsUser fsUser)
+    {
+        startPage();
+        List<FSUserVO> list = fsUserService.selectFsUserVOListByProject(fsUser);
+        for (FSUserVO vo : list){
+            vo.setPhone(ParseUtils.parsePhone(vo.getPhone()));
+        }
+        return getDataTable(list);
+    }
+
+
     /**
      * 导出用户列表
      */
@@ -174,8 +193,9 @@ public class FsUserController extends BaseController
     @PreAuthorize("@ss.hasPermi('store:user:enabledUsers')")
     @PostMapping("/enabledUsers")
     @ApiOperation("批量启用会员")
-    public ResponseResult<Boolean> enabledUsers(@RequestBody String[] ids) {
-        Boolean r = fsUserService.disabledUser(ids, true);
+    public ResponseResult<Boolean> enabledUsers(@RequestBody List<UserProjectDTO> ids) {
+        log.debug("批量启用会员 ids: {}", JSON.toJSONString(ids));
+        Boolean r = userCompanyUserService.batchUpdateUserProjectStatus(ids, 1);
         return ResponseResult.ok(r);
     }
 }

+ 6 - 27
fs-company-app/src/main/java/com/fs/app/controller/AppBaseController.java

@@ -1,43 +1,16 @@
 package com.fs.app.controller;
 
 
-import com.fs.app.annotation.Login;
 import com.fs.app.exception.FSException;
 import com.fs.app.utils.JwtUtils;
-import com.fs.common.config.FSConfig;
-import com.fs.common.core.domain.AjaxResult;
-import com.fs.common.core.domain.R;
-import com.fs.common.core.domain.entity.SysUser;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
-import com.fs.common.utils.file.FileUploadUtils;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyUserService;
-import com.fs.core.config.ServerConfig;
-import com.fs.crm.domain.CrmEvent;
-import com.fs.crm.service.ICrmCustomerService;
-import com.fs.crm.service.ICrmCustomerUserService;
-import com.fs.crm.service.ICrmEventService;
-import com.fs.crm.service.ICrmMsgService;
 import com.fs.store.domain.FsUser;
 import com.fs.store.service.IFsUserService;
-import com.fs.system.service.ISysConfigService;
-import com.fs.system.service.ISysDictDataService;
-import com.fs.system.service.ISysUserService;
-import com.fs.system.vo.DictVO;
-import com.fs.voice.service.IVoiceService;
-import com.github.xiaoymin.swaggerbootstrapui.util.CommonUtils;
 import io.jsonwebtoken.Claims;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
-
-import javax.servlet.http.HttpServletRequest;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
 
 
 public class AppBaseController {
@@ -64,6 +37,12 @@ public class AppBaseController {
 		String userId = claims.getSubject().toString();
 		return userId;
 	}
+	public Long getCompanyUserId() {
+		String headValue =  ServletUtils.getRequest().getHeader("APPToken");
+		Claims claims=jwtUtils.getClaimByToken(headValue);
+		String userId = claims.getSubject();
+		return Long.parseLong(userId);
+	}
 	//获取商城手机号
 	public Long getUserId(Long companyUserId)
 	{

+ 27 - 10
fs-company-app/src/main/java/com/fs/app/controller/CompanyTagController.java

@@ -1,20 +1,22 @@
 package com.fs.app.controller;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.app.annotation.Login;
+import com.fs.app.param.TagProjectParam;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.CompanyTag;
 import com.fs.company.service.ICompanyTagService;
 import com.fs.company.service.ICompanyTagUserService;
-import com.fs.company.vo.CompanyTagUserVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.HashMap;
@@ -39,8 +41,9 @@ public class CompanyTagController extends AppBaseController {
     @ApiOperation("查询公司标签列表")
     public R list(@RequestParam(required = false) String keyword,
                   @RequestParam(required = false, defaultValue = "1") Integer pageNum,
-                  @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
-        log.debug("查询公司标签列表 keyword: {}, pageNum: {}, pageSize: {}", keyword, pageNum, pageSize);
+                  @RequestParam(required = false, defaultValue = "10") Integer pageSize,
+                  @RequestParam(required = false) Long companyUserId) {
+        log.debug("查询公司标签列表 keyword: {}, pageNum: {}, pageSize: {}", keyword, pageNum, pageSize,companyUserId);
 
         Map<String, Object> params = new HashMap<>();
         params.put("companyId", getCompanyId());
@@ -49,8 +52,15 @@ public class CompanyTagController extends AppBaseController {
         }
 
         PageHelper.startPage(pageNum, pageSize);
-        List<CompanyTag> list = companyTagService.selectCompanyTagListByMap(params);
-        return R.ok().put("data", new PageInfo<>(list));
+        if (ObjectUtil.isNotEmpty(companyUserId)){
+            params.put("companyUserId",companyUserId);
+            List<CompanyTag> list = companyTagService.selectCompanyTagByList(params);
+            return R.ok().put("data", new PageInfo<>(list));
+        }else {
+            List<CompanyTag> list = companyTagService.selectCompanyTagListByMap(params);
+            return R.ok().put("data", new PageInfo<>(list));
+        }
+
     }
 
     /**
@@ -79,12 +89,19 @@ public class CompanyTagController extends AppBaseController {
     }
 
     @Login
-    @GetMapping("/tagSubUsers")
+    @PostMapping("/tagSubUsers")
     @ApiOperation("标签下会员列表")
-    public R tagSubUsers(@RequestParam List<Long> tagId) {
+    public R tagSubUsers(@RequestBody TagProjectParam tagProjectParam) {
         Map<String, Object> params = new HashMap<>();
-        params.put("tagIds", tagId);
-        params.put("companyId", getCompanyId());
-        return R.ok().put("data", companyTagUserService.selectUserListByMap(params));
+        params.put("tagIds", tagProjectParam.getTagIds());
+        if (CollectionUtils.isNotEmpty(tagProjectParam.getProjectIds())){
+            params.put("projectIds", tagProjectParam.getProjectIds());
+        }
+        if (ObjectUtil.isEmpty(tagProjectParam.getCompanyUserId())){
+            params.put("companyUserId", getCompanyUserId());
+        }else {
+            params.put("companyUserId", tagProjectParam.getCompanyUserId());
+        }
+        return R.ok().put("data", companyTagUserService.selectUserByMap(params));
     }
 }

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

@@ -1,13 +1,11 @@
 package com.fs.app.controller;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.app.annotation.Login;
-import com.fs.app.param.ChangeUserDeptAndPostParam;
-import com.fs.app.param.CompanyUserChangeApplyParam;
-import com.fs.app.param.CompanyUserParam;
-import com.fs.app.param.CompanyUserUpdateParam;
+import com.fs.app.param.*;
 import com.fs.app.service.IAppService;
 import com.fs.app.vo.CompanySubUserVO;
 import com.fs.common.annotation.RepeatSubmit;
@@ -17,15 +15,18 @@ import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.PatternUtils;
 import com.fs.common.utils.bean.BeanUtils;
 import com.fs.company.domain.*;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.company.mapper.CompanyRoleMapper;
-import com.fs.company.service.ICompanyDeptService;
-import com.fs.company.service.ICompanyService;
-import com.fs.company.service.ICompanyUserChangeApplyService;
-import com.fs.company.service.ICompanyUserService;
+import com.fs.company.service.*;
+import com.fs.company.vo.CompanyTagUserVO;
 import com.fs.company.vo.CompanyUserChangeApplyVO;
 import com.fs.core.security.SecurityUtils;
 import com.fs.course.service.IFsCourseRedPacketLogService;
 import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.store.domain.FsUserCompanyUser;
+import com.fs.store.service.IFsUserCompanyUserService;
+import com.fs.system.service.ISysDictDataService;
+import com.fs.system.vo.DictVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -33,6 +34,8 @@ import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
@@ -57,7 +60,10 @@ public class CompanyUserController extends AppBaseController {
     private final ICompanyUserChangeApplyService companyUserChangeApplyService;
     private final CompanyRoleMapper companyRoleMapper;
     private final IAppService appService;
-
+    private final IFsUserCompanyUserService fsUserCompanyUserService;
+    private final ICompanyTagUserService companyTagUserService;
+    @Autowired
+    private ISysDictDataService dictDataService;
     @Login
     @ApiOperation("查询用户列表")
     @GetMapping("/getCompanyUserList")
@@ -165,13 +171,18 @@ public class CompanyUserController extends AppBaseController {
     @ApiOperation("注册")
     @PostMapping("/resisterCompanyUser")
     public R resisterCompanyUser(@Valid @RequestBody CompanyUserParam param) {
-        Company company = companyService.selectCompanyById(param.getCompanyId());
+        CompanyUser upCompanyUser = companyUserService.selectCompanyUserById(param.getCompanyUserId());
+        if (Objects.isNull(upCompanyUser)) {
+            return R.error("邀请销售不存在");
+        }
+
+        Company company = companyService.selectCompanyById(upCompanyUser.getCompanyId());
         if (Objects.isNull(company)) {
             return R.error("公司不存在");
         }
 
         // 判断用户数量是否已达到上线
-        Integer count = companyUserService.selectCompanyUserCountByCompanyId(param.getCompanyId());
+        Integer count = companyUserService.selectCompanyUserCountByCompanyId(upCompanyUser.getCompanyId());
         if(count > company.getLimitUserCount()) {
             return R.error("用户数量已达到上限");
         }
@@ -195,9 +206,10 @@ public class CompanyUserController extends AppBaseController {
         companyUser.setPassword(SecurityUtils.encryptPassword(companyUser.getPassword()));
         companyUser.setCreateTime(new Date());
         companyUser.setIsAudit(0);
+        companyUser.setParentId(upCompanyUser.getUserId());
 
         // 部门
-        CompanyDept dept = companyDeptService.getDefaultCompanyDeptByCompanyId(param.getCompanyId());
+        CompanyDept dept = companyDeptService.getDefaultCompanyDeptByCompanyId(upCompanyUser.getCompanyId());
         if (Objects.nonNull(dept)) {
             companyUser.setDeptId(dept.getDeptId());
         }
@@ -221,7 +233,7 @@ public class CompanyUserController extends AppBaseController {
     @RepeatSubmit
     @ApiOperation("更换会员归属申请")
     @PostMapping("/changeUserParentApply")
-    public R changeVipUser(@Valid @RequestBody CompanyUserChangeApplyParam param) {
+    public R changeVipUser(@RequestBody CompanyUserChangeApplyParam param) {
         // 参数校验
         CompanyUser fromUser = companyUserService.selectCompanyUserById(param.getFrom());
         if (Objects.isNull(fromUser)) {
@@ -233,12 +245,18 @@ public class CompanyUserController extends AppBaseController {
             throw new ServiceException("申请更换归属销售不存在");
         }
 
+        if (Objects.equals(fromUser.getUserId(), toUser.getUserId())) {
+            throw new ServiceException("申请更换归属销售不能与原归属销售相同");
+        }
+
         if (param.getType() != 0 && param.getType() != 1) {
             throw new ServiceException("类型不正确");
         }
 
-        if (param.getType() == 1 && (Objects.isNull(param.getIds()) || param.getIds().isEmpty())) {
-            throw new ServiceException("请先选择会员");
+        if (param.getType() == 1 && (CollectionUtils.isEmpty(param.getIds()))
+                &&(CollectionUtils.isEmpty(param.getProject()))
+        &&(CollectionUtils.isEmpty(param.getTagList()))) {
+            throw new ServiceException("请先选择会员!");
         }
 
         // 存在待审核的申请不能再次申请
@@ -248,14 +266,59 @@ public class CompanyUserController extends AppBaseController {
         if (companyUserChangeApplyService.count(applyWrapper) > 0) {
             throw new ServiceException("存在待审核申请");
         }
-
         CompanyUser companyUser = companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
-
+        List<UserProjectDTO> list = param.getIds();
+        if(CollectionUtils.isEmpty(list)){
+            List<UserProjectDTO> userProjectDTOS = addUserId(param.getProject(),param.getTagList(),param.getFrom());
+            if (CollectionUtils.isNotEmpty(userProjectDTOS)){
+                list.addAll(userProjectDTOS);
+            }
+        }
         // 添加申请
-        companyUserChangeApplyService.apply(param.getFrom(), param.getTo(), param.getType(), param.getIds(), companyUser.getCompanyId(), companyUser.getUserName());
+        companyUserChangeApplyService.apply(param.getFrom(), param.getTo(), param.getType(),list, companyUser.getCompanyId(), companyUser.getUserName());
         return R.ok();
     }
 
+    /**
+     * 查询标签和项目中的会员添加
+     * @param project
+     * @param tagList
+     * @param companyUserId
+     * @return
+     */
+    private List<UserProjectDTO> addUserId(List<Long> projects, List<Long> tagList, Long companyUserId) {
+        // Prepare parameters map
+        Map<String, Object> params = new HashMap<>();
+
+        if (CollectionUtils.isNotEmpty(projects)) {
+            params.put("projectIds", projects);
+        } else {
+            params.put("tagIds", tagList);
+        }
+
+        // Set company user ID
+        Long effectiveCompanyUserId = ObjectUtil.isNotEmpty(companyUserId)
+                ? companyUserId
+                : getCompanyUserId();
+        params.put("companyUserId", effectiveCompanyUserId);
+
+        // Get data from service
+        List<CompanyTagUserVO> voList = companyTagUserService.selectUserByMap(params);
+        if (CollectionUtils.isEmpty(voList)) {
+            return Collections.emptyList();
+        }
+
+        // Transform data
+        return voList.stream()
+                .map(vo -> {
+                    UserProjectDTO dto = new UserProjectDTO();
+                    dto.setUserId(vo.getUserId());
+                    dto.setProjectId(vo.getProjectId());
+                    return dto;
+                })
+                .collect(Collectors.toList());
+    }
+
     @Login
     @ApiOperation("申请列表")
     @GetMapping("/applyList")
@@ -309,4 +372,23 @@ public class CompanyUserController extends AppBaseController {
         appService.changeUserDeptAndPost(param);
         return R.ok();
     }
+
+    @ApiOperation("查询指定公司中存在会员的项目")
+    @PostMapping("/getDictByKeyByProject")
+    public R getDictByKey(
+            @RequestBody TagProjectParam tagProjectParam){
+        List<DictVO> dictVOS=dictDataService.selectDictDataListByType("sys_course_project");
+        Map<String,Object> param = new HashMap<>();
+        param.put("tagIds",tagProjectParam.getTagIds());
+        param.put("companyUserId",tagProjectParam.getCompanyUserId());
+        List<Long> projectIds =fsUserCompanyUserService.selectFsUserCompanyUserList(param);
+        if (CollectionUtils.isEmpty(projectIds)){
+            return R.ok().put("data",new ArrayList<DictVO>());
+        }
+
+        List<DictVO> filteredDictVOS = dictVOS.stream()
+                .filter(dictVO -> projectIds.contains(Long.parseLong(dictVO.getDictValue())))
+                .collect(Collectors.toList());
+        return R.ok().put("data",filteredDictVOS);
+    }
 }

+ 45 - 29
fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.fs.app.annotation.Login;
 import com.fs.app.config.ImageStorageConfig;
+import com.fs.app.param.FsUserProjectUpdateParam;
 import com.fs.app.param.FsUserTagUpdateParam;
 import com.fs.app.param.FsUserUpdateParam;
 import com.fs.common.core.domain.R;
@@ -12,28 +13,25 @@ import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.CompanyUser;
-import com.fs.company.service.ICompanyTagUserService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.param.newfs.FsUserCourseBeMemberImageParam;
 import com.fs.course.param.newfs.FsUserCourseBeMemberParam;
 import com.fs.course.service.IFsUserCourseService;
 import com.fs.course.vo.newfs.FsCourseAnalysisVO;
-import com.fs.store.param.h5.CourseAnalysisParam;
 import com.fs.store.domain.FsUser;
+import com.fs.store.domain.FsUserCompanyUser;
+import com.fs.store.param.h5.CourseAnalysisParam;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.param.h5.TagListParam;
 import com.fs.store.param.h5.UserStatisticsCommonParam;
+import com.fs.store.service.IFsUserCompanyUserService;
 import com.fs.store.service.IFsUserCourseCountService;
+import com.fs.store.service.IFsUserProjectTagService;
 import com.fs.store.service.IFsUserService;
 import com.fs.store.vo.h5.*;
 import com.fs.system.service.ISysConfigService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
-import com.google.zxing.BarcodeFormat;
-import com.google.zxing.WriterException;
-import com.google.zxing.client.j2se.MatrixToImageWriter;
-import com.google.zxing.common.BitMatrix;
-import com.google.zxing.qrcode.QRCodeWriter;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
@@ -43,11 +41,13 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
 import java.io.InputStream;
-import java.text.ParseException;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
-import java.util.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
 @Slf4j
 @Api(tags = "用户会员相关接口")
@@ -61,9 +61,6 @@ public class FsUserController extends AppBaseController {
     @Autowired
     private ICompanyUserService companyUserService;
 
-    @Autowired
-    private ICompanyTagUserService companyTagUserService;
-
     @Autowired
     private ImageStorageConfig imageConfig;
 
@@ -76,10 +73,16 @@ public class FsUserController extends AppBaseController {
     @Autowired
     private ISysConfigService configService;
 
+    @Autowired
+    private IFsUserCompanyUserService userCompanyUserService;
+    @Autowired
+    private IFsUserProjectTagService userProjectTagService;
+
     @Login
     @PostMapping("/pageList")
     @ApiOperation("用户会员分页列表")
     public ResponseResult<PageInfo<FsUserPageListVO>> pageList(@RequestBody FsUserPageListParam param) {
+        log.debug("用户会员分页列表 param: {}", JSON.toJSONString(param));
         param.setUserId(Long.parseLong(getUserId()));
 //        PageHelper.startPage(param.getPageNum(), param.getPageSize());
         PageInfo<FsUserPageListVO> fsUserPageListVOPageInfo = fsUserService.selectFsUserPageList(param);
@@ -107,8 +110,9 @@ public class FsUserController extends AppBaseController {
     @GetMapping("/details")
     @ApiOperation("用户会员详情")
     public ResponseResult<UserDetailsVO> getUserDetails(@ApiParam(value = "用户id", required = true) @RequestParam Long userId,
+                                                        @ApiParam(value = "用户项目关联id", required = true) @RequestParam Long userCompanyId,
                                                         @ApiParam(value = "时间tab,不传表示查询全部,分别是:今天、昨天、前天、近七天", required = true) @RequestParam(required = false) String dateTag) {
-        UserDetailsVO userDetails = fsUserService.getUserDetails(Long.parseLong(getUserId()), userId, dateTag);
+        UserDetailsVO userDetails = fsUserService.getUserDetails(Long.parseLong(getUserId()), userId, dateTag,userCompanyId);
         return ResponseResult.ok(userDetails);
     }
 
@@ -116,9 +120,10 @@ public class FsUserController extends AppBaseController {
     @GetMapping("/tagList")
     @ApiOperation("用户会员标签列表")
     public ResponseResult<PageInfo<CompanyUserTagListVO>> getTagList(TagListParam param) {
+        log.debug("用户会员标签列表 param: {}", JSON.toJSONString(param));
         param.setUserId(Long.parseLong(getUserId()));
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
-        List<CompanyUserTagListVO> tagList = companyTagUserService.getTagList(param);
+        List<CompanyUserTagListVO> tagList = userProjectTagService.getTagList(param);
         CompanyUserTagListVO noTag = new CompanyUserTagListVO();
         noTag.setTagId(0L);
         noTag.setTagName("无标签");
@@ -130,22 +135,18 @@ public class FsUserController extends AppBaseController {
     @Login
     @PostMapping("/disabled")
     @ApiOperation("批量禁用会员")
-    public ResponseResult<Boolean> disabledUser(@ApiParam(value = "联系人id集合", required = true) @RequestBody String[] ids) {
-        Boolean r = fsUserService.disabledUser(ids, false);
+    public ResponseResult<Boolean> disabledUser(@ApiParam(value = "联系人id集合", required = true) @RequestBody List<Long> userCompanyUserIds) {
+        log.debug("批量禁用会员 ids: {}", JSON.toJSONString(userCompanyUserIds));
+        Boolean r = userCompanyUserService.batchUpdateStatus(userCompanyUserIds, 2);
         return ResponseResult.ok(r);
     }
 
     @Login
     @PostMapping("/enabled")
     @ApiOperation("批量启用会员")
-    public ResponseResult<Boolean> enabledUser(@ApiParam(value = "联系人id集合", required = true) @RequestBody String[] ids) {
-        // 如果存在重粉的数据,则禁止启用,需要提示
-        long companyUserId = Long.parseLong(getUserId());
-        Integer count = fsUserService.selectFsUserByUserIds(ids, companyUserId);
-        if(count > 0){
-            return ResponseResult.fail(400, "重粉会员不能移除小黑屋");
-        }
-        Boolean r = fsUserService.disabledUser(ids, true);
+    public ResponseResult<Boolean> enabledUser(@ApiParam(value = "联系人id集合", required = true) @RequestBody List<Long> userCompanyUserIds) {
+        log.debug("批量启用会员 ids: {}", JSON.toJSONString(userCompanyUserIds));
+        Boolean r = userCompanyUserService.batchUpdateStatus(userCompanyUserIds, 1);
         return ResponseResult.ok(r);
     }
 
@@ -252,26 +253,41 @@ public class FsUserController extends AppBaseController {
     }
 
     @Login
-    @ApiOperation("修改用户备注、姓名")
+    @ApiOperation("修改用户姓名")
     @PostMapping("/changeUserInfo")
     public ResponseResult<Object> changeUserInfo(@Valid @RequestBody FsUserUpdateParam param) {
-        log.debug("修改用户备注、姓名 param:{}", JSON.toJSONString(param));
+        log.debug("修改用户姓名 param:{}", JSON.toJSONString(param));
         FsUser fsUser = fsUserService.selectFsUserById(param.getFsUserId());
         if (Objects.isNull(fsUser)) {
             throw new ServiceException("用户不存在");
         }
 
         fsUser.setNickname(param.getNickName());
-        fsUser.setRemark(param.getRemark());
         fsUserService.updateFsUser(fsUser);
         return ResponseResult.ok();
     }
 
+    @Login
+    @ApiOperation("修改用户项目备注")
+    @PostMapping("/changeUserRemark")
+    public ResponseResult<Object> changeUserProjectRemark(@Valid @RequestBody FsUserProjectUpdateParam param) {
+        log.debug("修改用户备注 param:{}", JSON.toJSONString(param));
+        FsUserCompanyUser userCompanyUser = userCompanyUserService.selectFsUserCompanyUserById(param.getUserCompanyUserId());
+        if (Objects.isNull(userCompanyUser)) {
+            throw new ServiceException("用户不存在");
+        }
+
+        userCompanyUser.setRemark(param.getRemark());
+        userCompanyUserService.updateFsUserCompanyUser(userCompanyUser);
+        return ResponseResult.ok();
+    }
+
     @Login
     @ApiOperation("修改用户标签")
     @PostMapping("/changeUserTags")
     public ResponseResult<Object> changeUserTags(@Valid @RequestBody FsUserTagUpdateParam param) {
-        companyTagUserService.changeUserTags(param.getFsUserIds(), param.getTagIds());
+        log.debug("修改用户标签 param:{}", JSON.toJSONString(param));
+        userProjectTagService.addUserProjectTag(param.getUserCompanyUserIds(), param.getTagIds());
         return ResponseResult.ok();
     }
 
@@ -309,10 +325,10 @@ public class FsUserController extends AppBaseController {
         return ResponseResult.ok(fsUserService.companyUserSummaryCount(userId, companyUserId));
     }
 
-//    @Login
     @ApiOperation("会员关联绑定销售")
     @PostMapping("/beMember")
     public ResponseResult<Boolean> becomeMember(@Valid @RequestBody FsUserCourseBeMemberParam param) {
+        log.debug("会员关联绑定销售 param:{}", JSON.toJSONString(param));
         return fsUserService.becomeMember(param);
     }
 

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

@@ -131,6 +131,7 @@ public class FsUserCourseVideoController extends AppBaseController {
 
         PageHelper.startPage(pageNum, pageSize);
         List<FsUserCourseParticipationRecordVO> record = fsUserCourseService.getParticipationRecordByMap(params);
+        System.out.println("所有数据:"+record);
         return ResponseResult.ok(new PageInfo<>(record));
     }
 

+ 14 - 1
fs-company-app/src/main/java/com/fs/app/param/CompanyUserChangeApplyParam.java

@@ -1,5 +1,6 @@
 package com.fs.app.param;
 
+import com.fs.company.dto.UserProjectDTO;
 import lombok.Data;
 
 import javax.validation.constraints.NotNull;
@@ -22,8 +23,20 @@ public class CompanyUserChangeApplyParam {
      */
     @NotNull(message = "类型不能为空")
     private Integer type;
+
+    /**
+     * 项目id
+     */
+    private List<Long> project;
+
+
+    /**
+     * 标签id
+     */
+    private List<Long> tagList;
+
     /**
      * 需更换归属会员id集合
      */
-    private List<Long> ids;
+    private List<UserProjectDTO> ids;
 }

+ 5 - 4
fs-company-app/src/main/java/com/fs/app/param/CompanyUserParam.java

@@ -1,5 +1,6 @@
 package com.fs.app.param;
 
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import javax.validation.constraints.NotBlank;
@@ -7,12 +8,12 @@ import javax.validation.constraints.NotNull;
 
 @Data
 public class CompanyUserParam {
-
     /**
-     * 公司ID
+     * 上级销售
      */
-    @NotNull(message = "公司ID不能为空")
-    private Long companyId;
+    @NotNull(message = "上级销售不能为空")
+    @ApiModelProperty("上级销售不能为空")
+    private Long companyUserId;
     /**
      * 手机号码
      */

+ 17 - 0
fs-company-app/src/main/java/com/fs/app/param/FsUserProjectUpdateParam.java

@@ -0,0 +1,17 @@
+package com.fs.app.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+public class FsUserProjectUpdateParam {
+
+    @ApiModelProperty("用户项目ID")
+    @NotNull(message = "用户项目ID不能为空")
+    private Long userCompanyUserId;
+
+    @ApiModelProperty("备注")
+    private String remark;
+}

+ 3 - 3
fs-company-app/src/main/java/com/fs/app/param/FsUserTagUpdateParam.java

@@ -11,9 +11,9 @@ public class FsUserTagUpdateParam {
     /**
      * 用户ID
      */
-    @ApiModelProperty("用户ID集合")
-    @NotEmpty(message = "用户ID不能为空")
-    private List<Long> fsUserIds;
+    @ApiModelProperty("用户项目ID集合")
+    @NotEmpty(message = "用户项目ID不能为空")
+    private List<Long> userCompanyUserIds;
     /**
      * 标签ID
      */

+ 0 - 5
fs-company-app/src/main/java/com/fs/app/param/FsUserUpdateParam.java

@@ -18,9 +18,4 @@ public class FsUserUpdateParam {
      */
     @ApiModelProperty("用户昵称")
     private String nickName;
-    /**
-     * 用户备注
-     */
-    @ApiModelProperty("用户备注")
-    private String remark;
 }

+ 24 - 0
fs-company-app/src/main/java/com/fs/app/param/TagProjectParam.java

@@ -0,0 +1,24 @@
+package com.fs.app.param;
+
+import lombok.Data;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.List;
+
+@Data
+public class TagProjectParam {
+    /**
+     * 销售id
+     */
+    private Long companyUserId;
+
+    /**
+     * 标签id
+     */
+    private List<Long> tagIds;
+
+    /**
+     * 项目id
+     */
+    private List<Long> projectIds;
+}

+ 11 - 9
fs-company/src/main/java/com/fs/users/controller/FsUserController.java

@@ -1,5 +1,6 @@
 package com.fs.users.controller;
 
+import com.alibaba.fastjson.JSON;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -8,16 +9,18 @@ import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.core.security.LoginUser;
 import com.fs.core.web.service.TokenService;
 import com.fs.store.domain.FsUser;
 import com.fs.store.param.h5.FsUserPageListParam;
+import com.fs.store.service.IFsUserCompanyUserService;
 import com.fs.store.service.IFsUserService;
 import com.fs.store.vo.FsCompanyUserListQueryVO;
 import com.fs.store.vo.h5.FsUserPageListVO;
-import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -32,6 +35,7 @@ import java.util.Map;
  * @author fs
  * @date 2022-03-15
  */
+@Slf4j
 @RestController
 @RequestMapping("/users/user")
 public class FsUserController extends BaseController
@@ -40,6 +44,9 @@ public class FsUserController extends BaseController
     private IFsUserService fsUserService;
     @Autowired
     private TokenService tokenService;
+    @Autowired
+    private IFsUserCompanyUserService userCompanyUserService;
+
     /**
      * 查询用户列表
      */
@@ -144,14 +151,9 @@ public class FsUserController extends BaseController
     @PreAuthorize("@ss.hasPermi('users:user:enabledUsers')")
     @PostMapping("/enabledUsers")
     @ApiOperation("批量启用会员")
-    public ResponseResult<Boolean> enabledUsers(@RequestBody String[] ids) {
-        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        Integer count = fsUserService.selectFsUserByUserIds(ids, loginUser.getUser().getUserId());
-        if(count > 0){
-            return ResponseResult.fail(400, "重粉会员不能移除小黑屋");
-        }
-
-        Boolean r = fsUserService.disabledUser(ids, true);
+    public ResponseResult<Boolean> enabledUsers(@RequestBody List<UserProjectDTO> ids) {
+        log.debug("批量启用会员 ids: {}", JSON.toJSONString(ids));
+        Boolean r = userCompanyUserService.batchUpdateUserProjectStatus(ids, 1);
         return ResponseResult.ok(r);
     }
 }

+ 1 - 0
fs-qw-task/src/main/java/com/fs/app/task/UserCourseWatchCountTask.java

@@ -29,6 +29,7 @@ public class UserCourseWatchCountTask {
     /**
      * 每20分钟执行一次
      */
+//    @Scheduled(fixedRate = 1000)
     @Scheduled(cron = "0 */15 * * * ?")  // 每15分钟执行一次
     public void userCourseCountTask() {
         try {

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

@@ -21,4 +21,8 @@ public class CompanyUserChangeApplyUser {
      * 用户ID
      */
     private Long userId;
+    /**
+     * 项目ID
+     */
+    private Long projectId;
 }

+ 15 - 0
fs-service-system/src/main/java/com/fs/company/dto/UserProjectDTO.java

@@ -0,0 +1,15 @@
+package com.fs.company.dto;
+
+import lombok.Data;
+
+@Data
+public class UserProjectDTO {
+    /**
+     * 会员ID
+     */
+    private Long userId;
+    /**
+     * 项目ID
+     */
+    private Long projectId;
+}

+ 2 - 0
fs-service-system/src/main/java/com/fs/company/mapper/CompanyTagMapper.java

@@ -81,4 +81,6 @@ public interface CompanyTagMapper
     String findUserTagByUserId(@Param("userId") Long userId,@Param("companyUserId") Long companyUserId);
     @MapKey("tagId")
     Map<Long,CompanyTag> queryAllTagMap();
+
+    List<CompanyTag> selectCompanyTagByList(@Param("params") Map<String, Object> params);
 }

+ 2 - 0
fs-service-system/src/main/java/com/fs/company/mapper/CompanyTagUserMapper.java

@@ -79,4 +79,6 @@ public interface CompanyTagUserMapper
      * @param params    条件
      */
     void deleteCompanyTagUserByMap(@Param("params") Map<String, Object> params);
+
+    List<CompanyTagUserVO> selectUserByMap(@Param("params") Map<String, Object> params);
 }

+ 2 - 0
fs-service-system/src/main/java/com/fs/company/service/ICompanyTagService.java

@@ -79,4 +79,6 @@ public interface ICompanyTagService
     Map<Long,CompanyTag> queryAllTagMap();
 
     String findUserTagByUserId(Long userId,Long companyUserId);
+
+    List<CompanyTag> selectCompanyTagByList(Map<String, Object> params);
 }

+ 16 - 7
fs-service-system/src/main/java/com/fs/company/service/ICompanyTagUserService.java

@@ -1,14 +1,12 @@
 package com.fs.company.service;
 
-import java.util.List;
-import java.util.Map;
-
 import com.fs.company.domain.CompanyTagUser;
 import com.fs.company.vo.CompanyTagUserVO;
 import com.fs.store.param.h5.TagListParam;
 import com.fs.store.vo.h5.CompanyUserTagListVO;
 
-import javax.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Map;
 
 /**
  * companyService接口
@@ -82,8 +80,19 @@ public interface ICompanyTagUserService
 
     /**
      * 修改用户标签
-     * @param fsUserIds 用户ID集合
-     * @param tagIds   标签ID集合
+     *
+     * @param fsUserIds     用户ID集合
+     * @param tagIds        标签ID集合
+     * @param companyId     公司ID
+     * @param companyUserId 销售ID
+     */
+    void changeUserTags(List<Long> fsUserIds, List<Long> tagIds, Long companyId, Long companyUserId);
+
+    /**
+     * 根据条件查询标签下用户
+     *
+     * @param params
+     * @return
      */
-    void changeUserTags(List<Long> fsUserIds, List<Long> tagIds);
+    List<CompanyTagUserVO> selectUserByMap(Map<String, Object> params);
 }

+ 2 - 1
fs-service-system/src/main/java/com/fs/company/service/ICompanyUserChangeApplyService.java

@@ -2,6 +2,7 @@ package com.fs.company.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.company.domain.CompanyUserChangeApply;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.company.vo.CompanyUserChangeApplyVO;
 
 import java.util.List;
@@ -19,7 +20,7 @@ public interface ICompanyUserChangeApplyService extends IService<CompanyUserChan
      * @param companyId 公司ID
      * @param userName  操作用户
      */
-    void apply(Long from, Long to, Integer type, List<Long> ids, Long companyId, String userName);
+    void apply(Long from, Long to, Integer type, List<UserProjectDTO> ids, Long companyId, String userName);
 
     /**
      * 查询申请列表

+ 3 - 2
fs-service-system/src/main/java/com/fs/company/service/ICompanyUserService.java

@@ -2,6 +2,7 @@ package com.fs.company.service;
 
 import com.fs.common.core.domain.R;
 import com.fs.company.domain.CompanyUser;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.company.param.CompanyUserAreaParam;
 import com.fs.company.param.CompanyUserQwParam;
 import com.fs.company.vo.CompanyQwUserByIdsVo;
@@ -184,10 +185,10 @@ public interface ICompanyUserService {
 
     /**
      * 更改会员归属
-     * @param userIds
+     * @param users 需更换归属会员
      * @return
      */
-    int changeCompanyUser(List<Long> userIds, Long companyUserId, Long companyId);
+    int changeCompanyUser(List<UserProjectDTO> users, Long companyUserId, Long companyId);
 
     /**
      * 查询销售选项列表

+ 10 - 0
fs-service-system/src/main/java/com/fs/company/service/impl/CompanyTagServiceImpl.java

@@ -128,4 +128,14 @@ public class CompanyTagServiceImpl implements ICompanyTagService
     public String findUserTagByUserId(Long key,Long companyUserId) {
         return companyTagMapper.findUserTagByUserId(key,companyUserId);
     }
+
+    /**
+     * 查询标签列表
+     * @param params 条件
+     * @return list
+     */
+    @Override
+    public List<CompanyTag> selectCompanyTagByList(Map<String, Object> params) {
+        return companyTagMapper.selectCompanyTagByList(params);
+    }
 }

+ 51 - 15
fs-service-system/src/main/java/com/fs/company/service/impl/CompanyTagUserServiceImpl.java

@@ -1,25 +1,26 @@
 package com.fs.company.service.impl;
 
-import java.util.*;
-import java.util.stream.Collectors;
-
-import com.fs.common.exception.ServiceException;
+import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.CompanyTagUser;
 import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyTagUserMapper;
 import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.company.service.ICompanyTagUserService;
 import com.fs.company.vo.CompanyTagUserVO;
 import com.fs.store.domain.FsUser;
 import com.fs.store.mapper.FsUserMapper;
 import com.fs.store.param.h5.TagListParam;
 import com.fs.store.vo.h5.CompanyUserTagListVO;
+import com.fs.system.mapper.SysDictDataMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.fs.company.mapper.CompanyTagUserMapper;
-import com.fs.company.domain.CompanyTagUser;
-import com.fs.company.service.ICompanyTagUserService;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.*;
+import java.util.stream.Collectors;
+
 /**
  * companyService业务层处理
  *
@@ -36,6 +37,8 @@ public class CompanyTagUserServiceImpl implements ICompanyTagUserService
     private CompanyUserMapper  companyUserMapper;
     @Autowired
     private FsUserMapper fsUserMapper;
+    @Autowired
+    private SysDictDataMapper dictDataMapper;
 
     /**
      * 查询company
@@ -141,17 +144,29 @@ public class CompanyTagUserServiceImpl implements ICompanyTagUserService
      */
     @Override
     public List<CompanyTagUserVO> selectUserListByMap(Map<String, Object> params) {
-        return companyTagUserMapper.selectUserListByMap(params);
+        List<CompanyTagUserVO> companyTagUserVOS = companyTagUserMapper.selectUserListByMap(params);
+
+        // 项目
+        List<SysDictData> courseProject = dictDataMapper.selectDictDataByType("sys_course_project");
+        companyTagUserVOS.forEach(vo -> courseProject.stream()
+                .filter(c -> c.getDictValue().equals(vo.getProjectId().toString()))
+                .findFirst()
+                .ifPresent(c -> vo.setUserName(vo.getUserName() + "-" + (c.getDictLabel()))));
+
+        return companyTagUserVOS;
     }
 
     /**
      * 修改用户标签
-     * @param fsUserIds 用户ID
-     * @param tagIds   标签ID集合
+     *
+     * @param fsUserIds     用户ID
+     * @param tagIds        标签ID集合
+     * @param companyId     公司ID
+     * @param companyUserId 销售ID
      */
     @Transactional(rollbackFor = Exception.class)
     @Override
-    public void changeUserTags(List<Long> fsUserIds, List<Long> tagIds) {
+    public void changeUserTags(List<Long> fsUserIds, List<Long> tagIds, Long companyId, Long companyUserId) {
         fsUserIds.forEach(fsUserId -> {
             FsUser fsUser = fsUserMapper.selectFsUserById(fsUserId);
             if (Objects.isNull(fsUser)) {
@@ -161,20 +176,41 @@ public class CompanyTagUserServiceImpl implements ICompanyTagUserService
             // 删除原标签
             Map<String, Object> params = new HashMap<>();
             params.put("userId", fsUserId);
-            params.put("companyId", fsUser.getCompanyId());
-            params.put("companyUserId", fsUser.getCompanyUserId());
+            params.put("companyId", companyId);
+            params.put("companyUserId", companyUserId);
             companyTagUserMapper.deleteCompanyTagUserByMap(params);
 
             // 不为空则添加新标签
             if (Objects.nonNull(tagIds) && !tagIds.isEmpty()) {
                 CompanyTagUser companyTagUser = new CompanyTagUser();
                 companyTagUser.setUserId(fsUserId);
-                companyTagUser.setCompanyId(fsUser.getCompanyId());
-                companyTagUser.setCompanyUserId(fsUser.getCompanyUserId());
+                companyTagUser.setCompanyId(companyId);
+                companyTagUser.setCompanyUserId(companyUserId);
                 companyTagUser.setTagIds(tagIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
                 companyTagUser.setCreateTime(new Date());
                 companyTagUserMapper.insertCompanyTagUser(companyTagUser);
             }
         });
     }
+
+
+    /**
+     * 根据条件查询标签下用户
+     * @param params    条件
+     * @return  list
+     */
+    @Override
+    public List<CompanyTagUserVO> selectUserByMap(Map<String, Object> params) {
+        List<CompanyTagUserVO> companyTagUserVOS = companyTagUserMapper.selectUserByMap(params);
+
+        // 项目
+        List<SysDictData> courseProject = dictDataMapper.selectDictDataByType("sys_course_project");
+        companyTagUserVOS.forEach(vo -> courseProject.stream()
+                .filter(c -> c.getDictValue().equals(vo.getProjectId().toString()))
+                .findFirst()
+                .ifPresent(c -> vo.setUserName(vo.getUserName() + "-" + (c.getDictLabel()))));
+
+        return companyTagUserVOS;
+    }
+
 }

+ 18 - 12
fs-service-system/src/main/java/com/fs/company/service/impl/CompanyUserChangeApplyServiceImpl.java

@@ -4,25 +4,29 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.common.exception.ServiceException;
 import com.fs.company.domain.CompanyUserChangeApply;
 import com.fs.company.domain.CompanyUserChangeApplyUser;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.company.mapper.CompanyUserChangeApplyMapper;
 import com.fs.company.service.ICompanyUserChangeApplyService;
 import com.fs.company.service.ICompanyUserChangeApplyUserService;
 import com.fs.company.vo.CompanyUserChangeApplyVO;
-import com.fs.store.domain.FsUser;
-import com.fs.store.mapper.FsUserMapper;
+import com.fs.store.domain.FsUserCompanyUser;
+import com.fs.store.service.IFsUserCompanyUserService;
 import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDateTime;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
 @Service
 @AllArgsConstructor
 public class CompanyUserChangeApplyServiceImpl extends ServiceImpl<CompanyUserChangeApplyMapper, CompanyUserChangeApply> implements ICompanyUserChangeApplyService {
 
     private final ICompanyUserChangeApplyUserService companyUserChangeApplyUserService;
-    private final FsUserMapper userMapper;
+    private final IFsUserCompanyUserService userCompanyUserService;
 
     /**
      * 申请更换会员归属
@@ -36,30 +40,32 @@ public class CompanyUserChangeApplyServiceImpl extends ServiceImpl<CompanyUserCh
      */
     @Transactional(rollbackFor = Exception.class)
     @Override
-    public void apply(Long from, Long to, Integer type, List<Long> ids, Long companyId, String userName) {
+    public void apply(Long from, Long to, Integer type, List<UserProjectDTO> ids, Long companyId, String userName) {
         List<CompanyUserChangeApplyUser> users = new ArrayList<>();
 
         // 全部
         if (type == 0) {
-            FsUser param = new FsUser();
-            param.setCompanyUserId(from);
-            List<FsUser> userList = userMapper.selectFsUserList(param);
-            for (FsUser user : userList) {
+            FsUserCompanyUser params = new FsUserCompanyUser();
+            params.setCompanyUserId(from);
+            List<FsUserCompanyUser> userList = userCompanyUserService.selectFsUserCompanyUserList(params);
+            for (FsUserCompanyUser user : userList) {
                 CompanyUserChangeApplyUser uu = new CompanyUserChangeApplyUser();
                 uu.setUserId(user.getUserId());
+                uu.setProjectId(user.getProjectId());
                 users.add(uu);
             }
         }
         // 部分
         else {
-            for (Long id : ids) {
-                FsUser user = userMapper.selectFsUserById(id);
+            for (UserProjectDTO id : ids) {
+                FsUserCompanyUser user = userCompanyUserService.selectByUserIdAndProjectId(id.getUserId(), id.getProjectId());
                 if (Objects.isNull(user)) {
-                    throw new ServiceException("会员不存在");
+                    throw new ServiceException("会员关系不存在");
                 }
 
                 CompanyUserChangeApplyUser uu = new CompanyUserChangeApplyUser();
                 uu.setUserId(user.getUserId());
+                uu.setProjectId(user.getProjectId());
                 users.add(uu);
             }
         }

+ 26 - 1
fs-service-system/src/main/java/com/fs/company/service/impl/CompanyUserChangeApplyUserServiceImpl.java

@@ -2,17 +2,25 @@ package com.fs.company.service.impl;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.company.domain.CompanyUserChangeApplyUser;
+import com.fs.company.mapper.CompanyTcmConsumeMapper;
 import com.fs.company.mapper.CompanyUserChangeApplyUserMapper;
 import com.fs.company.service.ICompanyUserChangeApplyUserService;
 import com.fs.company.vo.CompanyUserChangeApplyUserVO;
+import com.fs.system.mapper.SysDictDataMapper;
+import com.fs.system.vo.DictVO;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 @Service
 public class CompanyUserChangeApplyUserServiceImpl extends ServiceImpl<CompanyUserChangeApplyUserMapper, CompanyUserChangeApplyUser> implements ICompanyUserChangeApplyUserService {
 
+    @Autowired
+    private SysDictDataMapper sysDictDataMapper;
     /**
      * 查询申请记录关联用户
      * @param applyId   申请记录ID
@@ -20,7 +28,24 @@ public class CompanyUserChangeApplyUserServiceImpl extends ServiceImpl<CompanyUs
      */
     @Override
     public List<CompanyUserChangeApplyUserVO> getApplyUsers(Long applyId) {
-        return baseMapper.getApplyUsers(applyId);
+        List<CompanyUserChangeApplyUserVO> list = baseMapper.getApplyUsers(applyId);
+        List<DictVO> dictVOS = sysDictDataMapper.selectDictDataListByType("sys_course_project");
+
+        // Create a map for faster lookup (dictValue -> dictVO)
+        Map<String, String> projectMap = dictVOS.stream()
+                .collect(Collectors.toMap(DictVO::getDictValue, DictVO::getDictLabel));
+
+        // Iterate through the list and set projectName where projectId matches dictValue
+        for (CompanyUserChangeApplyUserVO vo : list) {
+            if (vo.getProjectId() != null) {
+                String projectName = projectMap.get(vo.getProjectId().toString());
+                if (projectName != null) {
+                    vo.setProjectName(projectName);
+                }
+            }
+        }
+
+        return list;
     }
 
     /**

+ 12 - 4
fs-service-system/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java

@@ -8,6 +8,7 @@ import com.fs.common.exception.file.OssException;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.*;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.company.mapper.*;
 import com.fs.company.param.CompanyUserAreaParam;
 import com.fs.company.param.CompanyUserQwParam;
@@ -22,6 +23,7 @@ import com.fs.qw.vo.CompanyUserQwVO;
 import com.fs.qw.vo.QwUserVO;
 import com.fs.store.mapper.FsUserMapper;
 import com.fs.store.service.IFsCityService;
+import com.fs.store.service.IFsUserCompanyUserService;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -64,6 +66,8 @@ public class CompanyUserServiceImpl implements ICompanyUserService
 
     @Autowired
     private FsUserMapper fsUserMapper;
+    @Autowired
+    private IFsUserCompanyUserService userCompanyUserService;
 
     /**
      * 查询物业公司管理员信息
@@ -495,15 +499,19 @@ public class CompanyUserServiceImpl implements ICompanyUserService
 
     @Override
     @Transactional
-    public int changeCompanyUser(List<Long> userIds, Long companyUserId, Long companyId) {
+    public int changeCompanyUser(List<UserProjectDTO> users, Long companyUserId, Long companyId) {
 
         CompanyUser toUser = companyUserMapper.selectCompanyUserById(companyUserId);
         if (Objects.isNull(toUser)) {
             throw new ServiceException("需要更换归属的销售不存在");
         }
-       fsUserMapper.batchUpdateUserCompanyUser(userIds, companyUserId, companyId);
-        // 修改中间表
-        fsUserMapper.batchUpdateCompanyUserRelation(userIds, companyUserId, companyId);
+
+        for (UserProjectDTO u : users) {
+            userCompanyUserService.changeRelationship(u.getUserId(), u.getProjectId(), toUser.getCompanyId(), toUser.getUserId());
+        }
+//       fsUserMapper.batchUpdateUserCompanyUser(userIds, companyUserId, companyId);
+//        // 修改中间表
+//        fsUserMapper.batchUpdateCompanyUserRelation(userIds, companyUserId, companyId);
         return 1;
     }
 

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

@@ -12,4 +12,8 @@ public class CompanyTagUserVO {
      * 用户昵称
      */
     private String userName;
+    /**
+     * 项目ID
+     */
+    private Long projectId;
 }

+ 9 - 0
fs-service-system/src/main/java/com/fs/company/vo/CompanyUserChangeApplyUserVO.java

@@ -8,6 +8,15 @@ public class CompanyUserChangeApplyUserVO {
      * 用户ID
      */
     private Long userId;
+    /**
+     * 项目ID
+     */
+    private Long projectId;
+
+    /**
+     * 项目名称
+     */
+    private String projectName;
     /**
      * 用户名称
      */

+ 5 - 0
fs-service-system/src/main/java/com/fs/course/domain/FsUserCourseVideo.java

@@ -100,4 +100,9 @@ public class FsUserCourseVideo extends BaseEntity
 
     private Long userId;
 
+    /**
+     * 项目ID
+     */
+    private Long projectId;
+
 }

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

@@ -386,4 +386,26 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             ") dd ON ds.report_date = dd.log_date\n" +
             "ORDER BY ds.report_date ASC")
     List<WatchLogDTO> selectFsCourseWatchLog30DayByExtId(@Param("extId") Long extId);
+
+    @Select("select count(*) from fs_course_watch_log where user_id =#{userId} and project =#{projectId} ")
+    Long selectByWatchLjDay(@Param("userId") Long userId,@Param("projectId")  Long projectId);
+    @Select("SELECT IFNULL(MAX(streak), 0) AS current_consecutive_days\n" +
+            "FROM (\n" +
+            "    SELECT \n" +
+            "        watch_date,\n" +
+            "        @streak := IF(\n" +
+            "            DATEDIFF(@prev_date, watch_date) = 1, \n" +
+            "            @streak + 1, \n" +
+            "            1\n" +
+            "        ) AS streak,\n" +
+            "        @prev_date := watch_date\n" +
+            "    FROM (\n" +
+            "        SELECT DISTINCT DATE(create_time) AS watch_date\n" +
+            "        FROM fs_course_watch_log\n" +
+            "        WHERE user_id = #{userId} AND project = #{projectId}\n" +
+            "        ORDER BY watch_date DESC\n" +
+            "    ) AS dates,\n" +
+            "    (SELECT @streak := 0, @prev_date := NULL) AS vars\n" +
+            ") AS streak_data;")
+    Long selectByWatchlxDay(@Param("userId") Long userId,@Param("projectId")  Long projectId);
 }

+ 3 - 3
fs-service-system/src/main/java/com/fs/course/param/BatchVideoSvae.java

@@ -1,10 +1,7 @@
 package com.fs.course.param;
 
-import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
-import java.math.BigDecimal;
-import java.time.LocalTime;
 import java.util.List;
 
 @Data
@@ -12,4 +9,7 @@ public class BatchVideoSvae {
     private Long courseId;
     private Long userId;
     private List<Long> ids;
+
+    // 项目ID
+    private Long projectId;
 }

+ 4 - 1
fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseAddCompanyUserParam.java

@@ -3,7 +3,6 @@ package com.fs.course.param.newfs;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
-import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 import java.io.Serializable;
 
@@ -34,4 +33,8 @@ public class FsUserCourseAddCompanyUserParam implements Serializable {
     @ApiModelProperty(value = "营期id")
     private Long periodId;
 
+    @NotNull(message = "项目id不能为空")
+    @ApiModelProperty(value = "项目ID")
+    private Long projectId;
+
 }

+ 3 - 1
fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseBeMemberParam.java

@@ -24,5 +24,7 @@ public class FsUserCourseBeMemberParam implements Serializable {
     @ApiModelProperty(value = "标签ids,数组格式")
     private String[] tagIds;
 
-
+    @NotNull(message = "项目id不能为空")
+    @ApiModelProperty(value = "课程归属项目id")
+    private Long projectId;
 }

+ 2 - 0
fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -50,6 +50,7 @@ import com.hc.openapi.tool.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Isolation;
 import org.springframework.transaction.annotation.Propagation;
@@ -104,6 +105,7 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
     @Autowired
     private IFsUserCourseCacheService fsUserCourseCacheService;
 
+    @Lazy
     @Autowired
     private IFsUserCourseVideoCacheService fsUserCourseVideoCacheService;
 

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

@@ -429,7 +429,14 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
 
             // 观看时长
             recordVO.setWatchTime((BigDecimal) sumMap.getOrDefault("watchTime", BigDecimal.ZERO));
-
+            //累计观看天数
+            Long ljDay = fsCourseWatchLogMapper.selectByWatchLjDay(recordVO.getUserId(),recordVO.getProjectId());
+            System.out.println("进入了统计累计观看天数:"+ljDay+"天");
+            recordVO.setWatchLjCount(ljDay);
+            //连续观看天数
+            Long lxDay = fsCourseWatchLogMapper.selectByWatchlxDay(recordVO.getUserId(),recordVO.getProjectId());
+            System.out.println("进入了连续观看天数:"+lxDay+"天");
+            recordVO.setWatchLxCount(lxDay);
             // 领取状态
             Long count = fsCourseAnswerLogsMapper.selectRedStatus(recordVO.getUserId(), recordVO.getVideoId());
             if (Objects.nonNull(count) && count > 0) {

+ 53 - 29
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -3,17 +3,15 @@ package com.fs.course.service.impl;
 import cn.hutool.core.util.NumberUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fs.common.BeanCopyUtils;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
+import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.enums.BizResponseEnum;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.date.DateUtil;
-import com.fs.company.domain.Company;
-import com.fs.company.domain.CompanyMoneyLogs;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.course.config.CourseConfig;
@@ -52,8 +50,10 @@ import com.fs.store.mapper.FsUserCompanyUserMapper;
 import com.fs.store.mapper.FsUserIntegralLogsMapper;
 import com.fs.store.mapper.FsUserMapper;
 import com.fs.store.service.IFsStorePaymentService;
+import com.fs.store.service.IFsUserCompanyUserService;
 import com.fs.store.service.IFsUserService;
 import com.fs.store.service.cache.IFsUserCourseCacheService;
+import com.fs.system.mapper.SysDictDataMapper;
 import com.fs.system.service.ISysConfigService;
 import com.fs.voice.utils.StringUtil;
 import com.github.binarywang.wxpay.bean.transfer.TransferBillsResult;
@@ -63,7 +63,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.core.parameters.P;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -180,6 +179,11 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
     @Autowired
     private FsUserCourseCompanyUserTimeMapper companyUserTimeMapper;
+    @Autowired
+    private IFsUserCompanyUserService userCompanyUserService;
+    @Autowired
+    private SysDictDataMapper dictDataMapper;
+
 
     @Autowired
     private IFsUserWxService fsUserWxService;
@@ -1066,7 +1070,14 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
     @Override
     public List<FsUserCourseVideoPageListVO> pageListCourseVideo(UserCourseVideoPageParam param) {
-        return fsUserCourseVideoMapper.selectFsUserCourseVideoPageList(param);
+        List<FsUserCourseVideoPageListVO> vos = fsUserCourseVideoMapper.selectFsUserCourseVideoPageList(param);
+        // 项目
+        List<SysDictData> courseProject = dictDataMapper.selectDictDataByType("sys_course_project");
+        vos.forEach(vo -> courseProject.stream()
+                .filter(c -> c.getDictValue().equals(vo.getProjectId().toString()))
+                .findFirst()
+                .ifPresent(c -> vo.setProjectName(c.getDictLabel())));
+        return vos;
     }
 
     @Override
@@ -1156,21 +1167,26 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         //判断:1、如果没有绑定销售,就返回给客服的微信图片;
         //2、如果只绑定了当前销售,需要添加看课记录(正常流程);
         //3、以上都不是,则标识重粉,需要加入关系表,并打上重粉标签
-        if(fsUser.getCompanyUserId() == null) {
+        FsUserCompanyUser userCompanyUser = userCompanyUserService.selectByUserIdAndProjectId(fsUser.getUserId(), param.getProjectId());
+        if(Objects.isNull(userCompanyUser)) {
             return ResponseResult.fail(BizResponseEnum.DATA_NOT_EXIST, getCompanyUserQRCode(companyUser));
         }
 
         // 逻辑调整:如果会员已经绑定了销售,直接提示,不添加重粉数据了-2025年6月16日14点53分
-        if (fsUser.getCompanyUserId() != null && !param.getCompanyUserId().equals(fsUser.getCompanyUserId())){
+        if (!param.getCompanyUserId().equals(userCompanyUser.getCompanyUserId())){
             return ResponseResult.fail(500,"该用户("+fsUser.getUserId() + ")已成为其他销售会员");
         }
 
         // 如果开启了黑名单审核,需要提示
-        if(fsUser.getStatus() == 0) {
+        if(userCompanyUser.getStatus() == 0) {
 //            return ResponseResult.fail(505, "管理开启了会员审核,请等待审核");
             return ResponseResult.fail(BizResponseEnum.WAIT_APPROVAL,getCompanyUserQRCode(companyUser));
         }
 
+        if (userCompanyUser.getStatus() == 2) {
+            return ResponseResult.fail(504, "已被拉黑,请联系管理员");
+        }
+
         //查询看课记录
 //        FsCourseWatchLog watchCourseVideo = courseWatchLogMapper.getWatchCourseVideoByFsUser(param.getUserId(), param.getVideoId(), param.getCompanyUserId());
         FsCourseWatchLog watchCourseVideo = courseWatchLogMapper.getCourseWatchLogByUser(param.getUserId(), param.getVideoId());
@@ -1211,12 +1227,12 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
         // 添加会员销售关系表数据
         // 逻辑调整:如果会员已经绑定了销售,直接提示,不添加重粉数据了-2025年6月16日14点58分
-        FsUserCompanyUser fsUserCompanyUser = getFsUserCompanyUser(param, fsUser);
-        QueryWrapper<FsUserCompanyUser> queryWrapper = new QueryWrapper<FsUserCompanyUser>().eq("user_id", param.getUserId()).eq("company_user_id", param.getCompanyUserId());
-        Integer i = fsUserCompanyUserMapper.selectCount(queryWrapper);
-        if(i == 0) {
-            fsUserCompanyUserMapper.insertFsUserCompanyUser(fsUserCompanyUser);
-        }
+//        FsUserCompanyUser fsUserCompanyUser = getFsUserCompanyUser(param, fsUser);
+//        QueryWrapper<FsUserCompanyUser> queryWrapper = new QueryWrapper<FsUserCompanyUser>().eq("user_id", param.getUserId()).eq("company_user_id", param.getCompanyUserId());
+//        Integer i = fsUserCompanyUserMapper.selectCount(queryWrapper);
+//        if(i == 0) {
+//            fsUserCompanyUserMapper.insertFsUserCompanyUser(fsUserCompanyUser);
+//        }
 
 //        // 如果重粉需要打上重粉标签
 //        if(1 == fsUserCompanyUser.getIsRepeatFans()){
@@ -1229,20 +1245,20 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         return ResponseResult.ok(Boolean.TRUE);
     }
 
-    // 添加关系表数据
-    public static FsUserCompanyUser getFsUserCompanyUser(FsUserCourseAddCompanyUserParam param, FsUser fsUser) {
-        FsUserCompanyUser fsUserCompanyUser = new FsUserCompanyUser();
-        // 判断是否绑定了销售,如果已绑定,则需要标识为重粉,且放黑名单
-        if (fsUser.getCompanyUserId() != null && !fsUser.getCompanyUserId().equals(param.getCompanyUserId())) {
-            fsUserCompanyUser.setIsRepeatFans(1);
-        } else {
-            fsUserCompanyUser.setIsRepeatFans(0);
-        }
-        fsUserCompanyUser.setUserId(param.getUserId());
-        fsUserCompanyUser.setCompanyId(param.getCompanyId());
-        fsUserCompanyUser.setCompanyUserId(param.getCompanyUserId());
-        return fsUserCompanyUser;
-    }
+//    // 添加关系表数据
+//    public static FsUserCompanyUser getFsUserCompanyUser(FsUserCourseAddCompanyUserParam param, FsUser fsUser) {
+//        FsUserCompanyUser fsUserCompanyUser = new FsUserCompanyUser();
+//        // 判断是否绑定了销售,如果已绑定,则需要标识为重粉,且放黑名单
+//        if (fsUser.getCompanyUserId() != null && !fsUser.getCompanyUserId().equals(param.getCompanyUserId())) {
+//            fsUserCompanyUser.setIsRepeatFans(1);
+//        } else {
+//            fsUserCompanyUser.setIsRepeatFans(0);
+//        }
+//        fsUserCompanyUser.setUserId(param.getUserId());
+//        fsUserCompanyUser.setCompanyId(param.getCompanyId());
+//        fsUserCompanyUser.setCompanyUserId(param.getCompanyUserId());
+//        return fsUserCompanyUser;
+//    }
 
     private String getCompanyUserQRCode(CompanyUser companyUser) {
         String companyUserQRCode;
@@ -1448,6 +1464,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             entity.setFileKey(e.getFileKey());
             entity.setIsTranscode(0);
             entity.setUserId(vo.getUserId());
+            entity.setProjectId(vo.getProjectId());
             return entity;
         }).collect(Collectors.toList());
         fsUserCourseVideoMapper.insertBatchFsUserCourseVideo(collect);
@@ -1506,7 +1523,14 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
      */
     @Override
     public List<FsUserCourseVideoPageListVO> selectCourseVideoListByMap(Map<String, Object> params) {
-        return fsUserCourseVideoMapper.selectFsUserCourseVideoListByMap(params);
+        List<FsUserCourseVideoPageListVO> vos = fsUserCourseVideoMapper.selectFsUserCourseVideoListByMap(params);
+        // 项目
+        List<SysDictData> courseProject = dictDataMapper.selectDictDataByType("sys_course_project");
+        vos.forEach(vo -> courseProject.stream()
+                .filter(c -> c.getDictValue().equals(vo.getProjectId().toString()))
+                .findFirst()
+                .ifPresent(c -> vo.setProjectName(c.getDictLabel())));
+        return vos;
     }
 
     @Override

+ 15 - 0
fs-service-system/src/main/java/com/fs/course/vo/FsUserCourseParticipationRecordVO.java

@@ -18,6 +18,11 @@ public class FsUserCourseParticipationRecordVO {
      * 小节D
      */
     private Long videoId;
+
+    /**
+     * 项目编号
+     */
+    private Long projectId;
     /**
      * 用户昵称
      */
@@ -47,6 +52,16 @@ public class FsUserCourseParticipationRecordVO {
      * 完播次数
      */
     private Long finishCount;
+
+    /**
+     * 累计观看天数
+     */
+    private Long watchLjCount;
+
+    /**
+     * 连续观看天数
+     */
+    private Long watchLxCount;
     /**
      * 累计时长
      */

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

@@ -57,4 +57,9 @@ public class FsUserCourseVideoPageListVO extends BaseEntity {
     @ApiModelProperty(value = "课程结束时间")
     private Date endDateTime;
 
+    @ApiModelProperty(value = "项目ID")
+    private Long projectId;
+    @ApiModelProperty(value = "项目名称")
+    private String projectName;
+
 }

+ 38 - 42
fs-service-system/src/main/java/com/fs/qw/service/impl/CustomerTransferApprovalServiceImpl.java

@@ -1,8 +1,5 @@
 package com.fs.qw.service.impl;
 
-import java.util.*;
-
-import cn.hutool.core.lang.Pair;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
@@ -11,18 +8,18 @@ import com.fs.company.cache.ICompanyCacheService;
 import com.fs.company.cache.ICompanyUserCacheService;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.qw.domain.CustomerTransferApproval;
 import com.fs.qw.mapper.CustomerTransferApprovalMapper;
 import com.fs.qw.service.ICustomerTransferApprovalService;
 import com.fs.qw.vo.TransferCustomDTO;
 import com.fs.store.domain.FsUser;
+import com.fs.store.domain.FsUserCompanyUser;
 import com.fs.store.dto.FsUserTransferParamDTO;
-import com.fs.store.mapper.FsUserCompanyUserMapper;
-import com.fs.store.service.IFsUserService;
+import com.fs.store.service.IFsUserCompanyUserService;
 import com.fs.store.service.cache.IFsUserCacheService;
 import com.hc.openapi.tool.util.StringUtils;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.http.util.Asserts;
+import org.apache.hc.core5.util.Asserts;
 import org.springframework.aop.framework.AopContext;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
@@ -30,6 +27,11 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
 /**
  * 客户转移审批Service业务层处理
  *
@@ -53,10 +55,8 @@ public class CustomerTransferApprovalServiceImpl implements ICustomerTransferApp
     private IFsUserCacheService fsUserCacheService;
 
     @Autowired
-    private IFsUserService fsUserService;
+    private IFsUserCompanyUserService userCompanyUserService;
 
-    @Autowired
-    private FsUserCompanyUserMapper fsUserCompanyUserMapper;
     /**
      * 查询客户转移审批
      *
@@ -111,7 +111,7 @@ public class CustomerTransferApprovalServiceImpl implements ICustomerTransferApp
         }
 
         if(StringUtils.isBlank(item.getTransferBefore()) && StringUtils.isNotBlank(item.getCustomerIds())){
-            List<Long> customerIds = JSON.parseArray(item.getCustomerIds(), Long.class);
+            List<UserProjectDTO> customerIds = JSON.parseArray(item.getCustomerIds(), UserProjectDTO.class);
             List<TransferCustomDTO> customerList = getCustomerList(customerIds, item);
             item.setCustomerList(customerList);
         } else {
@@ -138,32 +138,31 @@ public class CustomerTransferApprovalServiceImpl implements ICustomerTransferApp
         }
     }
 
-    private List<TransferCustomDTO> getCustomerList(List<Long> customerIds, CustomerTransferApproval item) {
+    private List<TransferCustomDTO> getCustomerList(List<UserProjectDTO> customerIds, CustomerTransferApproval item) {
         List<TransferCustomDTO> customerList = new ArrayList<>();
 
-        for (Long customerId : customerIds) {
+        for (UserProjectDTO customerId : customerIds) {
 
-            FsUser fsUser = fsUserCacheService.selectFsUserById(customerId);
+            FsUser fsUser = fsUserCacheService.selectFsUserById(customerId.getUserId());
             if(ObjectUtils.isNotNull(fsUser)){
-                String companyUserName = "无";
-                String afterCompanyUserName = "无";
-                if(ObjectUtils.isNotNull(fsUser.getCompanyUserId())){
-                    CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(fsUser.getCompanyUserId());
-                    companyUserName = String.format("%s_%d", companyUser.getUserName(), companyUser.getUserId());
+                FsUserCompanyUser userCompanyUser = userCompanyUserService.selectByUserIdAndProjectId(customerId.getUserId(), customerId.getProjectId());
+                if(Objects.nonNull(userCompanyUser)){
+                    CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(userCompanyUser.getCompanyUserId());
+                    String companyUserName = String.format("%s_%d", companyUser.getUserName(), companyUser.getUserId());
+
+                    CompanyUser afterCompanyUser = companyUserCacheService.selectCompanyUserById(item.getTargetUserId());
+                    String afterCompanyUserName = String.format("%s_%d", afterCompanyUser.getUserName(), companyUser.getUserId());
+
+                    customerList.add(TransferCustomDTO.builder()
+                            .userName(String.format("%s_%d", fsUser.getNickname(), fsUser.getUserId()))
+                            .userId(fsUser.getUserId())
+                            .projectId(customerId.getProjectId())
+                            .beforeCompanyUserName(companyUserName)
+                            .beforeCompanyUserId(companyUser.getUserId())
+                            .afterCompanyUserName(afterCompanyUserName)
+                            .afterCompanyUserId(item.getTargetUserId())
+                            .build());
                 }
-                if(ObjectUtils.isNotNull(item.getTargetUserId())) {
-                    CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(item.getTargetUserId());
-                    afterCompanyUserName = String.format("%s_%d", companyUser.getUserName(), companyUser.getUserId());
-                }
-
-                customerList.add(TransferCustomDTO.builder()
-                        .userName(String.format("%s_%d", fsUser.getNickname(), fsUser.getUserId()))
-                        .userId(fsUser.getUserId())
-                        .beforeCompanyUserName(companyUserName)
-                        .beforeCompanyUserId(fsUser.getCompanyUserId())
-                        .afterCompanyUserName(afterCompanyUserName)
-                        .afterCompanyUserId(item.getTargetUserId())
-                        .build());
             }
         }
         return customerList;
@@ -248,6 +247,9 @@ public class CustomerTransferApprovalServiceImpl implements ICustomerTransferApp
         item.setProcessedAt(new Date());
 //        审批状态: 0=待审批, 1=审批通过, 2=审批驳回, 3=已撤销
         // 如果审批通过 进行转移
+        List<UserProjectDTO> customerIds1 = JSON.parseArray(item.getCustomerIds(), UserProjectDTO.class);
+        List<TransferCustomDTO> customerList = getCustomerList(customerIds1, item);
+        item.setTransferBefore(JSON.toJSONString(customerList));
 
         if(ObjectUtil.equal(1,item.getApprovalStatus())){
             FsUserTransferParamDTO transferParam = new FsUserTransferParamDTO();
@@ -255,22 +257,16 @@ public class CustomerTransferApprovalServiceImpl implements ICustomerTransferApp
             transferParam.setTargetCompanyUserId(item.getTargetUserId());
 
             Asserts.check(StringUtils.isNotBlank(item.getCustomerIds()),"转移客户不能为空!");
-            List<Long> customerIds = JSON.parseArray(item.getCustomerIds(), Long.class);
+            List<UserProjectDTO> customerIds = JSON.parseArray(item.getCustomerIds(), UserProjectDTO.class);
             transferParam.setUserIds(customerIds);
             transferParam.setSourceCompanyUserId(item.getOriginalUserId());
 
-            if(CollectionUtils.isNotEmpty(transferParam.getUserIds())) {
-                fsUserService.transfer(transferParam);
-            }
-
-            if(CollectionUtils.isNotEmpty(transferParam.getUserIds())) {
-                fsUserCompanyUserMapper.transfer(transferParam);
+            for (UserProjectDTO customerId : customerIds) {
+                CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(item.getTargetUserId());
+                userCompanyUserService.changeRelationship(customerId.getUserId(), customerId.getProjectId(), companyUser.getCompanyId(), companyUser.getUserId());
             }
         }
 
-        List<Long> customerIds = JSON.parseArray(item.getCustomerIds(), Long.class);
-        List<TransferCustomDTO> customerList = getCustomerList(customerIds, item);
-        item.setTransferBefore(JSON.toJSONString(customerList));
 
         return customerTransferApprovalMapper.updateCustomerTransferApproval(item);
     }

+ 4 - 0
fs-service-system/src/main/java/com/fs/qw/vo/TransferCustomDTO.java

@@ -17,6 +17,10 @@ public class TransferCustomDTO implements Serializable {
      */
     private String userName;
     private Long userId;
+    /**
+     * 项目ID
+     */
+    private Long projectId;
     /**
      * 转移前销售
      */

+ 13 - 0
fs-service-system/src/main/java/com/fs/store/domain/FsUser.java

@@ -160,6 +160,19 @@ public class FsUser extends BaseEntity
     @Excel(name = "所属公司", sort = 9)
     private String companyName;
 
+    /**
+     * 项目ID
+     */
+    private Long projectId;
+
+    public Long getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(Long projectId) {
+        this.projectId = projectId;
+    }
+
 
 
     @ApiModelProperty(value = "是否点过注册链接 0:否 1:是")

+ 34 - 5
fs-service-system/src/main/java/com/fs/store/domain/FsUserCompanyUser.java

@@ -1,11 +1,13 @@
 package com.fs.store.domain;
 
+import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.fs.common.annotation.Excel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
-import com.fs.common.core.domain.BaseEntity;
-import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
 
 /**
  * 微信用户和销售关系对象 fs_user_company_user
@@ -14,10 +16,10 @@ import lombok.EqualsAndHashCode;
  * @date 2025-05-09
  */
 @Data
-@EqualsAndHashCode(callSuper = true)
-public class FsUserCompanyUser extends BaseEntity{
+public class FsUserCompanyUser {
 
     /** id */
+    @TableId(type = IdType.AUTO)
     private Long id;
 
     /** 用户id(关联fs_user表user_id) */
@@ -40,5 +42,32 @@ public class FsUserCompanyUser extends BaseEntity{
     @ApiModelProperty(value = "重粉所属销售,多个用逗号隔开")
     private String repeatCompanyUserName;
 
-
+    /**
+     * 项目ID
+     */
+    private Long projectId;
+    /**
+     * 企微用户ID
+     */
+    private Long qwUserId;
+    /**
+     * 企微外部联系人ID
+     */
+    private Long qwExternalContactId;
+    /**
+     * 企微主体ID
+     */
+    private Long qwCompanyId;
+    /**
+     * 状态 0小黑屋 1正常 2拉黑
+     */
+    private Integer status;
+    /**
+     * 备注
+     */
+    private String remark;
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
 }

+ 14 - 0
fs-service-system/src/main/java/com/fs/store/domain/FsUserCourseCount.java

@@ -57,6 +57,11 @@ public class FsUserCourseCount extends BaseEntity
     @Excel(name = "用户状态,1-正常;2-停止;3-未看")
     private Long status;
 
+    /**
+     * 项目id
+     */
+    private Long projectId;
+
     /** 停课天数(如果状态是停止) */
     @Excel(name = "停课天数", readConverterExp = "如=果状态是停止")
     private Long stopWatchDays;
@@ -83,6 +88,15 @@ public class FsUserCourseCount extends BaseEntity
     @JsonFormat(pattern = "yyyy-MM-dd")
     private Date lastDate;
 
+    public Long getProjectId() {
+        return projectId;
+    }
+
+    public FsUserCourseCount setProjectId(Long projectId) {
+        this.projectId = projectId;
+        return this;
+    }
+
     public void setId(Long id)
     {
         this.id = id;

+ 30 - 0
fs-service-system/src/main/java/com/fs/store/domain/FsUserProjectTag.java

@@ -0,0 +1,30 @@
+package com.fs.store.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("fs_user_project_tag")
+public class FsUserProjectTag {
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    /**
+     * 用户项目ID
+     */
+    private Long userCompanyUserId;
+    /**
+     * 标签ID
+     */
+    private Long tagId;
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+}

+ 2 - 1
fs-service-system/src/main/java/com/fs/store/dto/FsUserTransferParamDTO.java

@@ -1,5 +1,6 @@
 package com.fs.store.dto;
 
+import com.fs.company.dto.UserProjectDTO;
 import lombok.Data;
 
 import java.io.Serializable;
@@ -22,7 +23,7 @@ public class FsUserTransferParamDTO implements Serializable {
     /**
      * 客户id
      */
-    private List<Long> userIds;
+    private List<UserProjectDTO> userIds;
 
     /**
      * 转移提示内容/原因

+ 16 - 2
fs-service-system/src/main/java/com/fs/store/mapper/FsUserCompanyUserMapper.java

@@ -1,12 +1,14 @@
 package com.fs.store.mapper;
 
-import java.util.List;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.store.domain.FsUserCompanyUser;
 import com.fs.store.dto.FsUserTransferParamDTO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
-import org.apache.ibatis.annotations.Update;
+
+import java.util.List;
+import java.util.Map;
 
 /**
  * 微信用户和销售关系Mapper接口
@@ -79,4 +81,16 @@ public interface FsUserCompanyUserMapper extends BaseMapper<FsUserCompanyUser>{
     List<FsUserCompanyUser> selectRepeatCompanyUserName(@Param("userIds") List<Long> userIds);
 
     void transfer(@Param("param") FsUserTransferParamDTO transferParam);
+
+    /**
+     * 批量修改用户-项目状态
+     * @param ids       参数
+     * @param status    状态
+     * @return  count
+     */
+    int changeUserProjectStatus(@Param("ids") List<UserProjectDTO> ids, @Param("status") int status);
+
+    int batchUpdateStatus(@Param("ids") List<Long> ids, @Param("status") int status);
+
+    List<Long> selectFsUserCompanyUserListByMap(@Param("param") Map<String, Object> param);
 }

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

@@ -136,7 +136,7 @@ public interface FsUserMapper
 
 
     @Select({"<script> " +
-            "select u.*,cu.nick_name as company_user_nick_name  from fs_user u left join company_user cu on u.company_user_id=cu.user_id " +
+            "select distinct u.*,cu.nick_name as company_user_nick_name  from fs_user u left join fs_user_company_user ucu on ucu.user_id = u.user_id left join company_user cu on ucu.company_user_id=cu.user_id " +
             "where u.is_del=0 " +
             "<if test = 'maps.nickname != null and  maps.nickname !=\"\"    '> " +
             "and u.nickname like CONCAT('%',#{maps.nickname},'%') " +
@@ -145,13 +145,13 @@ public interface FsUserMapper
             "and u.phone=#{maps.phone} " +
             "</if>" +
             "<if test = 'maps.companyId != null    '> " +
-            "and u.company_id=#{maps.companyId} " +
+            "and cu.company_id=#{maps.companyId} " +
             "</if>" +
             "<if test = 'maps.status != null   and  maps.status !=\"\"   '> " +
             "and u.status=#{maps.status} " +
             "</if>" +
             "<if test = 'maps.companyUserId != null    '> " +
-            "and u.company_user_id=#{maps.companyUserId} " +
+            "and cu.company_user_id=#{maps.companyUserId} " +
             "</if>" +
             " order by u.create_time desc "+
             "</script>"})
@@ -177,8 +177,6 @@ public interface FsUserMapper
     @Select("select * from fs_user where course_ma_open_id=#{openId}")
     FsUser selectFsUserByCourseMaOpenId(String openId);
 
-    Long selectCrmCustomerCount(int type, Long companyId);
-
     @Select("select * from fs_user where user_code=#{userCode}")
     FsUser selectFsUserByUserCode(String userCode);
     @Select("select * from fs_user where user_id=#{userId} for update")
@@ -215,7 +213,7 @@ public interface FsUserMapper
 
     int getRepeatUserNumber(@Param("userId") Long userId);
 
-    UserDetailsVO getCountWatchCourse (@Param("userId") Long userId, @Param("fsUserId") Long fsUserId, @Param("dateTag") String dateTag);
+    UserDetailsVO getCountWatchCourse (@Param("userId") Long userId, @Param("fsUserId") Long fsUserId, @Param("dateTag") String dateTag,@Param("userCompanyId")  Long userCompanyId);
 
     UserDetailsVO getCountAnswer (@Param("userId") Long userId, @Param("fsUserId") Long fsUserId, @Param("dateTag") String dateTag);
 
@@ -313,6 +311,8 @@ public interface FsUserMapper
 
     List<FsUser> selectFsUserListByJointUserNameKey(String userNameKey);
 
+    List<FSUserVO> selectFsUserVOListByProject(@Param("maps") FsUser fsUser);
+
     Map<String, Long> countUserCourse2(UserStatisticsCommonParam param);
 
     Map<String, Long> countCourseDetailsNew(UserStatisticsCommonParam param);

+ 19 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsUserProjectTagMapper.java

@@ -0,0 +1,19 @@
+package com.fs.store.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.store.domain.FsUserProjectTag;
+import com.fs.store.vo.h5.CompanyUserTagListVO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+public interface FsUserProjectTagMapper extends BaseMapper<FsUserProjectTag> {
+
+    /**
+     * 查询用户项目标签列表
+     * @param params     参数
+     * @return  list
+     */
+    List<CompanyUserTagListVO> getTagList(@Param("params") Map<String, Object> params);
+}

+ 1 - 1
fs-service-system/src/main/java/com/fs/store/param/FsStoreProductAddEditParam.java

@@ -142,7 +142,7 @@ public class FsStoreProductAddEditParam implements Serializable
     private List<ProductArrtDTO> items;
     //sku结果集
     private List<FsStoreProductAttrValue> values;
-    // 指定企业
+        // 指定企业
     private String companyIds;
 
 }

+ 8 - 3
fs-service-system/src/main/java/com/fs/store/param/h5/FsUserPageListParam.java

@@ -6,7 +6,6 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.io.Serializable;
-import java.util.List;
 import java.util.Set;
 
 
@@ -40,7 +39,7 @@ public class FsUserPageListParam implements Serializable {
     private String registerEndTime;
 
     @ApiModelProperty(value = "标签")
-    private String[] tagIds;
+    private Long[] tagIds;
 
     @ApiModelProperty(value = "tab序号,0全部;1今日新增;2今日完播;3未看过课")
     private String tabValue;
@@ -78,8 +77,14 @@ public class FsUserPageListParam implements Serializable {
      */
     private Boolean isNullTag =false;
 
+    /**
+     * 项目ID
+     */
+    @ApiModelProperty(value = "项目ID")
+    private Long projectId;
 
-
+    @ApiModelProperty(value = "状态 0小黑屋 1正常 2拉黑")
+    private Integer status;
 
 }
 

+ 84 - 8
fs-service-system/src/main/java/com/fs/store/service/IFsUserCompanyUserService.java

@@ -1,19 +1,22 @@
 package com.fs.store.service;
 
-import java.util.List;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.store.domain.FsUserCompanyUser;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * 微信用户和销售关系Service接口
- * 
+ *
  * @author fs
  * @date 2025-05-09
  */
 public interface IFsUserCompanyUserService extends IService<FsUserCompanyUser>{
     /**
      * 查询微信用户和销售关系
-     * 
+     *
      * @param id 微信用户和销售关系主键
      * @return 微信用户和销售关系
      */
@@ -21,15 +24,22 @@ public interface IFsUserCompanyUserService extends IService<FsUserCompanyUser>{
 
     /**
      * 查询微信用户和销售关系列表
-     * 
+     *
      * @param fsUserCompanyUser 微信用户和销售关系
      * @return 微信用户和销售关系集合
      */
     List<FsUserCompanyUser> selectFsUserCompanyUserList(FsUserCompanyUser fsUserCompanyUser);
 
+    /**
+     * 根据销售id查询出项目id
+     * @param param
+     * @return
+     */
+    List<Long> selectFsUserCompanyUserList(Map<String,Object> param );
+
     /**
      * 新增微信用户和销售关系
-     * 
+     *
      * @param fsUserCompanyUser 微信用户和销售关系
      * @return 结果
      */
@@ -37,7 +47,7 @@ public interface IFsUserCompanyUserService extends IService<FsUserCompanyUser>{
 
     /**
      * 修改微信用户和销售关系
-     * 
+     *
      * @param fsUserCompanyUser 微信用户和销售关系
      * @return 结果
      */
@@ -45,7 +55,7 @@ public interface IFsUserCompanyUserService extends IService<FsUserCompanyUser>{
 
     /**
      * 批量删除微信用户和销售关系
-     * 
+     *
      * @param ids 需要删除的微信用户和销售关系主键集合
      * @return 结果
      */
@@ -53,9 +63,75 @@ public interface IFsUserCompanyUserService extends IService<FsUserCompanyUser>{
 
     /**
      * 删除微信用户和销售关系信息
-     * 
+     *
      * @param id 微信用户和销售关系主键
      * @return 结果
      */
     int deleteFsUserCompanyUserById(Long id);
+
+    /**
+     * 根据用户ID和项目ID查询微信用户与销售的关系
+     * @param userId            用户ID
+     * @param projectId   项目ID
+     * @return FsUserCompanyUser
+     */
+    FsUserCompanyUser selectByUserIdAndProjectId(Long userId, Long projectId);
+
+    /**
+     * 查询是否已绑定关系
+     * @param userId            用户ID
+     * @param companyUserId     销售ID
+     * @return  boolean
+     */
+    boolean hasBind(Long userId, Long companyUserId);
+
+    /**
+     * 获取当前销售的所有重粉会员
+     * @param userId 销售ID
+     * @return  list
+     */
+    List<FsUserCompanyUser> selectRepeatUser(Long userId);
+
+    /**
+     * 获取会员的重粉的所属销售
+     * @param userIds   会员ID集合
+     * @return  list
+     */
+    List<FsUserCompanyUser> selectRepeatCompanyUserName(List<Long> userIds);
+
+    /**
+     * 绑定会员-项目-销售关系
+     *
+     * @param userId        会员ID
+     * @param projectId     项目ID
+     * @param companyId     公司ID
+     * @param companyUserId 销售ID
+     * @param status        状态 1正常 0小黑屋
+     */
+    FsUserCompanyUser bindRelationship(Long userId, Long projectId, Long companyId, Long companyUserId, int status);
+
+    /**
+     * 修改会员-项目-销售关系
+     * @param userId        会员ID
+     * @param projectId     项目ID
+     * @param companyId     公司ID
+     * @param companyUserId 销售ID
+     */
+    void changeRelationship(Long userId, Long projectId, Long companyId, Long companyUserId);
+
+    /**
+     * 修改会员-项目关系 状态
+     * @param ids       参数
+     * @param status    状态 0小黑屋 1正常 2拉黑
+     * @return Boolean
+     */
+    Boolean batchUpdateUserProjectStatus(List<UserProjectDTO> ids, int status);
+
+    /**
+     * 修改会员-项目关系 状态
+     * @param ids       参数
+     * @param status    状态 0小黑屋 1正常 2拉黑
+     * @return Boolean
+     */
+    Boolean batchUpdateStatus(List<Long> ids, int status);
 }

+ 33 - 0
fs-service-system/src/main/java/com/fs/store/service/IFsUserProjectTagService.java

@@ -0,0 +1,33 @@
+package com.fs.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.store.domain.FsUserProjectTag;
+import com.fs.store.param.h5.TagListParam;
+import com.fs.store.vo.h5.CompanyUserTagListVO;
+
+import java.util.List;
+
+public interface IFsUserProjectTagService extends IService<FsUserProjectTag> {
+
+    /**
+     * 添加用户项目标签
+     * @param id        用户项目ID
+     * @param tagIds    标签ID集合
+     */
+    void addUserProjectTag(Long id, List<Long> tagIds);
+
+    /**
+     * 添加用户项目标签
+     * @param ids       用户项目ID集合
+     * @param tagIds    标签ID集合
+     */
+    void addUserProjectTag(List<Long> ids, List<Long> tagIds);
+
+    /**
+     * 查询用户项目标签列表
+     * @param param     参数
+     * @return  list
+     */
+    List<CompanyUserTagListVO> getTagList(TagListParam param);
+
+}

+ 8 - 1
fs-service-system/src/main/java/com/fs/store/service/IFsUserService.java

@@ -147,7 +147,7 @@ public interface IFsUserService
 
     UserListPageVO getUserNumber(Long userId);
 
-    UserDetailsVO getUserDetails(Long userId, Long fsUserId, String dateTag);
+    UserDetailsVO getUserDetails(Long userId, Long fsUserId, String dateTag, Long userCompanyId);
 
     /**
      * 查询重粉用户是否存在
@@ -263,4 +263,11 @@ public interface IFsUserService
      */
     List<FsUser> selectFsUserListByJointUserNameKey(String userNameKey);
 
+    /**
+     * 查询项目会员数据
+     *
+     * @param fsUser
+     * @return
+     */
+    List<FSUserVO> selectFsUserVOListByProject(FsUser fsUser);
 }

+ 154 - 11
fs-service-system/src/main/java/com/fs/store/service/impl/FsUserCompanyUserServiceImpl.java

@@ -1,16 +1,24 @@
 package com.fs.store.service.impl;
 
-import java.util.List;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import com.fs.store.mapper.FsUserCompanyUserMapper;
+import com.fs.common.exception.CustomException;
+import com.fs.company.dto.UserProjectDTO;
 import com.fs.store.domain.FsUserCompanyUser;
+import com.fs.store.mapper.FsUserCompanyUserMapper;
 import com.fs.store.service.IFsUserCompanyUserService;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * 微信用户和销售关系Service业务层处理
- * 
+ *
  * @author fs
  * @date 2025-05-09
  */
@@ -19,7 +27,7 @@ public class FsUserCompanyUserServiceImpl extends ServiceImpl<FsUserCompanyUserM
 
     /**
      * 查询微信用户和销售关系
-     * 
+     *
      * @param id 微信用户和销售关系主键
      * @return 微信用户和销售关系
      */
@@ -31,7 +39,7 @@ public class FsUserCompanyUserServiceImpl extends ServiceImpl<FsUserCompanyUserM
 
     /**
      * 查询微信用户和销售关系列表
-     * 
+     *
      * @param fsUserCompanyUser 微信用户和销售关系
      * @return 微信用户和销售关系
      */
@@ -41,9 +49,20 @@ public class FsUserCompanyUserServiceImpl extends ServiceImpl<FsUserCompanyUserM
         return baseMapper.selectFsUserCompanyUserList(fsUserCompanyUser);
     }
 
+    /**
+     * 根据销售id查询出项目id
+     *
+     * @param companyUserId 销售id
+     * @return 微信用户和销售关系
+     */
+    @Override
+    public List<Long> selectFsUserCompanyUserList(Map<String,Object> param) {
+
+        return baseMapper.selectFsUserCompanyUserListByMap(param);
+    }
     /**
      * 新增微信用户和销售关系
-     * 
+     *
      * @param fsUserCompanyUser 微信用户和销售关系
      * @return 结果
      */
@@ -55,7 +74,7 @@ public class FsUserCompanyUserServiceImpl extends ServiceImpl<FsUserCompanyUserM
 
     /**
      * 修改微信用户和销售关系
-     * 
+     *
      * @param fsUserCompanyUser 微信用户和销售关系
      * @return 结果
      */
@@ -67,7 +86,7 @@ public class FsUserCompanyUserServiceImpl extends ServiceImpl<FsUserCompanyUserM
 
     /**
      * 批量删除微信用户和销售关系
-     * 
+     *
      * @param ids 需要删除的微信用户和销售关系主键
      * @return 结果
      */
@@ -79,7 +98,7 @@ public class FsUserCompanyUserServiceImpl extends ServiceImpl<FsUserCompanyUserM
 
     /**
      * 删除微信用户和销售关系信息
-     * 
+     *
      * @param id 微信用户和销售关系主键
      * @return 结果
      */
@@ -88,4 +107,128 @@ public class FsUserCompanyUserServiceImpl extends ServiceImpl<FsUserCompanyUserM
     {
         return baseMapper.deleteFsUserCompanyUserById(id);
     }
+
+    /**
+     * 根据用户ID和项目ID查询微信用户与销售的关系
+     * @param userId            用户ID
+     * @param projectId   项目ID
+     * @return FsUserCompanyUser
+     */
+    @Override
+    public FsUserCompanyUser selectByUserIdAndProjectId(Long userId, Long projectId) {
+        LambdaQueryWrapper<FsUserCompanyUser> queryWrapper = Wrappers.<FsUserCompanyUser>lambdaQuery()
+                .eq(FsUserCompanyUser::getUserId, userId)
+                .eq(FsUserCompanyUser::getProjectId, projectId);
+        return getOne(queryWrapper);
+    }
+
+    /**
+     * 查询是否已绑定关系
+     * @param userId            用户ID
+     * @param companyUserId     销售ID
+     * @return  boolean
+     */
+    @Override
+    public boolean hasBind(Long userId, Long companyUserId) {
+        LambdaQueryWrapper<FsUserCompanyUser> queryWrapper = Wrappers.<FsUserCompanyUser>lambdaQuery()
+                .eq(FsUserCompanyUser::getUserId, userId)
+                .eq(FsUserCompanyUser::getCompanyUserId, companyUserId);
+        return count(queryWrapper) > 0;
+    }
+
+    /**
+     * 获取当前销售的所有重粉会员
+     * @param userId 销售ID
+     * @return list
+     */
+    @Override
+    public List<FsUserCompanyUser> selectRepeatUser(Long userId) {
+        return baseMapper.selectRepeatUser(userId);
+    }
+
+    /**
+     * 获取会员的重粉的所属销售
+     * @param userIds   会员ID集合
+     * @return  list
+     */
+    @Override
+    public List<FsUserCompanyUser> selectRepeatCompanyUserName(List<Long> userIds) {
+        return baseMapper.selectRepeatCompanyUserName(userIds);
+    }
+
+    /**
+     * 绑定会员-项目-销售关系
+     *
+     * @param userId        会员ID
+     * @param projectId     项目ID
+     * @param companyId     公司ID
+     * @param companyUserId 销售ID
+     * @param status        状态 0小黑屋 1正常
+     */
+    @Override
+    public FsUserCompanyUser bindRelationship(Long userId, Long projectId, Long companyId, Long companyUserId, int status) {
+        FsUserCompanyUser userCompanyUser = new FsUserCompanyUser();
+        userCompanyUser.setUserId(userId);
+        userCompanyUser.setProjectId(projectId);
+        userCompanyUser.setCompanyId(companyId);
+        userCompanyUser.setCompanyUserId(companyUserId);
+//        boolean hasBind = hasBind(userId, companyUserId);
+//        userCompanyUser.setIsRepeatFans(hasBind ? 1 : 0);
+        userCompanyUser.setIsRepeatFans(0);
+        userCompanyUser.setStatus(status);
+        userCompanyUser.setCreateTime(LocalDateTime.now());
+        save(userCompanyUser);
+        return userCompanyUser;
+    }
+
+    /**
+     * 修改会员-项目-销售关系
+     * @param userId        会员ID
+     * @param projectId     项目ID
+     * @param companyId     公司ID
+     * @param companyUserId 销售ID
+     */
+    @Override
+    public void changeRelationship(Long userId, Long projectId, Long companyId, Long companyUserId) {
+        LambdaQueryWrapper<FsUserCompanyUser> queryWrapper = Wrappers.<FsUserCompanyUser>lambdaQuery()
+                .eq(FsUserCompanyUser::getUserId, userId)
+                .eq(FsUserCompanyUser::getProjectId, projectId)
+                .last("limit 1");
+        FsUserCompanyUser userCompanyUser = getOne(queryWrapper);
+        if (Objects.nonNull(userCompanyUser)) {
+            userCompanyUser.setCompanyId(companyId);
+            userCompanyUser.setCompanyUserId(companyUserId);
+            updateById(userCompanyUser);
+        }
+    }
+
+    /**
+     * 修改会员-项目关系 状态
+     * @param ids       参数
+     * @param status    状态 0小黑屋 1正常 2拉黑
+     * @return Boolean
+     */
+    @Override
+    public Boolean batchUpdateUserProjectStatus(List<UserProjectDTO> ids, int status) {
+        if (Objects.isNull(ids) || ids.isEmpty()) {
+            throw new CustomException("请先选择会员");
+        }
+        return baseMapper.changeUserProjectStatus(ids, status) > 0;
+    }
+
+
+
+    /**
+     * 修改会员-项目关系 状态
+     * @param ids       参数
+     * @param status    状态 0小黑屋 1正常 2拉黑
+     * @return Boolean
+     */
+    @Override
+    public Boolean batchUpdateStatus(List<Long> ids, int status) {
+        if (Objects.isNull(ids) || ids.isEmpty()) {
+            throw new CustomException("请先选择会员");
+        }
+        return baseMapper.batchUpdateStatus(ids, status) > 0;
+    }
 }

+ 2 - 2
fs-service-system/src/main/java/com/fs/store/service/impl/FsUserCourseCountServiceImpl.java

@@ -121,10 +121,10 @@ public class FsUserCourseCountServiceImpl implements IFsUserCourseCountService
         // 查询用户-每天的最新的看课状态,和最后的心跳时间
         List<FsUserCourseCount> userStatusAndLastWatchDate = fsUserCourseCountMapper.getUserStatusAndLastWatchDate();
         Map<String, FsUserCourseCount> map = userStatusAndLastWatchDate.stream()
-                .collect(Collectors.toMap(k -> String.format("%s-%s", k.getUserId(), k.getLastDate()), v -> v));
+                .collect(Collectors.toMap(k -> String.format("%s-%s-%s", k.getUserId(),k.getProjectId(), k.getLastDate()), v -> v));
 
         for (FsUserCourseCount data : countResult) {
-            String key = String.format("%s-%s",data.getUserId(), data.getLastDate());
+            String key = String.format("%s-%s-%s", data.getUserId(),data.getProjectId(), data.getLastDate());
             FsUserCourseCount fsUserCourseCount = map.get(key);
             if(fsUserCourseCount != null){
                 data.setLastWatchDate(fsUserCourseCount.getLastWatchDate());

+ 105 - 0
fs-service-system/src/main/java/com/fs/store/service/impl/FsUserProjectTagServiceImpl.java

@@ -0,0 +1,105 @@
+package com.fs.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.store.domain.FsUserProjectTag;
+import com.fs.store.mapper.FsUserProjectTagMapper;
+import com.fs.store.param.h5.TagListParam;
+import com.fs.store.service.IFsUserProjectTagService;
+import com.fs.store.vo.h5.CompanyUserTagListVO;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+public class FsUserProjectTagServiceImpl extends ServiceImpl<FsUserProjectTagMapper, FsUserProjectTag> implements IFsUserProjectTagService {
+
+    @Resource
+    private CompanyUserMapper companyUserMapper;
+
+    /**
+     * 添加用户项目标签
+     * @param id        用户项目ID
+     * @param tagIds    标签ID集合
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void addUserProjectTag(Long id, List<Long> tagIds) {
+        // 删除原有标签
+        LambdaUpdateWrapper<FsUserProjectTag> deleteWrapper = Wrappers.<FsUserProjectTag>lambdaUpdate()
+                .eq(FsUserProjectTag::getUserCompanyUserId, id);
+        baseMapper.delete(deleteWrapper);
+
+        // 添加新标签
+        List<FsUserProjectTag> tagList = tagIds.stream().distinct().map(tagId -> {
+            FsUserProjectTag userProjectTag = new FsUserProjectTag();
+            userProjectTag.setUserCompanyUserId(id);
+            userProjectTag.setTagId(tagId);
+            userProjectTag.setCreateTime(LocalDateTime.now());
+            return userProjectTag;
+        }).collect(Collectors.toList());
+
+        if (!tagList.isEmpty()) {
+            saveBatch(tagList);
+        }
+    }
+
+    /**
+     * 添加用户项目标签
+     * @param ids       用户项目ID集合
+     * @param tagIds    标签ID集合
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void addUserProjectTag(List<Long> ids, List<Long> tagIds) {
+        // 删除原有标签
+        LambdaUpdateWrapper<FsUserProjectTag> deleteWrapper = Wrappers.<FsUserProjectTag>lambdaUpdate()
+                .in(FsUserProjectTag::getUserCompanyUserId, ids);
+        baseMapper.delete(deleteWrapper);
+
+        if (tagIds.isEmpty()) {
+            return;
+        }
+
+        // 添加新标签
+        List<FsUserProjectTag> tagList = ids.stream().map(id ->
+                tagIds.stream().distinct().map(tagId -> {
+            FsUserProjectTag userProjectTag = new FsUserProjectTag();
+            userProjectTag.setUserCompanyUserId(id);
+            userProjectTag.setTagId(tagId);
+            userProjectTag.setCreateTime(LocalDateTime.now());
+            return userProjectTag;
+        }).collect(Collectors.toList())).flatMap(List::stream).collect(Collectors.toList());
+
+        if (!tagList.isEmpty()) {
+            saveBatch(tagList);
+        }
+    }
+
+    /**
+     * 查询用户项目标签列表
+     * @param param     参数
+     * @return  list
+     */
+    @Override
+    public List<CompanyUserTagListVO> getTagList(TagListParam param) {
+        Map<String, Object> params = new HashMap<>();
+        params.put("keywords", Optional.ofNullable(param.getKeyword()).map(keyword -> keyword.split(",")).orElse(null));
+
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getUserId());
+        if (Objects.nonNull(companyUser) && companyUser.isAdmin()) {
+            params.put("companyId", companyUser.getCompanyId());
+        } else {
+            params.put("companyUserId", param.getUserId());
+        }
+
+        return baseMapper.getTagList(params);
+    }
+}

+ 47 - 103
fs-service-system/src/main/java/com/fs/store/service/impl/FsUserServiceImpl.java

@@ -2,11 +2,11 @@ package com.fs.store.service.impl;
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.common.constant.HttpStatus;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
+import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.DictUtils;
@@ -31,21 +31,23 @@ import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
 import com.fs.course.vo.newfs.FsCourseAnalysisVO;
 import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
 import com.fs.his.vo.OptionsVO;
-import com.fs.qw.cache.IQwExternalContactCacheService;
-import com.fs.qw.mapper.QwExternalContactMapper;
-import com.fs.qw.mapper.QwSessionMapper;
 import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.vo.QwFsUserVO;
 import com.fs.store.domain.*;
 import com.fs.store.dto.FsStoreCartDTO;
 import com.fs.store.dto.FsUserTransferParamDTO;
 import com.fs.store.enums.BillDetailEnum;
-import com.fs.store.mapper.*;
+import com.fs.store.mapper.FsStoreOrderMapper;
+import com.fs.store.mapper.FsStoreProductAttrValueMapper;
+import com.fs.store.mapper.FsUserCourseCountMapper;
+import com.fs.store.mapper.FsUserMapper;
 import com.fs.store.param.SelectCusListPageParam;
 import com.fs.store.param.h5.CourseAnalysisParam;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.param.h5.UserStatisticsCommonParam;
 import com.fs.store.service.IFsUserBillService;
+import com.fs.store.service.IFsUserCompanyUserService;
+import com.fs.store.service.IFsUserProjectTagService;
 import com.fs.store.service.IFsUserService;
 import com.fs.store.service.cache.IFsUserCourseCountCacheService;
 import com.fs.store.vo.FSUserVO;
@@ -53,6 +55,7 @@ import com.fs.store.vo.FsCompanyUserListQueryVO;
 import com.fs.store.vo.FsUserLastCount;
 import com.fs.store.vo.FsUserTuiVO;
 import com.fs.store.vo.h5.*;
+import com.fs.system.mapper.SysDictDataMapper;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import lombok.extern.slf4j.Slf4j;
@@ -118,24 +121,23 @@ public class FsUserServiceImpl implements IFsUserService
     private ICompanyTagCacheService companyTagCacheService;
 
     @Autowired
-    private IQwExternalContactCacheService qwExternalContactCacheService;
-
-    @Autowired
-    private FsUserCompanyUserMapper fsUserCompanyUserMapper;
+    private IFsUserCompanyUserService userCompanyUserService;
 
     @Autowired
     private ICompanyTagService companyTagService;
 
     @Autowired
     CompanyTagMapper companyTagMapper;
+
     @Autowired
-    private QwSessionMapper qwSessionMapper;
-    @Autowired
-    private QwExternalContactMapper qwExternalContactMapper;
+    private SysDictDataMapper dictDataMapper;
 
     @Autowired
     private FsUserCourseCountMapper fsUserCourseCountMapper;
 
+    @Autowired
+    private IFsUserProjectTagService userProjectTagService;
+
     /**
      * 查询用户
      *
@@ -257,12 +259,6 @@ public class FsUserServiceImpl implements IFsUserService
                     fsUser.setStatusText("正常");
                 }
             }
-            if(ObjectUtils.isNotNull(fsUser.getCompanyUserId())){
-                CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(fsUser.getCompanyUserId());
-                if(ObjectUtils.isNotNull(companyUser)){
-                    fsUser.setCompanyUserName(String.format("%s_%d",companyUser.getUserName(),companyUser.getUserId()));
-                }
-            }
         }
         TableDataInfo tableDataInfo = new TableDataInfo();
         tableDataInfo.setRows(fsUsers);
@@ -481,7 +477,6 @@ public class FsUserServiceImpl implements IFsUserService
 
     @Override
     public PageInfo<FsUserPageListVO> selectFsUserPageList(FsUserPageListParam param) {
-//        System.out.println("参数:" + Arrays.toString(param.getTagIds()));
         CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getUserId());
         if (companyUser!=null && companyUser.isAdmin()){
             param.setUserId(0L);
@@ -503,7 +498,14 @@ public class FsUserServiceImpl implements IFsUserService
         }
         List<FsUserPageListVO> fsUserPageListVOS = fsUserMapper.selectFsUserPageList(param);
 
+
+        List<SysDictData> courseProject = dictDataMapper.selectDictDataByType("sys_course_project");
         for (FsUserPageListVO fsUserPageListVO : fsUserPageListVOS) {
+            // 项目
+            courseProject.stream()
+                    .filter(c -> c.getDictValue().equals(fsUserPageListVO.getProjectId().toString()))
+                    .findFirst()
+                    .ifPresent(c -> fsUserPageListVO.setProjectName(c.getDictLabel()));
             if (StringUtils.isEmpty(fsUserPageListVO.getNickname())){
                 fsUserPageListVO.setNickname("用户暂未授权昵称");
             }
@@ -511,29 +513,6 @@ public class FsUserServiceImpl implements IFsUserService
 
         return new PageInfo<>(fsUserPageListVOS);
 
-        // 获取当前销售所有重粉会员
-//        List<FsUserCompanyUser> fsUserCompanyUsers = fsUserCompanyUserMapper.selectRepeatUser(param.getUserId());
-
-//        if(!fsUserCompanyUsers.isEmpty()){
-//            List<Long> userIds = fsUserCompanyUsers.stream().map(FsUserCompanyUser::getUserId).collect(Collectors.toList());
-//            // 获取会员所属的重粉销售
-//            List<FsUserCompanyUser> repeatCompanyUserNames = fsUserCompanyUserMapper.selectRepeatCompanyUserName(userIds);
-//            Map<Long, String> map = repeatCompanyUserNames.stream().collect(Collectors.toMap(FsUserCompanyUser::getUserId, FsUserCompanyUser::getRepeatCompanyUserName));
-//
-//            for (FsUserPageListVO fsUserPageListVO : fsUserPageListVOS) {
-//                fsUserPageListVO.setRepeatCompanyUserName(map.get(fsUserPageListVO.getUserId()));
-//                if ("微信用户".equals(fsUserPageListVO.getNickname()) && StringUtils.isNotEmpty(fsUserPageListVO.getPhone())) {
-//                    fsUserPageListVO.setNickname(fsUserPageListVO.getPhone());
-//                }
-//                if(StringUtils.isNotEmpty(fsUserPageListVO.getTag()) && fsUserPageListVO.getIsRepeatFans() == 0){
-//                    StringBuilder newTag = removeRepeatFansTag(fsUserPageListVO);
-//                    fsUserPageListVO.setTag(newTag.toString());
-//                }
-//            }
-//            return new PageInfo<>(fsUserPageListVOS);
-//        } else {
-//            return new PageInfo<>(fsUserPageListVOS);
-//        }
     }
 
     private static StringBuilder removeRepeatFansTag(FsUserPageListVO fsUserPageListVO) {
@@ -659,24 +638,28 @@ public class FsUserServiceImpl implements IFsUserService
         }
         List<UserListCountVO> list = fsUserMapper.getUserNumber(userId, companyId);
         Map<String, Integer> map = list.stream()
-                .collect(Collectors.toMap(UserListCountVO::getStatus, UserListCountVO::getNum, (v1, v2) -> v1));
+                .collect(Collectors.toMap(UserListCountVO::getStatus, UserListCountVO::getNum, (v1, v2) -> v1 + v2));
         UserListPageVO pageVO = new UserListPageVO();
         Integer normalNum = map.getOrDefault("1", 0);
-        Integer blackNum = map.getOrDefault("0", 0);
+        Integer smallBlackNum = map.getOrDefault("0", 0);
+        Integer blackNum = map.getOrDefault("2", 0);
 
         // 黑名单人数加上重粉的数量,正常人数去掉重粉数量
         int repeatUserNumber = fsUserMapper.getRepeatUserNumber(userId);
         pageVO.setNumber(normalNum - repeatUserNumber);
         pageVO.setBlackNum(blackNum + repeatUserNumber);
+        pageVO.setSmallBlackNum(smallBlackNum + repeatUserNumber);
 
         return pageVO;
     }
 
     @Override
-    public UserDetailsVO getUserDetails(Long userId, Long fsUserId, String dateTag) {
-        UserDetailsVO countWatchCourse = fsUserMapper.getCountWatchCourse(userId, fsUserId, dateTag);
-        UserDetailsVO countAnswer = fsUserMapper.getCountAnswer(userId, fsUserId, dateTag);
-        UserDetailsVO countRedPacket = fsUserMapper.getCountRedPacket(userId, fsUserId, dateTag);
+    public UserDetailsVO getUserDetails(Long userId, Long fsUserId, String dateTag, Long userCompanyId) {
+        UserDetailsVO countWatchCourse = fsUserMapper.getCountWatchCourse(userId, fsUserId, dateTag,userCompanyId);
+        FsUserCompanyUser fsUserCompanyUser = userCompanyUserService.selectFsUserCompanyUserById(userCompanyId);
+
+        UserDetailsVO countAnswer = fsUserMapper.getCountAnswer(fsUserCompanyUser.getCompanyUserId(), fsUserId, dateTag);
+        UserDetailsVO countRedPacket = fsUserMapper.getCountRedPacket(fsUserCompanyUser.getCompanyUserId(), fsUserId, dateTag);
         UserDetailsVO vo = new UserDetailsVO();
         if (countWatchCourse != null){
             BeanUtils.copyProperties(countWatchCourse, vo);
@@ -1012,56 +995,27 @@ public class FsUserServiceImpl implements IFsUserService
             return ResponseResult.fail(407,"不允许注册会员,请联系管理员");
         }
 
-        //2025.7.10 解决在不知销售已经审核的情况下 多次点击
-        if (fsUser.getIsBecomeMember() == 1){
-            if(fsUser.getStatus() == 0){
-                //禁用
-                return ResponseResult.fail(402, "已成功注册,待管理审核");
-            } else {
-                return ResponseResult.ok(Boolean.TRUE); //已经成为会员
-            }
-        }
-        fsUser.setIsBecomeMember(1);
-
-        // 添加关系表数据
-        FsUserCompanyUser fsUserCompanyUser = getFsUserCompanyUser(param, fsUser);
-        QueryWrapper<FsUserCompanyUser> queryWrapper = new QueryWrapper<FsUserCompanyUser>().eq("user_id", param.getUserId()).eq("company_user_id", param.getCompanyUserId());
-        Integer i = fsUserCompanyUserMapper.selectCount(queryWrapper);
-        if(i == 0) {
-            fsUserCompanyUserMapper.insertFsUserCompanyUser(fsUserCompanyUser);
+        // 逻辑调整:如果会员已经绑定了销售,直接提示,不添加重粉数据了-2025年6月16日14点53分
+        // 逻辑调整:会员与销售的绑定关系通过中间表关联 /20250625 17:13
+        FsUserCompanyUser userCompanyUser = userCompanyUserService.selectByUserIdAndProjectId(fsUser.getUserId(), param.getProjectId());
+        if (Objects.nonNull(userCompanyUser) && !userCompanyUser.getCompanyUserId().equals(param.getCompanyUserId())){
+            return ResponseResult.fail(406,"该用户已成为其他销售会员");
         }
 
-        // 关联销售
-        if(fsUser.getCompanyUserId() == null) {
-            fsUser.setCompanyId(param.getCompanyId());
-            fsUser.setCompanyUserId(param.getCompanyUserId());
-        }
         // 特殊(需求设计:需要根据公司是否开启黑名单来设置会员初始化的状态)
-        Company company = null;
-        if(param.getCompanyId() != null) {
-            company = companyMapper.selectCompanyById(param.getCompanyId());
-        }
+        Company company = companyMapper.selectCompanyById(param.getCompanyId());
         // isDefaultBlack 值为1 ,表示需要加入小黑屋,否则不加
         int isDefaultBlack = company != null ? company.getFsUserIsDefaultBlack() : 0;
-        fsUser.setStatus(isDefaultBlack == 1 ? 0 : 1);
-        fsUserMapper.updateFsUser(fsUser);
+
+        // 添加关系表数据
+        if (Objects.isNull(userCompanyUser)){
+            int defaultStatus = isDefaultBlack == 1 ? 0 : 1;
+            userCompanyUser = userCompanyUserService.bindRelationship(param.getUserId(), param.getProjectId(), companyUser.getCompanyId(), companyUser.getUserId(), defaultStatus);
+        }
 
         // 不为空则添加新标签
         if (Objects.nonNull(param.getTagIds()) && param.getTagIds().length > 0) {
-            //关联会员标签,先删除再新增
-            Map<String, Object> map = new HashMap<>();
-            map.put("userId", param.getUserId());
-            map.put("companyId", fsUser.getCompanyId());
-            map.put("companyUserId", fsUser.getCompanyUserId());
-            companyTagUserMapper.deleteCompanyTagUserByMap(map);
-
-            CompanyTagUser companyTagUser = new CompanyTagUser();
-            companyTagUser.setUserId(param.getUserId());
-            companyTagUser.setCompanyId(fsUser.getCompanyId());
-            companyTagUser.setCompanyUserId(fsUser.getCompanyUserId());
-            companyTagUser.setTagIds(String.join(",", param.getTagIds()));
-            companyTagUser.setCreateTime(new Date());
-            companyTagUserMapper.insertCompanyTagUser(companyTagUser);
+            userProjectTagService.addUserProjectTag(userCompanyUser.getId(), Arrays.stream(param.getTagIds()).map(Long::valueOf).collect(Collectors.toList()));
         }
 
 //        // 如果是重粉,直接打上重粉的标签
@@ -1114,19 +1068,9 @@ public class FsUserServiceImpl implements IFsUserService
         return fsUserMapper.selectFsUserListByJointUserNameKey(userNameKey);
     }
 
-    // 添加关系表数据
-    private static FsUserCompanyUser getFsUserCompanyUser(FsUserCourseBeMemberParam param, FsUser fsUser) {
-        FsUserCompanyUser fsUserCompanyUser = new FsUserCompanyUser();
-        // 判断是否绑定了销售,如果已绑定,则需要标识为重粉,且放黑名单
-        if (fsUser.getCompanyUserId() != null && !fsUser.getCompanyUserId().equals(param.getCompanyUserId())) {
-            fsUserCompanyUser.setIsRepeatFans(1);
-        } else {
-            fsUserCompanyUser.setIsRepeatFans(0);
-        }
-        fsUserCompanyUser.setUserId(param.getUserId());
-        fsUserCompanyUser.setCompanyId(param.getCompanyId());
-        fsUserCompanyUser.setCompanyUserId(param.getCompanyUserId());
-        return fsUserCompanyUser;
+    @Override
+    public List<FSUserVO> selectFsUserVOListByProject(FsUser fsUser) {
+        return fsUserMapper.selectFsUserVOListByProject(fsUser);
     }
 
     /**

+ 7 - 0
fs-service-system/src/main/java/com/fs/store/vo/h5/FsUserPageListVO.java

@@ -93,4 +93,11 @@ public class FsUserPageListVO {
     @ApiModelProperty(value = "重粉所属销售,多个用逗号隔开")
     private String repeatCompanyUserName;
 
+    @ApiModelProperty(value = "项目ID")
+    private Long projectId;
+    @ApiModelProperty(value = "项目名称")
+    private String projectName;
+    @ApiModelProperty(value = "用户-项目-销售ID")
+    private Long userCompanyUserId;
+
 }

+ 3 - 0
fs-service-system/src/main/java/com/fs/store/vo/h5/UserListPageVO.java

@@ -17,4 +17,7 @@ public class UserListPageVO {
     @ApiModelProperty(value = "黑名单数量")
     private int blackNum;
 
+    @ApiModelProperty(value = "小黑屋数量")
+    private int smallBlackNum;
+
 }

+ 6 - 6
fs-service-system/src/main/resources/application-config-dev.yml

@@ -64,11 +64,11 @@ wx:
         aesKey: PKvaxtpSv8NGpfTDm7VUHIK8Wok2ESyYX24qpXJAdMP
   miniapp:
     configs:
-      - appid: wx11a2ce7c2bbc4521   #倍力优会员商城
-        secret: d680dc8ff20258b158c9355f8b7769ae
-        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
-        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
-        msgDataFormat: JSON
+#      - appid: wx11a2ce7c2bbc4521   #倍力优会员商城
+#        secret: d680dc8ff20258b158c9355f8b7769ae
+#        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
+#        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+#        msgDataFormat: JSON
 
 ##  云联融智优选小程序,暂时使用
       - appid: wxd70f99287830cb51   #云联融智优选(暂时用于测试销售app)
@@ -100,7 +100,7 @@ wx:
       timeout: 2000
     configs:
       - appId: wx93ce67750e3cfba3 # 第一个公众号的appid  //公众号名称:云联
-        secret: 659fafaace3ca1c4df1bffac173a9bd7
+        secret: c670cfb4254688ab8637a44c1e7095f0
         token: PPKOdAlCoMO # 接口配置里的Token值
         aesKey: Eswa6VjwtVMCcw03qZy6fWllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
 jpush:

+ 36 - 0
fs-service-system/src/main/resources/db/upgrade/20250625会员关联项目.sql

@@ -0,0 +1,36 @@
+-- 微信用户和销售关系表添加项目ID
+alter table fs_user_company_user
+    add column project_id bigint comment '课程项目ID';
+
+alter table fs_user_company_user add constraint uk_user_project
+    unique (user_id, project_id);
+
+alter table fs_user_company_user
+    add column qw_user_id bigint comment '企微用户ID',
+    add column qw_external_contact_id bigint comment '企微外部联系人ID',
+    add column qw_company_id bigint comment '企微主体ID';
+
+alter table fs_user_course_video
+    add column project_id bigint comment '项目ID';
+
+alter table fs_user_company_user
+    add column status tinyint default 1 comment '状态 0小黑屋 1正常 2拉黑';
+
+alter table fs_user_company_user
+    add column remark varchar(255) comment '备注';
+
+drop table if exists fs_user_project_tag;
+create table fs_user_project_tag (
+    `id`                             bigint        not null auto_increment comment '主键ID',
+    `user_company_user_id`           bigint                                comment '用户项目ID',
+    `tag_id`                         bigint                                comment '标签ID',
+    `create_time`                    datetime                              comment '创建时间',
+    primary key (`id`) using btree,
+    index idx_user_company_user_id(`user_company_user_id`) using btree,
+    index idx_tag_id(`tag_id`) using btree
+) engine = InnoDB comment = '用户项目标签表';
+
+alter table fs_user_company_user
+    add column create_time datetime comment '创建时间';
+
+ALTER TABLE fs_user_company_user MODIFY COLUMN create_time datetime DEFAULT now() NULL COMMENT '创建时间';

+ 32 - 0
fs-service-system/src/main/resources/mapper/company/CompanyTagMapper.xml

@@ -99,4 +99,36 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
     </delete>
 
+
+    <!-- 查询标签列表 -->
+    <select id="selectCompanyTagByList" resultType="com.fs.company.domain.CompanyTag">
+        select ct.*,fu.nickname from company_tag ct
+       inner join fs_user_project_tag ft on ct.tag_id =ft.tag_id
+       inner join fs_user_company_user fucu on fucu.id = ft.user_company_user_id
+       inner join fs_user fu on fu.user_id =fucu.user_id
+        <where>
+            <if test="params.keyword != null and params.keyword.length > 0">
+                and
+                <foreach collection="params.keyword" item="item"  open="(" separator="or" close=")">
+                    ct.tag like concat('%',#{item},'%')
+                </foreach>
+            </if>
+            <if test="params.companyId != null">
+                and ct.company_id = #{params.companyId}
+            </if>
+
+            <if test="params.companyUserId != null">
+                and fucu.company_user_id = #{params.companyUserId}
+            </if>
+            <if test="params.tagName != null">
+                and ct.tag = #{params.tagName}
+            </if>
+            <if test="params.tagName != null">
+                and ct.tag = #{params.tagName}
+            </if>
+        </where>
+        group by ct.tag_id
+    </select>
+
+
 </mapper>

+ 44 - 6
fs-service-system/src/main/resources/mapper/company/CompanyTagUserMapper.xml

@@ -105,13 +105,51 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectUserListByMap" resultType="com.fs.company.vo.CompanyTagUserVO">
         select
             fu.user_id,
+            ucu.project_id,
             fu.nickname as userName
-        from company_tag_user ctu
-        inner join fs_user fu on fu.user_id = ctu.user_id
-        where ctu.company_id = #{params.companyId} and
-        <foreach collection="params.tagIds" item="tagId" open="(" separator="or" close=")">
-            find_in_set(#{tagId}, ctu.tag_ids)
-        </foreach>
+        from fs_user fu
+        left join company_tag_user ctu on fu.user_id = ctu.user_id and ctu.company_user_id = #{params.companyUserId}
+        left join fs_user_company_user ucu on ucu.user_id = fu.user_id
+        where ucu.company_user_id = #{params.companyUserId}
+        <if test="params.tagIds != null and params.tagIds.size() > 0">
+            AND (
+            <foreach collection="params.tagIds" item="tagId" separator="OR">
+                FIND_IN_SET(#{tagId}, ctu.tag_ids)
+            </foreach>
+            )
+        </if>
+
+        <if test="params.projectIds != null and params.projectIds.size() > 0">
+            AND (
+            <foreach collection="params.projectIds" item="projectId" separator="OR">
+                FIND_IN_SET(#{projectId}, ucu.project_id)
+            </foreach>
+            )
+        </if>
+    </select>
+
+    <select id="selectUserByMap" resultType="com.fs.company.vo.CompanyTagUserVO">
+        select fu.user_id,
+        fucu.project_id,
+        fu.nickname as userName from company_tag ct
+        inner join fs_user_project_tag ft on ct.tag_id =ft.tag_id
+        inner join fs_user_company_user fucu on fucu.id = ft.user_company_user_id
+        inner join fs_user fu on fu.user_id =fucu.user_id
+        where fucu.company_user_id = #{params.companyUserId}
+        <if test="params.tagIds != null and params.tagIds.size() > 0">
+            and ft.tag_id in
+            <foreach collection="params.tagIds" item="item" separator="," open="(" close=")">
+                #{item}
+            </foreach>
+        </if>
+
+        <if test="params.projectIds != null and params.projectIds.size() > 0">
+            and fucu.project_id in
+            <foreach collection="params.projectIds" item="item" separator="," open="(" close=")">
+                #{item}
+            </foreach>
+        </if>
+        group by fucu.user_id
     </select>
 
 </mapper>

+ 5 - 4
fs-service-system/src/main/resources/mapper/company/CompanyUserChangeApplyUserMapper.xml

@@ -9,7 +9,8 @@
         select
             fu.user_id,
             fu.nickname as userName,
-            fu.avatar
+            fu.avatar,
+            cucau.project_id
         from company_user_change_apply_user cucau
         inner join fs_user fu on fu.user_id = cucau.user_id
         where cucau.apply_id = #{applyId}
@@ -17,9 +18,9 @@
 
     <!-- 修改申请记录关联用户销售 -->
     <update id="changeUser">
-        update fs_user fu
-        inner join company_user_change_apply_user cucau on cucau.user_id = fu.user_id
-        left join company_user_change_apply cuca on cuca.id = cucau.apply_id
+        update fs_user_company_user fu
+        inner join company_user_change_apply_user cucau on cucau.user_id = fu.user_id  and cucau.project_id = fu.project_id
+        inner join company_user_change_apply cuca on cuca.id = cucau.apply_id
         set fu.company_user_id = cuca.`to`
         where cuca.id = #{applyId}
     </update>

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

@@ -24,6 +24,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="sendFinishMsg"    column="send_finish_msg"    />
         <result property="campPeriodTime"    column="camp_period_time"    />
         <result property="lastHeartbeatTime"    column="last_heartbeat_time"    />
+        <result property="project"    column="project"    />
     </resultMap>
 
     <sql id="selectFsCourseWatchLogVo">
@@ -44,6 +45,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="courseId != null "> and course_id = #{courseId}</if>
             <if test="sendType != null "> and send_type = #{sendType}</if>
             <if test="campPeriodTime != null "> and camp_period_time = #{campPeriodTime}</if>
+            <if test="project != null "> and project = #{project}</if>
         </where>
     </select>
 
@@ -285,6 +287,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="sendFinishMsg != null">send_finish_msg,</if>
             <if test="campPeriodTime != null">camp_period_time,</if>
             <if test="periodId != null">period_id,</if>
+            <if test="project != null">project,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="userId != null">#{userId},</if>
@@ -305,6 +308,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="sendFinishMsg != null">#{sendFinishMsg},</if>
             <if test="campPeriodTime != null">#{campPeriodTime},</if>
             <if test="periodId != null">#{periodId},</if>
+            <if test="project != null">#{project},</if>
         </trim>
     </insert>
 
@@ -328,6 +332,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="finishTime != null">finish_time,</if>
             <if test="sendFinishMsg != null">send_finish_msg,</if>
             <if test="campPeriodTime != null">camp_period_time,</if>
+            <if test="project != null">project,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="userId != null">#{userId},</if>
@@ -347,6 +352,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="finishTime != null">#{finishTime},</if>
             <if test="sendFinishMsg != null">#{sendFinishMsg},</if>
             <if test="campPeriodTime != null">#{campPeriodTime},</if>
+            <if test="project != null">#{project},</if>
         </trim>
         on duplicate key update
         <trim suffixOverrides=",">
@@ -371,7 +377,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         send_type,
         reward_type,
         sop_id,
-        camp_period_time
+        camp_period_time,
+        project
         )
         VALUES
         <foreach collection="watchLogs" item="log" separator=",">
@@ -390,7 +397,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{log.sendType},
             #{log.rewardType},
             #{log.sopId},
-            #{log.campPeriodTime}
+            #{log.campPeriodTime},
+            #{log.project}
             )
         </foreach>
         ON DUPLICATE KEY UPDATE
@@ -420,6 +428,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="sendFinishMsg != null">send_finish_msg = #{sendFinishMsg},</if>
             <if test="lastHeartbeatTime != null">last_heartbeat_time = #{lastHeartbeatTime},</if>
             <if test="periodId != null">period_id = #{periodId},</if>
+            <if test="project != null">project = #{project},</if>
         </trim>
         where log_id = #{logId}
     </update>

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

@@ -257,6 +257,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         select
             fu.user_id,
             fucv.video_id,
+        fucv.project_id,
             fu.nickname as nickName,
             fu.username as userName,
             fu.avatar,

+ 13 - 4
fs-service-system/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml

@@ -35,6 +35,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="viewStartTime"    column="view_start_time"    />
         <result property="viewEndTime"    column="view_end_time"    />
         <result property="lastJoinTime"    column="last_join_time"    />
+        <result property="projectId"    column="project_id"    />
     </resultMap>
 
     <sql id="selectFsUserCourseVideoVo">
@@ -56,6 +57,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="courseSort != null "> and course_sort = #{courseSort}</if>
             <if test="questionBankId != null "> and question_bank_id = #{questionBankId}</if>
             <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="projectId != null "> and project_id = #{projectId}</if>
         </where>
     </select>
 
@@ -97,6 +99,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="viewEndTime != null">view_end_time,</if>
             <if test="lastJoinTime != null">last_join_time,</if>
             <if test="userId != null">user_id,</if>
+            <if test="projectId != null">project_id,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="fileId != null">#{fileId},</if>
@@ -129,6 +132,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="viewEndTime != null">#{viewEndTime},</if>
             <if test="lastJoinTime != null">#{lastJoinTime},</if>
             <if test="userId != null">#{userId},</if>
+            <if test="projectId != null">#{projectId},</if>
          </trim>
     </insert>
     <insert id="insertBatchFsUserCourseVideo" parameterType="FsUserCourseVideo" useGeneratedKeys="true" keyProperty="videoId">
@@ -150,7 +154,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         file_size,
         file_key,
         is_transcode,
-        user_id
+        user_id,
+        project_id
         )
         values
         <foreach collection="collect" item="item" separator=",">
@@ -171,7 +176,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{item.fileSize},
             #{item.fileKey},
             #{item.isTranscode},
-            #{item.userId}
+            #{item.userId},
+            #{item.projectId}
             )
         </foreach>
     </insert>
@@ -208,6 +214,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="viewStartTime != null">view_start_time = #{viewStartTime},</if>
             <if test="viewEndTime != null">view_end_time = #{viewEndTime},</if>
             <if test="lastJoinTime != null">last_join_time = #{lastJoinTime},</if>
+            <if test="projectId != null">project_id = #{projectId},</if>
         </trim>
         where video_id = #{videoId}
     </update>
@@ -244,7 +251,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             fcpd.period_id,
             fcpd.id,
             if(ccut.start_date_time is null, fcpd.start_date_time, ccut.start_date_time) as startDateTime,
-            if(ccut.end_date_time is null, fcpd.end_date_time, ccut.end_date_time) as endDateTime
+            if(ccut.end_date_time is null, fcpd.end_date_time, ccut.end_date_time) as endDateTime,
+            video.project_id
         FROM `fs_user_course_video` video
         left join fs_user_course_period_days fcpd on fcpd.video_id = video.video_id
         left join fs_user_course_period fcp on fcp.period_id = fcpd.period_id
@@ -295,7 +303,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             fcpd.period_id,
             fcpd.id,
             if(ccut.start_date_time is null, fcpd.start_date_time, ccut.start_date_time) as startDateTime,
-            if(ccut.end_date_time is null, fcpd.end_date_time, ccut.end_date_time) as endDateTime
+            if(ccut.end_date_time is null, fcpd.end_date_time, ccut.end_date_time) as endDateTime,
+            course.project as projectId
         from `fs_user_course_video` video
         left join fs_user_course_period_days fcpd on fcpd.video_id = video.video_id
         left join fs_user_course_period fcp on fcp.period_id = fcpd.period_id

+ 0 - 1
fs-service-system/src/main/resources/mapper/store/FsStoreProductMapper.xml

@@ -114,7 +114,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test = 'companyIds != null and companyIds != "" '>
                 and find_in_set(#{companyIds}, company_ids)
             </if>
-
         </where>
     </select>
 

+ 75 - 1
fs-service-system/src/main/resources/mapper/store/FsUserCompanyUserMapper.xml

@@ -10,10 +10,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="companyUserId"    column="company_user_id"    />
         <result property="companyId"    column="company_id"    />
         <result property="isRepeatFans"    column="is_repeat_fans"    />
+        <result property="projectId"    column="project_id"    />
+        <result property="qwUserId"    column="qw_user_id"    />
+        <result property="qwExternalContactId"    column="qw_external_contact_id"    />
+        <result property="qwCompanyId"    column="qw_company_id"    />
+        <result property="status"    column="status"    />
+        <result property="remark"    column="remark"    />
+        <result property="createTime"    column="create_time"    />
     </resultMap>
 
     <sql id="selectFsUserCompanyUserVo">
-        select id, user_id, company_user_id, company_id, is_repeat_fans from fs_user_company_user
+        select id, user_id, company_user_id, company_id, is_repeat_fans, project_id, qw_user_id, qw_external_contact_id, qw_company_id, `status`, remark, create_time from fs_user_company_user
     </sql>
 
     <select id="selectFsUserCompanyUserList" parameterType="FsUserCompanyUser" resultMap="FsUserCompanyUserResult">
@@ -23,6 +30,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
             <if test="companyId != null "> and company_id = #{companyId}</if>
             <if test="isRepeatFans != null "> and is_repeat_fans = #{isRepeatFans}</if>
+            <if test="projectId != null "> and project_id = #{projectId}</if>
+            <if test="qwUserId != null "> and qw_user_id = #{qwUserId}</if>
+            <if test="qwExternalContactId != null "> and qw_external_contact_id = #{qwExternalContactId}</if>
+            <if test="qwCompanyId != null "> and qw_company_id = #{qwCompanyId}</if>
+            <if test="status != null "> and `status` = #{status}</if>
+            <if test="remark != null "> and remark = #{remark}</if>
+            <if test="createTime != null "> and create_time = #{createTime}</if>
         </where>
     </select>
 
@@ -39,6 +53,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyUserId != null">company_user_id,</if>
             <if test="companyId != null">company_id,</if>
             <if test="isRepeatFans != null">is_repeat_fans,</if>
+            <if test="projectId != null">project_id,</if>
+            <if test="qwUserId != null">qw_user_id,</if>
+            <if test="qwExternalContactId != null">qw_external_contact_id,</if>
+            <if test="qwCompanyId != null">qw_company_id,</if>
+            <if test="status != null">`status`,</if>
+            <if test="remark != null">remark,</if>
+            <if test="createTime != null">create_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="id != null">#{id},</if>
@@ -46,6 +67,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyUserId != null">#{companyUserId},</if>
             <if test="companyId != null">#{companyId},</if>
             <if test="isRepeatFans != null">#{isRepeatFans},</if>
+            <if test="projectId != null">#{projectId},</if>
+            <if test="qwUserId != null">#{qwUserId},</if>
+            <if test="qwExternalContactId != null">#{qwExternalContactId},</if>
+            <if test="qwCompanyId != null">#{qwCompanyId},</if>
+            <if test="status != null">#{status},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="createTime != null">#{createTime},</if>
          </trim>
     </insert>
 
@@ -56,6 +84,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
             <if test="companyId != null">company_id = #{companyId},</if>
             <if test="isRepeatFans != null">is_repeat_fans = #{isRepeatFans},</if>
+            <if test="projectId != null">project_id = #{projectId},</if>
+            <if test="qwUserId != null">qw_user_id = #{qwUserId},</if>
+            <if test="qwExternalContactId != null">qw_external_contaact_id = #{qwExternalContactId},</if>
+            <if test="qwCompanyId != null">qw_company_id = #{qwCompanyId},</if>
+            <if test="status != null">`status` = #{status},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
         </trim>
         where id = #{id}
     </update>
@@ -71,6 +106,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </where>
     </update>
 
+    <update id="changeUserProjectStatus">
+        update fs_user_company_user ucu
+        set ucu.`status` = #{status}
+        where (ucu.user_id, ucu.project_id) in
+        <foreach collection="ids" item="item" separator="," open="(" close=")">
+            (#{item.userId}, #{item.projectId})
+        </foreach>
+    </update>
+
+    <update id="batchUpdateStatus">
+        update fs_user_company_user ucu
+        set ucu.`status` = #{status}
+        where id in
+        <foreach collection="ids" item="item" separator="," open="(" close=")">
+            (#{item})
+        </foreach>
+    </update>
+
+
     <delete id="deleteFsUserCompanyUserById" parameterType="Long">
         delete from fs_user_company_user where id = #{id}
     </delete>
@@ -98,4 +152,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         GROUP BY
             fs_user_company_user.user_id
     </select>
+
+    <select id="selectFsUserCompanyUserListByMap" resultType="java.lang.Long">
+        select fucu.project_id from company_tag ct
+        inner join fs_user_project_tag ft on ct.tag_id =ft.tag_id
+        inner join fs_user_company_user fucu on fucu.id = ft.user_company_user_id
+        <where>
+            <if test="param.tagIds != null and param.tagIds.size() > 0">
+                and ct.tag_id in
+                <foreach collection="param.tagIds" item="item" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="param.companyUserId != null">
+               and fucu.company_user_id = #{param.companyUserId}
+            </if>
+        </where>
+        group by fucu.project_id
+    </select>
+
+
 </mapper>

+ 12 - 4
fs-service-system/src/main/resources/mapper/store/FsUserCourseCountMapper.xml

@@ -24,6 +24,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="completeWatchCount"    column="complete_watch_count"    />
         <result property="watchTimes"    column="watch_times"    />
         <result property="createDate"    column="create_date"    />
+        <result property="projectId"    column="project_id"    />
     </resultMap>
 
     <sql id="selectFsUserCourseCountVo">
@@ -150,6 +151,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="getCountResult" resultType="FsUserCourseCount">
         SELECT
             fwl.user_id,
+            ucu.project_id,
             count( DISTINCT CASE WHEN fwl.log_type != 3 THEN fwl.video_id END ) AS watchCourseCount,
             count( DISTINCT CASE WHEN fwl.log_type = 3 THEN fwl.video_id END ) AS missCourseCount,
             IF
@@ -165,14 +167,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             DATE (fwl.create_time ) AS lastDate
         FROM
             fs_course_watch_log fwl
-            where fwl.send_type = 1 and fwl.create_time &gt;= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
+            left join fs_user_company_user ucu on ucu.user_id = fwl.user_id
+            where fwl.send_type = 1 and fwl.create_time &gt;= DATE_SUB(CURDATE(), INTERVAL 7 DAY) and fwl.project = ucu.project_id
         GROUP BY
-            fwl.user_id, date(fwl.create_time)
+            fwl.user_id, date(fwl.create_time),ucu.project_id
     </select>
 
     <select id="getUserStatusAndLastWatchDate" resultType="FsUserCourseCount">
         SELECT
             fs_course_watch_log.user_id,
+            ucu.project_id,
             Max( fs_course_watch_log.last_heartbeat_time ) AS lastWatchDate,
             Max( DATE ( fs_course_watch_log.create_time ) ) AS lastDate,
             CASE
@@ -190,11 +194,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 --                     DATE_FORMAT( fs_course_watch_log.last_heartbeat_time, '%Y-%m-%d' )) AS stop_watch_days
         FROM
             fs_course_watch_log
+                left join fs_user_company_user ucu on ucu.user_id = fs_course_watch_log.user_id
         WHERE
             fs_course_watch_log.send_type = 1
-          AND fs_course_watch_log.create_time >= DATE_SUB( CURDATE(), INTERVAL 7 DAY )
+          AND fs_course_watch_log.create_time >= DATE_SUB( CURDATE(), INTERVAL 7 DAY ) and fs_course_watch_log.project = ucu.project_id
         GROUP BY
-            fs_course_watch_log.user_id, date(fs_course_watch_log.create_time)
+            fs_course_watch_log.user_id, date(fs_course_watch_log.create_time),ucu.project_id
     </select>
 
 
@@ -217,6 +222,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="completeWatchCount != null">complete_watch_count,</if>
             <if test="watchTimes != null">watch_times,</if>
             <if test="createDate != null">create_date,</if>
+            <if test="projectId != null">project_id,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="userId != null">#{userId},</if>
@@ -235,9 +241,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="completeWatchCount != null">#{completeWatchCount},</if>
             <if test="watchTimes != null">#{watchTimes},</if>
             <if test="createDate != null">#{createDate},</if>
+            <if test="projectId != null">#{projectId},</if>
         </trim>
         on duplicate key update
         <trim suffixOverrides=",">
+
             <if test="watchCourseCount != null">watch_course_count = #{watchCourseCount},</if>
             <if test="missCourseCount != null">miss_course_count = #{missCourseCount},</if>
             <if test="missCourseStatus != null">miss_course_status = #{missCourseStatus},</if>

File diff suppressed because it is too large
+ 279 - 169
fs-service-system/src/main/resources/mapper/store/FsUserMapper.xml


+ 29 - 0
fs-service-system/src/main/resources/mapper/store/FsUserProjectTagMapper.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.store.mapper.FsUserProjectTagMapper">
+
+    <select id="getTagList" resultType="com.fs.store.vo.h5.CompanyUserTagListVO">
+        select distinct
+            tag.tag_id,
+            tag.tag tagName
+        from company_tag tag
+        inner join fs_user_project_tag upt on tag.tag_id = upt.tag_id
+        inner join fs_user_company_user ucu on ucu.id = upt.user_company_user_id
+        <where>
+            <if test="params.companyId != null">
+                and ucu.company_id = #{params.companyId}
+            </if>
+            <if test="params.companyUserId != null">
+                and ucu.company_user_id = #{params.companyUserId}
+            </if>
+            <if test="params.keywords != null and params.keywords.length > 0 ">
+                and
+                <foreach item="item" collection="params.keywords" open="(" separator="or" close=")">
+                    tag.tag like concat('%',#{item},'%')
+                </foreach>
+            </if>
+        </where>
+    </select>
+</mapper>

+ 15 - 1
fs-user-app/src/main/java/com/fs/app/controller/UserController.java

@@ -4,12 +4,15 @@ package com.fs.app.controller;
 import cn.hutool.core.img.ImgUtil;
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.extra.qrcode.QrCodeUtil;
+import com.alibaba.fastjson.JSON;
 import com.fs.app.annotation.Login;
 import com.fs.common.config.FSConfig;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.exception.CustomException;
 import com.fs.common.param.BaseQueryParam;
 import com.fs.common.utils.OrderUtils;
+import com.fs.course.param.newfs.FsUserCourseBeMemberParam;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.service.IFsUserCourseService;
 import com.fs.course.service.IFsUserCourseVideoService;
@@ -27,6 +30,7 @@ import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
@@ -48,9 +52,9 @@ import java.util.List;
 @Api("个人中心")
 @RestController
 @RequestMapping(value="/app/user")
+@Slf4j
 public class UserController extends  AppBaseController {
 
-    private static final Logger log = LoggerFactory.getLogger(UserController.class);
     @Autowired
     private FSConfig fsConfig;
     @Autowired
@@ -68,6 +72,9 @@ public class UserController extends  AppBaseController {
     private IFsUserPromoterApplyService userPromoterApplyService;
     @Autowired
     private IFsUserCourseVideoService courseVideoService;
+    @Autowired
+    private IFsUserService fsUserService;
+
     /**
      * 获取用户信息
      * @param request
@@ -357,4 +364,11 @@ public class UserController extends  AppBaseController {
             return R.error("您已是推广员,不能重复绑定");
         }
     }
+
+    @ApiOperation("会员关联绑定销售")
+    @PostMapping("/beMember")
+    public ResponseResult<Boolean> becomeMember(@Valid @RequestBody FsUserCourseBeMemberParam param) {
+        log.debug("会员关联绑定销售 param:{}", JSON.toJSONString(param));
+        return fsUserService.becomeMember(param);
+    }
 }

+ 11 - 72
fs-user-app/src/main/java/com/fs/app/controller/WxCompanyUserController.java

@@ -4,28 +4,21 @@ import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
 import cn.hutool.core.date.DateTime;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.app.param.LoginMaWxParam;
 import com.fs.app.utils.JwtUtils;
 import com.fs.common.core.domain.R;
-import com.fs.common.core.redis.RedisCache;
-
 import com.fs.common.utils.IpUtil;
-import com.fs.common.utils.ServletUtils;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
-
 import com.fs.his.domain.FsUserWx;
 import com.fs.his.service.IFsUserWxService;
 import com.fs.store.domain.FsUser;
+import com.fs.store.domain.FsUserCompanyUser;
+import com.fs.store.service.IFsUserCompanyUserService;
 import com.fs.store.service.IFsUserService;
-import com.fs.system.mapper.SysConfigMapper;
 import com.fs.wx.miniapp.config.WxMaConfiguration;
-import com.fs.wx.miniapp.config.WxMaProperties;
-import io.jsonwebtoken.Claims;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
@@ -34,12 +27,14 @@ import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.Date;
+import java.util.Objects;
 
 import static com.fs.common.utils.PhoneUtil.encryptPhone;
 
@@ -51,34 +46,26 @@ import static com.fs.common.utils.PhoneUtil.encryptPhone;
 public class WxCompanyUserController extends AppBaseController {
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
-    @Autowired
-    private WxMaProperties maProperties;
-
     @Autowired
     JwtUtils jwtUtils;
 
-    @Autowired
-    RedisCache redisCache;
-
     @Autowired
     private ICompanyUserService companyUserService;
 
-
     @Autowired
     private IFsUserService userService;
 
     @Autowired
     ICompanyService companyService;
-
     @Autowired
-    private SysConfigMapper sysConfigMapper;
+    private IFsUserCompanyUserService userCompanyUserService;
 
     @Autowired
     private IFsUserWxService fsUserWxService;
 
     @ApiOperation("小程序-授权登录")
     @PostMapping("/loginByMa")
-    public R login(@RequestBody LoginMaWxParam param) {
+    public R login(@Validated @RequestBody LoginMaWxParam param) {
         log.info("=====================进入小程序授权登录, 入参: {}", param);
         if (StringUtils.isBlank(param.getCode())) {
             return R.error("code不存在");
@@ -123,8 +110,8 @@ public class WxCompanyUserController extends AppBaseController {
 
             FsUser user = getUserByAuthType(param, wxService, session, phoneNoInfo);
 
-
-            if (user!=null && user.getCompanyUserId() != null && !param.getCompanyUserId().equals(user.getCompanyUserId())) {
+            FsUserCompanyUser userCompanyUser = userCompanyUserService.selectByUserIdAndProjectId(user.getUserId(), param.getProjectId());
+            if (Objects.nonNull(userCompanyUser) && !param.getCompanyUserId().equals(userCompanyUser.getCompanyUserId())){
                 return R.error(500, "该用户("+user.getUserId() + ")已成为其他销售会员");
             }
 
@@ -186,8 +173,8 @@ public class WxCompanyUserController extends AppBaseController {
         }
         if((companyUser.getIsAllowedAllRegister() == null || companyUser.getIsAllowedAllRegister() == 1)
                 && companyUser.getIsNeedRegisterMember() != null && companyUser.getIsNeedRegisterMember() != 1){
-            user.setCompanyId(param.getCompanyId());
-            user.setCompanyUserId(param.getCompanyUserId());
+            int defaultStatus = (company != null ? company.getFsUserIsDefaultBlack() : 0) == 1 ? 0 : 1;
+            userCompanyUserService.bindRelationship(user.getUserId(), param.getProjectId(), companyUser.getCompanyId(), companyUser.getUserId(), defaultStatus);
         }
         userService.insertFsUser(user);
         return user;
@@ -204,6 +191,7 @@ public class WxCompanyUserController extends AppBaseController {
         if (param.getAuthType() == 1 && phoneNoInfo != null) {
             userMap.setPhone(phoneNoInfo.getPhoneNumber());
         }
+
         userService.updateFsUser(userMap);
         return userMap;
     }
@@ -214,26 +202,6 @@ public class WxCompanyUserController extends AppBaseController {
      */
     private void handleFsUserWx(FsUser user, LoginMaWxParam param, Company company, WxMaJscode2SessionResult session) {
         if (user == null) return;
-//        FsUserWx fsUserWx = fsUserWxService.selectByAppIdAndUserId(company.getCourseMiniAppId(), user.getUserId(), 1);
-//        if (fsUserWx == null) {
-//            fsUserWx = new FsUserWx();
-//            fsUserWx.setType(1);
-//            fsUserWx.setFsUserId(user.getUserId());
-//            fsUserWx.setCompanyId(param.getCompanyId());
-//            fsUserWx.setAppId(company.getCourseMiniAppId());
-//            fsUserWx.setOpenId(session.getOpenid());
-//            fsUserWx.setUnionId(session.getUnionid() == null ? "" : session.getUnionid());
-//            fsUserWx.setCreateTime(new Date());
-//            fsUserWxService.save(fsUserWx);
-//        } else {
-//            fsUserWx.setFsUserId(user.getUserId());
-//            fsUserWx.setCompanyId(param.getCompanyId());
-//            fsUserWx.setAppId(company.getCourseMiniAppId());
-//            fsUserWx.setOpenId(session.getOpenid());
-//            fsUserWx.setUnionId(session.getUnionid() == null ? "" : session.getUnionid());
-//            fsUserWx.setUpdateTime(new Date());
-//            fsUserWxService.updateById(fsUserWx);
-//        }
         // 尝试更新
         boolean updated = fsUserWxService.lambdaUpdate()
                 .eq(FsUserWx::getFsUserId, user.getUserId())
@@ -259,33 +227,4 @@ public class WxCompanyUserController extends AppBaseController {
         }
     }
 
-
-//    @Login(isMiniLogin = true)
-//    @ApiOperation("获取销售通过小程序登录后的用户信息")
-//    @GetMapping("/getMaUser")
-//    public R getUserInfo() {
-//        try {
-//            CompanyUser companyUser = companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
-//            if (companyUser == null) {
-//                return R.error(401, "用户信息不存在");
-//            }
-//            return R.ok().put("user", companyUser);
-//        } catch (Exception e) {
-//            return R.error("操作异常");
-//        }
-//    }
-
-    /**
-     * 特殊要求:销售小程序临时登录,登录后页面中还有一个之前常用的登录,所以为了区分,token名称不能跟之前的一样
-     *
-     * @return 用户id
-     */
-    public String getUserId() {
-        String headValue = ServletUtils.getRequest().getHeader("UserToken");
-        Claims claims = jwtUtils.getClaimByToken(headValue);
-        String userId = claims.getSubject().toString();
-        return userId;
-    }
-
-
 }

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

@@ -9,26 +9,26 @@ import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
-import com.fs.course.mapper.FsCourseSopLogsMapper;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.store.domain.FsUser;
+import com.fs.store.service.IFsUserCompanyUserService;
 import com.fs.store.service.IFsUserService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import lombok.Synchronized;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
-import me.chanjar.weixin.common.bean.menu.WxMenu;
 import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
 import me.chanjar.weixin.common.error.WxErrorException;
-import me.chanjar.weixin.mp.api.WxMpMenuService;
 import me.chanjar.weixin.mp.api.WxMpService;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
 import javax.validation.Valid;
 import java.util.Date;
@@ -61,6 +61,8 @@ public class WxH5MpController {
     ICompanyService companyService;
     @Autowired
     ICompanyUserService companyUserService;
+    @Autowired
+    private IFsUserCompanyUserService userCompanyUserService;
 
 
     @ApiOperation("课程分享链接公众号登录")
@@ -99,15 +101,14 @@ public class WxH5MpController {
                 user = new FsUser();
                 user.setNickname(wxMpUser.getNickname());
                 user.setAvatar(wxMpUser.getHeadImgUrl());
-                user.setStatus((company != null ? company.getFsUserIsDefaultBlack() : 0) == 1 ? 0 : 1);
                 user.setMpOpenId(wxMpUser.getOpenid());
                 user.setUnionId(wxMpUser.getUnionId());
                 user.setCreateTime(new Date());
+                userService.insertFsUser(user);
                 if((companyUser.getIsAllowedAllRegister() == null || companyUser.getIsAllowedAllRegister() != 0) && companyUser.getIsNeedRegisterMember() != 1){
-                    user.setCompanyId(param.getCompanyId());
-                    user.setCompanyUserId(param.getCompanyUserId());
+                    int defaultStatus = (company != null ? company.getFsUserIsDefaultBlack() : 0) == 1 ? 0 : 1;
+                    userCompanyUserService.bindRelationship(user.getUserId(), param.getProjectId(), companyUser.getCompanyId(), companyUser.getUserId(), defaultStatus);
                 }
-                userService.insertFsUser(user);
             }
             log.error("用户信息user: {}, 用户id: {}", user, user.getUserId());
             String token = jwtUtils.generateToken(user.getUserId());
@@ -126,5 +127,4 @@ public class WxH5MpController {
 
     }
 
-
 }

+ 3 - 0
fs-user-app/src/main/java/com/fs/app/param/FsUserLoginByMpParam.java

@@ -20,4 +20,7 @@ public class FsUserLoginByMpParam implements Serializable {
     @NotNull(message = "销售id不能为空")
     @ApiModelProperty(value = "销售id")
     private Long companyUserId;
+    @NotNull(message = "项目ID不能为空")
+    @ApiModelProperty(value = "项目ID")
+    private Long projectId;
 }

+ 4 - 0
fs-user-app/src/main/java/com/fs/app/param/LoginMaWxParam.java

@@ -36,6 +36,10 @@ public class LoginMaWxParam implements Serializable {
     @ApiModelProperty(value = "用户头像")
     private String avatar;
 
+    @NotNull(message = "项目id不能为空")
+    @ApiModelProperty(value = "课程归属项目id")
+    private Long projectId;
+
 //    @ApiModelProperty(value = "公司id,如果不是第一位销售,都需要传")
 //    private Long companyId;
 

Some files were not shown because too many files changed in this diff