Pārlūkot izejas kodu

feat:移动会员看课功能(未完成)

caoliqin 4 dienas atpakaļ
vecāks
revīzija
34b4db5b86
65 mainītis faili ar 3950 papildinājumiem un 234 dzēšanām
  1. 18 0
      fs-company-app/src/main/java/com/fs/app/config/ImageStorageConfig.java
  2. 26 1
      fs-company-app/src/main/java/com/fs/app/controller/AppBaseController.java
  3. 89 0
      fs-company-app/src/main/java/com/fs/app/controller/CompanyTagController.java
  4. 311 0
      fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  5. 341 0
      fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
  6. 0 195
      fs-company-app/src/main/java/com/fs/app/controller/UserController.java
  7. 44 0
      fs-company-app/src/main/java/com/fs/app/controller/WxMpController.java
  8. 26 0
      fs-company-app/src/main/java/com/fs/app/param/ChangeUserDeptAndPostParam.java
  9. 29 0
      fs-company-app/src/main/java/com/fs/app/param/CompanyUserChangeApplyParam.java
  10. 32 0
      fs-company-app/src/main/java/com/fs/app/param/CompanyUserParam.java
  11. 27 0
      fs-company-app/src/main/java/com/fs/app/param/CompanyUserUpdateParam.java
  12. 39 0
      fs-company-app/src/main/java/com/fs/app/service/IAppService.java
  13. 102 0
      fs-company-app/src/main/java/com/fs/app/service/impl/AppServiceImpl.java
  14. 15 0
      fs-company-app/src/main/java/com/fs/app/vo/CompanyRoleVO.java
  15. 32 0
      fs-company-app/src/main/java/com/fs/app/vo/CompanySubUserVO.java
  16. 17 0
      fs-company-app/src/main/java/com/fs/app/vo/UserPostVO.java
  17. 78 0
      fs-company-app/src/main/java/com/fs/core/PermissionService.java
  18. 186 0
      fs-company-app/src/main/java/com/fs/core/aspectj/DataScopeAspect.java
  19. 31 0
      fs-company-app/src/main/java/com/fs/core/config/ApplicationConfig.java
  20. 123 0
      fs-company-app/src/main/java/com/fs/core/config/DruidConfig.java
  21. 72 0
      fs-company-app/src/main/java/com/fs/core/config/FastJson2JsonRedisSerializer.java
  22. 61 0
      fs-company-app/src/main/java/com/fs/core/config/FilterConfig.java
  23. 106 0
      fs-company-app/src/main/java/com/fs/core/config/MyBatisConfig.java
  24. 85 0
      fs-company-app/src/main/java/com/fs/core/config/RedisConfig.java
  25. 78 0
      fs-company-app/src/main/java/com/fs/core/config/ResourcesConfig.java
  26. 40 0
      fs-company-app/src/main/java/com/fs/core/config/SecurityConfig.java
  27. 33 0
      fs-company-app/src/main/java/com/fs/core/config/ServerConfig.java
  28. 124 0
      fs-company-app/src/main/java/com/fs/core/config/SwaggerConfig.java
  29. 63 0
      fs-company-app/src/main/java/com/fs/core/config/ThreadPoolConfig.java
  30. 77 0
      fs-company-app/src/main/java/com/fs/core/config/properties/DruidProperties.java
  31. 27 0
      fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSource.java
  32. 45 0
      fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSourceContextHolder.java
  33. 56 0
      fs-company-app/src/main/java/com/fs/core/interceptor/RepeatSubmitInterceptor.java
  34. 130 0
      fs-company-app/src/main/java/com/fs/core/interceptor/impl/SameUrlDataInterceptor.java
  35. 50 0
      fs-company-app/src/main/java/com/fs/core/security/SecurityUtils.java
  36. 11 0
      fs-service/src/main/java/com/fs/company/domain/CompanyUser.java
  37. 57 0
      fs-service/src/main/java/com/fs/company/domain/CompanyUserChangeApply.java
  38. 24 0
      fs-service/src/main/java/com/fs/company/domain/CompanyUserChangeApplyUser.java
  39. 16 8
      fs-service/src/main/java/com/fs/company/mapper/CompanyDeptMapper.java
  40. 26 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyUserChangeApplyMapper.java
  41. 24 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyUserChangeApplyUserMapper.java
  42. 25 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyUserMapper.java
  43. 16 8
      fs-service/src/main/java/com/fs/company/service/ICompanyDeptService.java
  44. 46 0
      fs-service/src/main/java/com/fs/company/service/ICompanyUserChangeApplyService.java
  45. 23 0
      fs-service/src/main/java/com/fs/company/service/ICompanyUserChangeApplyUserService.java
  46. 48 0
      fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java
  47. 17 7
      fs-service/src/main/java/com/fs/company/service/impl/CompanyDeptServiceImpl.java
  48. 148 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserChangeApplyServiceImpl.java
  49. 33 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserChangeApplyUserServiceImpl.java
  50. 61 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
  51. 19 0
      fs-service/src/main/java/com/fs/company/vo/CompanyUserChangeApplyUserVO.java
  52. 69 0
      fs-service/src/main/java/com/fs/company/vo/CompanyUserChangeApplyVO.java
  53. 8 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseRedPacketLogService.java
  54. 7 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java
  55. 6 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseRedPacketLogServiceImpl.java
  56. 5 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  57. 19 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  58. 2 1
      fs-service/src/main/java/com/fs/his/service/IFsUserService.java
  59. 25 11
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  60. 40 0
      fs-service/src/main/resources/mapper/company/CompanyUserChangeApplyMapper.xml
  61. 27 3
      fs-service/src/main/resources/mapper/company/CompanyUserMapper.xml
  62. 36 0
      fs-service/src/main/resources/mapper/his/FsUserMapper.xml
  63. 132 0
      fs-user-app/src/main/java/com/fs/app/controller/CourseWxH5Controller.java
  64. 238 0
      fs-user-app/src/main/java/com/fs/app/controller/WxCompanyUserController.java
  65. 129 0
      fs-user-app/src/main/java/com/fs/app/controller/WxH5MpController.java

+ 18 - 0
fs-company-app/src/main/java/com/fs/app/config/ImageStorageConfig.java

@@ -0,0 +1,18 @@
+package com.fs.app.config;
+
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Getter
+@Configuration
+public class ImageStorageConfig {
+
+    // 直接返回配置的路径
+    @Value("${image.storage.local-path}")
+    private String localPath;
+
+    @Value("${image.storage.server-path}")
+    private String serverPath;
+
+}

+ 26 - 1
fs-company-app/src/main/java/com/fs/app/controller/AppBaseController.java

@@ -1,9 +1,14 @@
 package com.fs.app.controller;
 
 
+import com.fs.app.exception.FSException;
 import com.fs.app.utils.JwtUtils;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
 import io.jsonwebtoken.Claims;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -14,6 +19,17 @@ public class AppBaseController {
 	@Autowired
 	RedisCache redisCache;
 
+	@Autowired
+	private ICompanyUserService companyUserService;
+	@Autowired
+	private IFsUserService userService;
+	public Long getCompanyId() {
+		String headValue =  ServletUtils.getRequest().getHeader("APPToken");
+		Claims claims=jwtUtils.getClaimByToken(headValue);
+		String userId = claims.getSubject().toString();
+		Long companyId =(Long)redisCache.getCacheObject("companyId:"+userId);
+		return companyId;
+	}
 	public String getUserId()
 	{
 		String headValue =  ServletUtils.getRequest().getHeader("APPToken");
@@ -21,6 +37,15 @@ public class AppBaseController {
 		String userId = claims.getSubject().toString();
 		return userId;
 	}
-
+	//获取商城手机号
+	public Long getUserId(Long companyUserId)
+	{
+		CompanyUser companyUser=companyUserService.selectCompanyUserById(companyUserId);
+		FsUser user=userService.selectFsUserByPhone(companyUser.getPhonenumber());
+		if(user==null){
+			throw new FSException("未授权,请用员工手机号登录商城");
+		}
+		return user.getUserId();
+	}
 
 }

+ 89 - 0
fs-company-app/src/main/java/com/fs/app/controller/CompanyTagController.java

@@ -0,0 +1,89 @@
+package com.fs.app.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.app.annotation.Login;
+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.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.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Api(tags = "企业标签接口")
+@RestController
+@RequestMapping("/app/companyTag")
+@AllArgsConstructor
+public class CompanyTagController extends AppBaseController {
+
+    private final ICompanyTagService companyTagService;
+    private final ICompanyTagUserService companyTagUserService;
+
+    /**
+     * 查询公司标签列表
+     */
+    @Login
+    @GetMapping("/list")
+    @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);
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("companyId", getCompanyId());
+        if (StringUtils.isNotBlank(keyword)) {
+            params.put("keyword", keyword.split(","));
+        }
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<CompanyTag> list = companyTagService.selectCompanyTagListByMap(params);
+        return R.ok().put("data", new PageInfo<>(list));
+    }
+
+    /**
+     * 新增公司标签
+     */
+    @Login
+    @PostMapping("/add")
+    @ApiOperation("新增公司标签")
+    public R add(@RequestBody CompanyTag companyTag) throws CustomException {
+        log.debug("新增公司标签 companyTag: {}", JSON.toJSONString(companyTag));
+        companyTag.setCompanyId(getCompanyId());
+        companyTagService.insertCompanyTag(companyTag);
+        return R.ok();
+    }
+
+    /**
+     * 删除公司标签
+     */
+    @Login
+    @PostMapping("/delete")
+    @ApiOperation("删除公司标签")
+    public R remove(@RequestParam List<Long> tagIds) {
+        log.debug("删除公司标签 tagIds: {}", tagIds);
+        companyTagService.deleteCompanyTagByTagIds(tagIds);
+        return R.ok();
+    }
+
+    @Login
+    @GetMapping("/tagSubUsers")
+    @ApiOperation("标签下会员列表")
+    public R tagSubUsers(@RequestParam List<Long> tagId) {
+        Map<String, Object> params = new HashMap<>();
+        params.put("tagIds", tagId);
+        params.put("companyId", getCompanyId());
+        return R.ok().put("data", companyTagUserService.selectUserListByMap(params));
+    }
+}

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

@@ -0,0 +1,311 @@
+package com.fs.app.controller;
+
+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.service.IAppService;
+import com.fs.app.vo.CompanySubUserVO;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.constant.UserConstants;
+import com.fs.common.core.domain.R;
+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.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.vo.CompanyUserChangeApplyVO;
+import com.fs.core.security.SecurityUtils;
+import com.fs.course.service.IFsCourseRedPacketLogService;
+import com.fs.course.service.IFsCourseWatchLogService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Api(tags = "企业用户接口")
+@RestController
+@RequestMapping("/app/companyUser")
+@AllArgsConstructor
+public class CompanyUserController extends AppBaseController {
+
+    private final ICompanyUserService companyUserService;
+    private final ICompanyService companyService;
+    private final ICompanyDeptService companyDeptService;
+    private final IFsCourseWatchLogService courseWatchLogService;
+    private final IFsCourseRedPacketLogService courseRedPacketLogService;
+    private final ICompanyUserChangeApplyService companyUserChangeApplyService;
+    private final CompanyRoleMapper companyRoleMapper;
+    private final IAppService appService;
+
+    @Login
+    @ApiOperation("查询用户列表")
+    @GetMapping("/getCompanyUserList")
+    public R getCompanyUserList(@RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                                @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        log.debug("查询用户列表 pageNum: {}, pageSize: {}", pageNum, pageSize);
+
+        Long userId = Long.parseLong(getUserId());
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(userId);
+        List<CompanyRole> companyRoles = companyRoleMapper.selectRolePermissionByUserId(userId);
+        List<CompanyUser> companyUsers;
+
+        // 判断是否管理员 或者包含 1:全部数据权限
+        if (companyUser.isAdmin() || companyRoles.stream().anyMatch(r -> "1".equals(r.getDataScope()))) {
+            PageHelper.startPage(pageNum, pageSize);
+            companyUsers = companyUserService.getCompanyUserListByDeptId(null);
+        }
+        // 判断是否包含 3:本部门数据权限 4:本部门及以下数据权限
+        else if (companyRoles.stream().anyMatch(r -> "3".equals(r.getDataScope()) || "4".equals(r.getDataScope()))) {
+            PageHelper.startPage(pageNum, pageSize);
+            companyUsers = companyUserService.getCompanyUserListByDeptId(companyUser.getDeptId());
+        }
+        // 默认空 -- 判断是否包含 5:仅可查看本人
+        else {
+            companyUsers = new ArrayList<>();
+        }
+        PageInfo<CompanyUser> page = new PageInfo<>(companyUsers);
+
+        // 转换对象
+        List<CompanySubUserVO> users = page.getList().stream().map(u -> {
+            CompanySubUserVO vo = new CompanySubUserVO();
+            BeanUtils.copyProperties(u, vo);
+
+            Map<String, Object> params = new HashMap<>();
+            params.put("date", LocalDate.now());
+            params.put("companyUserId", u.getUserId());
+            params.put("logTypes", Arrays.asList(1,2,4));
+
+            // 今日观看人数
+            int count = courseWatchLogService.countByMap(params);
+            vo.setTodayWatchCount(count);
+
+            // 今日完播人数
+            params.put("logTypes", Collections.singletonList(2));
+            int finishCount = courseWatchLogService.countByMap(params);
+            vo.setTodayFinishCount(finishCount);
+
+            // 今日完播率
+            BigDecimal rate = count > 0 ? BigDecimal.valueOf(finishCount)
+                    .divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_UP)
+                    .multiply(BigDecimal.valueOf(100)) : BigDecimal.ZERO;
+            vo.setTodayFinishRate(rate);
+
+            // 今日红包金额
+            BigDecimal amount = courseRedPacketLogService.getSumByCompanyUserIdId(u.getUserId());
+            vo.setTodayRedPacketAmount(amount);
+
+            // 是否存在会员转移申请
+            Wrapper<CompanyUserChangeApply> applyWrapper = Wrappers.<CompanyUserChangeApply>lambdaQuery()
+                    .eq(CompanyUserChangeApply::getFrom, u.getUserId())
+                    .orderByDesc(CompanyUserChangeApply::getApplyTime)
+                    .last("limit 1");
+            CompanyUserChangeApply apply = companyUserChangeApplyService.getOne(applyWrapper);
+            int applyStatus = 3; // 不存在
+            if (Objects.nonNull(apply)) {
+                applyStatus = apply.getStatus();
+            }
+            vo.setApplyStatus(applyStatus);
+
+            return vo;
+        }).collect(Collectors.toList());
+
+        PageInfo<CompanySubUserVO> pageInfo = new PageInfo<>();
+        BeanUtils.copyProperties(page, pageInfo);
+        pageInfo.setList(users);
+
+        return R.ok().put("data", pageInfo);
+    }
+
+    @Login
+    @ApiOperation("获取销售信息")
+    @GetMapping("/getCompanyUserInfo")
+    public R getCompanyUserInfo(@ApiParam(name = "companyUserId", value = "销售用户ID", required = true)
+                                @RequestParam Long companyUserId) {
+        return R.ok().put("data", companyUserService.selectCompanyUserById(companyUserId));
+    }
+
+    @Login
+    @ApiOperation("修改用户信息")
+    @PostMapping("/updateUserInfo")
+    public R updateUserInfo(@Valid @RequestBody CompanyUserUpdateParam param) {
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(param.getUserId());
+        if (Objects.isNull(companyUser)) {
+            throw new ServiceException("用户不存在");
+        }
+
+        companyUser.setUserId(param.getUserId());
+        companyUser.setNickName(param.getNickName());
+        companyUser.setPhonenumber(param.getPhoneNumber());
+        companyUser.setRemark(param.getRemark());
+        companyUserService.updateCompanyUser(companyUser);
+        return R.ok();
+    }
+
+    @ApiOperation("注册")
+    @PostMapping("/resisterCompanyUser")
+    public R resisterCompanyUser(@Valid @RequestBody CompanyUserParam param) {
+        Company company = companyService.selectCompanyById(param.getCompanyId());
+        if (Objects.isNull(company)) {
+            return R.error("公司不存在");
+        }
+
+        // 判断用户数量是否已达到上线
+        Integer count = companyUserService.selectCompanyUserCountByCompanyId(param.getCompanyId());
+        if(count > company.getLimitUserCount()) {
+            return R.error("用户数量已达到上限");
+        }
+
+        if (UserConstants.NOT_UNIQUE.equals(String.valueOf(companyUserService.checkUserName(param.getPhoneNumber())))) {
+            return R.error("注册用户'" + param.getPhoneNumber() + "'失败,登录账号已存在");
+        }
+
+        // 密码校验
+        if (!PatternUtils.checkPassword(param.getPassword())) {
+            return R.error("密码格式不正确,需包含字母、数字和特殊字符,长度为 8-20位");
+        }
+
+        // 组装参数
+        CompanyUser companyUser = new CompanyUser();
+        BeanUtils.copyProperties(param, companyUser);
+
+        companyUser.setUserName(param.getPhoneNumber());
+        companyUser.setPhonenumber(param.getPhoneNumber());
+        companyUser.setPassword(SecurityUtils.encryptPassword(companyUser.getPassword()));
+        companyUser.setCreateTime(new Date());
+        companyUser.setIsAudit(0);
+
+        // 部门
+        CompanyDept dept = companyDeptService.getDefaultCompanyDeptByCompanyId(param.getCompanyId());
+        if (Objects.nonNull(dept)) {
+            companyUser.setDeptId(dept.getDeptId());
+        }
+
+        companyUserService.insertUser(companyUser);
+        return R.ok();
+    }
+
+    @Login
+    @ApiOperation("接收群管列表")
+    @GetMapping("/companyUserListByCompanyId")
+    public R CompanyUserListByCompanyId(){
+        // 查询公司下销售
+        CompanyUser companyUser = new CompanyUser();
+        companyUser.setCompanyId(getCompanyId());
+        List<CompanyUser> companyUsers = companyUserService.selectCompanyUserList(companyUser);
+        return R.ok().put("data",companyUsers);
+    }
+
+    @Login
+    @RepeatSubmit
+    @ApiOperation("更换会员归属申请")
+    @PostMapping("/changeUserParentApply")
+    public R changeVipUser(@Valid @RequestBody CompanyUserChangeApplyParam param) {
+        // 参数校验
+        CompanyUser fromUser = companyUserService.selectCompanyUserById(param.getFrom());
+        if (Objects.isNull(fromUser)) {
+            return R.error("原归属销售不存在");
+        }
+
+        CompanyUser toUser = companyUserService.selectCompanyUserById(param.getTo());
+        if (Objects.isNull(toUser)) {
+            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("请先选择会员");
+        }
+
+        // 存在待审核的申请不能再次申请
+        Wrapper<CompanyUserChangeApply> applyWrapper = Wrappers.<CompanyUserChangeApply>lambdaQuery()
+                .eq(CompanyUserChangeApply::getFrom, fromUser.getUserId())
+                .eq(CompanyUserChangeApply::getStatus, 0);
+        if (companyUserChangeApplyService.count(applyWrapper) > 0) {
+            throw new ServiceException("存在待审核申请");
+        }
+
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
+
+        // 添加申请
+        companyUserChangeApplyService.apply(param.getFrom(), param.getTo(), param.getType(), param.getIds(), companyUser.getCompanyId(), companyUser.getUserName());
+        return R.ok();
+    }
+
+    @Login
+    @ApiOperation("申请列表")
+    @GetMapping("/applyList")
+    public R applyList(@RequestParam(required = false) Integer status,
+                       @RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                       @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        log.debug("申请列表 status: {}, pageNum: {}, pageSize: {}", status, pageNum, pageSize);
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("status", status);
+        map.put("companyId", getCompanyId());
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<CompanyUserChangeApplyVO> list = companyUserChangeApplyService.selectApplyListByMap(map);
+        PageInfo<CompanyUserChangeApplyVO> page = new PageInfo<>(list);
+        return R.ok().put("data", page);
+    }
+
+    @Login
+    @ApiOperation("部门树列表")
+    @GetMapping("/deptList")
+    public R deptList() {
+        return R.ok().put("data", appService.getDeptTreeSelect(getCompanyId()));
+    }
+
+    @Login
+    @ApiOperation("岗位列表")
+    @GetMapping("/postList")
+    public R postList() {
+        return R.ok().put("data", appService.postList(getCompanyId()));
+    }
+
+    @Login
+    @ApiOperation("角色列表")
+    @GetMapping("/roleList")
+    public R roleList() {
+        return R.ok().put("data", appService.roleList(getCompanyId()));
+    }
+
+    @Login
+    @ApiOperation("修改用户部门和岗位")
+    @PostMapping("/changeUserDeptAndPost")
+    public R changeUserDeptAndPost(@Valid @RequestBody ChangeUserDeptAndPostParam param) {
+        log.debug("changeUserDeptAndPost param: {}", JSON.toJSONString(param));
+
+        CompanyUser companyUser = companyUserService.selectCompanyUserByUserId(Long.parseLong(getUserId()));
+        if (!companyUser.isAdmin()) {
+            return R.error("没有权限");
+        }
+
+        appService.changeUserDeptAndPost(param);
+        return R.ok();
+    }
+}

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

@@ -0,0 +1,341 @@
+package com.fs.app.controller;
+
+
+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.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.his.service.IFsUserService;
+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.IFsUserCourseCountService;
+import com.fs.store.vo.h5.*;
+import com.fs.system.service.ISysConfigService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.util.*;
+
+@Slf4j
+@Api(tags = "用户会员相关接口")
+@RestController
+@RequestMapping("/app/fs/user")
+public class FsUserController extends AppBaseController {
+
+    @Autowired
+    private IFsUserService fsUserService;
+
+    @Autowired
+    private ICompanyUserService companyUserService;
+
+    @Autowired
+    private ICompanyTagUserService companyTagUserService;
+
+    @Autowired
+    private ImageStorageConfig imageConfig;
+
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
+
+    @Autowired
+    private IFsUserCourseCountService userCourseCountService;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @Login
+    @PostMapping("/pageList")
+    @ApiOperation("用户会员分页列表")
+    public ResponseResult<PageInfo<FsUserPageListVO>> pageList(@RequestBody FsUserPageListParam param) {
+        param.setUserId(Long.parseLong(getUserId()));
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        PageInfo<FsUserPageListVO> fsUserPageListVOPageInfo = fsUserService.selectFsUserPageList(param);
+//        PageInfo<FsUserPageListVO> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(fsUserPageListVOPageInfo);
+    }
+
+    @Login
+    @GetMapping("/allCompanyUser")
+    @ApiOperation("获取所有公司销售")
+    public ResponseResult<List<CompanyUser>> getAllCompanyUser() {
+        List<CompanyUser> companyUsers = companyUserService.selectAllCompanyUserAndSelf(Long.parseLong(getUserId()));
+        return ResponseResult.ok(companyUsers);
+    }
+
+//    @Login
+//    @GetMapping("/totalNumber")
+//    @ApiOperation("用户会员数量统计")
+//    public ResponseResult<UserListPageVO> getTotalNumber() {
+//        UserListPageVO userNumber = fsUserService.getUserNumber(Long.parseLong(getUserId()));
+//        return ResponseResult.ok(userNumber);
+//    }
+//
+//    @Login
+//    @GetMapping("/details")
+//    @ApiOperation("用户会员详情")
+//    public ResponseResult<UserDetailsVO> getUserDetails(@ApiParam(value = "用户id", required = true) @RequestParam Long userId,
+//                                                        @ApiParam(value = "时间tab,不传表示查询全部,分别是:今天、昨天、前天、近七天", required = true) @RequestParam(required = false) String dateTag) {
+//        UserDetailsVO userDetails = fsUserService.getUserDetails(Long.parseLong(getUserId()), userId, dateTag);
+//        return ResponseResult.ok(userDetails);
+//    }
+//
+//    @Login
+//    @GetMapping("/tagList")
+//    @ApiOperation("用户会员标签列表")
+//    public ResponseResult<PageInfo<CompanyUserTagListVO>> getTagList(TagListParam param) {
+//        param.setUserId(Long.parseLong(getUserId()));
+//        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+//        List<CompanyUserTagListVO> tagList = companyTagUserService.getTagList(param);
+//        PageInfo<CompanyUserTagListVO> pageInfo = new PageInfo<>(tagList);
+//        return ResponseResult.ok(pageInfo);
+//    }
+//
+//    @Login
+//    @PostMapping("/disabled")
+//    @ApiOperation("批量禁用会员")
+//    public ResponseResult<Boolean> disabledUser(@ApiParam(value = "联系人id集合", required = true) @RequestBody String[] ids) {
+//        Boolean r = fsUserService.disabledUser(ids, false);
+//        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);
+//        return ResponseResult.ok(r);
+//    }
+//
+//    @Login
+//    @GetMapping("/firstPage/summaryCount")
+//    @ApiOperation("首页数据-顶部汇总统计")
+//    public ResponseResult<FsUserSummaryCountVO> userSummaryCount() {
+//        long userId = Long.parseLong(getUserId());
+//        return ResponseResult.ok(fsUserService.userSummaryCount(userId));
+//    }
+//
+//    @Login
+//    @GetMapping("/firstPage/statistics")
+//    @ApiOperation("首页数据-课程/答题/红包统计")
+//    public ResponseResult<FsUserStatisticsVO> userStatistics(@ApiParam(value = "开始时间", required = true) @RequestParam String startTime,
+//                                                             @ApiParam(value = "结束时间", required = true) @RequestParam String endTime) {
+//        long userId = Long.parseLong(getUserId());
+//        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+//        String nowDate = dateFormat.format(new Date());
+//        /*---------- 如果传入的日期是今天 ----------*/
+//        UserStatisticsCommonParam param = new UserStatisticsCommonParam();
+//        param.setUserId(userId).setStartTime(startTime).setEndTime(endTime);
+//        FsUserStatisticsVO vo = fsUserService.userStatistics(param);
+//        if (nowDate.compareTo(startTime) > 0 && nowDate.compareTo(endTime) < 0) {
+//            String yesterday = LocalDate.now().minusDays(1).toString();
+//            UserStatisticsCommonParam paramYes = new UserStatisticsCommonParam();
+//            paramYes.setUserId(userId).setStartTime(yesterday + " 00:00:00").setEndTime(yesterday + " 23:59:59");
+//            FsUserStatisticsVO fsUserStatisticsVO = fsUserService.userStatistics(paramYes);
+//            vo.setYesterdayVO(fsUserStatisticsVO);
+//        }
+//        return ResponseResult.ok(vo);
+//    }
+//
+//    @Login
+//    @GetMapping("/statistics/details")
+//    @ApiOperation("数据统计详情-课程/答题/红包统计")
+//    public ResponseResult<FsUserStatisticsVO> userStatisticsDetails(@ApiParam(value = "营期id") @RequestParam(required = false) String periodId,
+//                                                                    @ApiParam(value = "视频id") @RequestParam(required = false) String videoId) {
+//        long userId = Long.parseLong(getUserId());
+//        long companyId = getCompanyId();
+//        UserStatisticsCommonParam param = new UserStatisticsCommonParam();
+//        param.setUserId(userId).setPeriodId(periodId).setVideoId(videoId).setCompanyId(companyId);
+//        FsUserStatisticsVO fsUserStatisticsVO = fsUserService.userStatisticsDetails(param);
+//        return ResponseResult.ok(fsUserStatisticsVO);
+//    }
+//
+//    @Login
+//    @GetMapping("/firstPage/userRanking")
+//    @ApiOperation("首页数据/详情-销售排行榜统计")
+//    public ResponseResult<List<FsUserRankingVO>> userRanking(
+//            @ApiParam(value = "开始时间") @RequestParam(required = false) String startTime,
+//            @ApiParam(value = "结束时间") @RequestParam(required = false) String endTime,
+//            @ApiParam(value = "营期id") @RequestParam(required = false) String periodId,
+//            @ApiParam(value = "视频id") @RequestParam(required = false) String videoId,
+//            @ApiParam(value = "asc-正序,desc-倒序", required = true) @RequestParam String order,
+//            @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam Integer type
+//    ) {
+//        long userId = Long.parseLong(getUserId());
+//        return ResponseResult.ok(fsUserService.userRanking(userId, startTime, endTime, periodId, videoId, order, type));
+//    }
+//
+//    @Login
+//    @GetMapping("/firstPage/courseRanking")
+//    @ApiOperation("首页数据/详情-课程排行榜统计")
+//    public ResponseResult<List<FsCourseRankingVO>> courseRanking(
+//            @ApiParam(value = "开始时间") @RequestParam(required = false) String startTime,
+//            @ApiParam(value = "结束时间") @RequestParam(required = false) String endTime,
+//            @ApiParam(value = "课程id") @RequestParam(required = false) String courseId,
+//            @ApiParam(value = "视频id") @RequestParam(required = false) String videoId,
+//            @ApiParam(value = "asc-正序,desc-倒序", required = true) @RequestParam String order,
+//            @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam Integer type
+//    ) {
+//        long userId = Long.parseLong(getUserId());
+//        return ResponseResult.ok(fsUserService.courseRanking(userId, startTime, endTime, courseId, videoId, order, type));
+//    }
+//
+//    @Login
+//    @GetMapping("/firstPage/graphic")
+//    @ApiOperation("首页数据-转化漏斗图")
+//    public ResponseResult<List<FsUserGraphicStatisticsVO>> graphicStatistics(@ApiParam(value = "开始时间", required = true) @RequestParam String startTime,
+//                                                                             @ApiParam(value = "结束时间", required = true) @RequestParam String endTime) {
+//        long userId = Long.parseLong(getUserId());
+//        UserStatisticsCommonParam param = new UserStatisticsCommonParam();
+//        param.setUserId(userId).setStartTime(startTime).setEndTime(endTime);
+//        List<FsUserGraphicStatisticsVO> list = fsUserService.graphicStatistics(param);
+//        return ResponseResult.ok(list);
+//    }
+//
+//    @Login
+//    @GetMapping("/graphic/details")
+//    @ApiOperation("详情-转化漏斗图")
+//    public ResponseResult<List<FsUserGraphicStatisticsVO>> graphicStatisticsDetails(@ApiParam(value = "营期id") @RequestParam(required = false) String periodId,
+//                                                                                    @ApiParam(value = "视频id") @RequestParam(required = false) String videoId) {
+//        long userId = Long.parseLong(getUserId());
+//        UserStatisticsCommonParam param = new UserStatisticsCommonParam();
+//        param.setUserId(userId).setPeriodId(periodId).setVideoId(videoId);
+//        List<FsUserGraphicStatisticsVO> list = fsUserService.graphicStatistics(param);
+//        return ResponseResult.ok(list);
+//    }
+//
+//    @Login
+//    @ApiOperation("修改用户备注、姓名")
+//    @PostMapping("/changeUserInfo")
+//    public ResponseResult<Object> changeUserInfo(@Valid @RequestBody FsUserUpdateParam 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("/changeUserTags")
+//    public ResponseResult<Object> changeUserTags(@Valid @RequestBody FsUserTagUpdateParam param) {
+//        companyTagUserService.changeUserTags(param.getFsUserIds(), param.getTagIds());
+//        return ResponseResult.ok();
+//    }
+//
+//    @Login
+//    @GetMapping("/courseAnalysis")
+//    @ApiOperation("管理-课程分析-分页列表查询")
+//    public ResponseResult<PageInfo<FsCourseAnalysisVO>> courseAnalysisList(CourseAnalysisParam param) {
+//        param.setCompanyId(getCompanyId());
+//        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+//        List<FsCourseAnalysisVO> list = fsUserService.courseAnalysis(param);
+//        PageInfo<FsCourseAnalysisVO> pageInfo = new PageInfo<>(list);
+//        return ResponseResult.ok(pageInfo);
+//    }
+//
+//    @Login
+//    @GetMapping("/companyUser/details")
+//    @ApiOperation("管理-群管数据-根据销售id,群管数据统计")
+//    public ResponseResult<FsUserStatisticsVO> companyUserStatistics(@ApiParam(value = "营期id") @RequestParam(required = false) String periodId,
+//                                                                    @ApiParam(value = "视频id") @RequestParam(required = false) String videoId,
+//                                                                    @ApiParam(value = "销售用户id", required = true) @RequestParam String companyUserId) {
+//        UserStatisticsCommonParam param = new UserStatisticsCommonParam();
+//        param.setUserId(Long.parseLong(getUserId()))
+//                .setPeriodId(periodId).setVideoId(videoId)
+//                .setCompanyId(getCompanyId())
+//                .setCompanyUserId(companyUserId);
+//        FsUserStatisticsVO fsUserStatisticsVO = fsUserService.userStatisticsDetails(param);
+//        return ResponseResult.ok(fsUserStatisticsVO);
+//    }
+//
+//    @Login
+//    @GetMapping("/companyUser/summaryCount")
+//    @ApiOperation("管理-群管数据-顶部会员统计")
+//    public ResponseResult<CompanyUserSummaryCountVO> companyUserSummaryCount(@ApiParam(value = "销售用户id", required = true) @RequestParam String companyUserId) {
+//        long userId = Long.parseLong(getUserId());
+//        return ResponseResult.ok(fsUserService.companyUserSummaryCount(userId, companyUserId));
+//    }
+//
+//    @Login
+//    @ApiOperation("会员关联绑定销售")
+//    @PostMapping("/beMember")
+//    public ResponseResult<Boolean> becomeMember(@Valid @RequestBody FsUserCourseBeMemberParam param) {
+//        return fsUserService.becomeMember(param);
+//    }
+//
+//    @Login
+//    @PostMapping("/userImage")
+//    @ApiOperation("生成分享会员海报")
+//    public R createCourseImage(@RequestBody FsUserCourseBeMemberImageParam param) {
+//        try {
+//            //获取用户头像
+////            FsUser fsUser = fsUserService.selectFsUserById(param.getUserId());
+////            String url = "";
+////            if(fsUser != null) {
+////                url = fsUser.getAvatar();
+////            }
+//            String path = imageConfig.getServerPath();
+//            InputStream inputStream = fsUserCourseService.handleImage("", path);
+//
+//            // 背景图片使用的后台商城配置的会员海报,如果没有配置则使用默认的logo图片
+//            String config = configService.selectConfigByKey("store.config");
+//            JSONObject jsonObject = JSONObject.parseObject(config);
+//            String userPosterImage = jsonObject.getString("userPosterImage");
+//            String backgroundImagePath;
+//            if(StringUtils.isEmpty(userPosterImage)){
+//                backgroundImagePath = "https://fbylive.obs.cn-southwest-2.myhuaweicloud.com/fs/20250430/1745980979886.png";
+//            } else {
+//                backgroundImagePath = userPosterImage;
+//            }
+//            String base64Image = fsUserCourseService.createUserImageQR(param.getRealLink(), backgroundImagePath, inputStream, "png", param.getCompanyUserId());
+//            // 返回Base64编码的图片字符串
+//            Map<String, Object> map = new HashMap<>();
+//            map.put("url", base64Image);
+//            return R.ok().put("posterImage", map);
+//        } catch (Exception e) {
+//            log.error("生成海报失败,param:{}", param);
+//            return R.error("生成海报失败!");
+//        }
+//    }
+
+    @PostMapping("/test")
+    @ApiOperation("测试定时任务")
+    public void userCourseCountTask() {
+        userCourseCountService.insertFsUserCourseCountTask();
+    }
+
+}

+ 0 - 195
fs-company-app/src/main/java/com/fs/app/controller/UserController.java

@@ -1,195 +0,0 @@
-package com.fs.app.controller;
-
-
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.json.JSONUtil;
-import com.fs.ai.service.IBaiduAIService;
-import com.fs.ai.vo.BaiduAIMsgResultVO;
-import com.fs.app.annotation.Login;
-import com.fs.app.param.AICallChatParam;
-import com.fs.app.param.EditChatMsgParam;
-import com.fs.app.param.LoginParam;
-import com.fs.app.vo.UserListVO;
-import com.fs.app.vo.UserVO;
-import com.fs.chat.config.WxConfig;
-import com.fs.chat.domain.ChatMsg;
-import com.fs.chat.domain.ChatMsgLogs;
-import com.fs.chat.domain.ChatRole;
-import com.fs.chat.domain.ChatSession;
-import com.fs.chat.param.ChatMsgListAParam;
-import com.fs.chat.service.IChatMsgLogsService;
-import com.fs.chat.service.IChatMsgService;
-import com.fs.chat.service.IChatRoleService;
-import com.fs.chat.service.IChatSessionService;
-import com.fs.chat.vo.ChatMsgListAVO;
-import com.fs.common.annotation.Log;
-import com.fs.common.core.domain.AjaxResult;
-import com.fs.common.core.domain.R;
-import com.fs.common.core.redis.RedisCache;
-import com.fs.common.utils.PinYinUtil;
-import com.fs.common.utils.SecurityUtils;
-import com.fs.common.utils.ServletUtils;
-import com.fs.common.utils.StringUtils;
-import com.fs.common.utils.sign.Md5Utils;
-import com.fs.common.utils.spring.SpringUtils;
-import com.fs.company.domain.CompanyUser;
-import com.fs.company.service.ICompanyPostService;
-import com.fs.company.service.ICompanyUserService;
-import com.fs.company.vo.CompanyUserVO;
-import com.fs.his.service.IFsUserService;
-import com.github.pagehelper.PageHelper;
-import com.github.pagehelper.PageInfo;
-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 javax.servlet.http.HttpServletRequest;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import static com.fs.common.constant.FsConstants.REDIS_CHAT_SESSION;
-
-
-@RestController
-@RequestMapping(value="/app/user")
-public class UserController extends AppBaseController {
-	@Autowired
-	ICompanyPostService postService;
-	@Autowired
-	IChatRoleService chatRoleService;
-	@Autowired
-	ICompanyUserService companyUserService;
-
-	@Autowired
-	RedisCache redisCache;
-	@PostMapping("/login")
-	public R login(@RequestBody LoginParam param)
-	{
-		CompanyUser user=companyUserService.selectUserByUserName(param.getAccount());
-		if(user==null){
-			return R.error("帐号不存在");
-		}
-		if(!user.getStatus().equals("0")){
-			return R.error("帐号已被禁用");
-		}
-		if(!SecurityUtils.matchesPassword(param.getPassword(),user.getPassword())){
-			return R.error("密码不正确");
-		}
-		String token = jwtUtils.generateToken(user.getUserId());
-//		redisCache.setCacheObject("doctorToken:"+user.getUserId(),token,604800, TimeUnit.SECONDS);
-		redisCache.setCacheObject("AiChatToken:"+user.getUserId(),token,604800, TimeUnit.SECONDS);
-
-		Map<String,Object> map=new HashMap<>();
-		map.put("token",token);
-		map.put("user",user);
-
-		return R.ok().put("data",map);
-	}
-
-
- 	@Login
-	@GetMapping("getCompanyUser")
-	public R getCompanyUser()
-	{
-		CompanyUser user = companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
-		return R.ok().put("data",user);
-	}
-
-	@ApiOperation("检测是否登录")
-	@GetMapping("/checkLogin")
-	public R checkLogin(HttpServletRequest request){
-		if(StringUtils.isEmpty(getUserId())){
-			//未登录
-			return R.error("未登录");
-		}
-		else{
-			//登录
-			String token = jwtUtils.generateToken(Long.parseLong(getUserId()));
-			Map<String,Object> map=new HashMap<>();
-			map.put("token",token);
-			return R.ok("认证成功").put("userId",getUserId()).put("token",token);
-		}
-	}
-
-	@Login
-	@ApiOperation("获取通讯录")
-	@GetMapping("/getAllUsers")
-	public R getAllUsers(HttpServletRequest request, @ApiParam(required = false, name = "searchKey", value = "searchKey") @RequestParam(value = "searchKey", required = false) String searchKey){
-		CompanyUser user=new CompanyUser();
-		user.setNickName(searchKey);
-		CompanyUser companyUser = companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
-		user.setCompanyId(companyUser.getCompanyId());
-		user.setIsDel(0);
-		List<CompanyUserVO> list = companyUserService.selectMyUserList(user);
-		List<UserVO> users=new ArrayList<>();
-		for(CompanyUserVO u:list){
-			UserVO vo=new UserVO();
-			vo.setDeptName(u.getDeptName());
-			vo.setFirstLetter(PinYinUtil.cn2py(u.getNickName().substring(0,1)));
-			vo.setNickName(u.getNickName());
-			vo.setUserId(u.getUserId());
-			vo.setAvatar(u.getAvatar());
-			users.add(vo);
-		}
-		Map<Object, List<UserVO>> res = users.parallelStream().collect(
-				Collectors.groupingBy(
-						item->{return Character.toUpperCase(item.getFirstLetter().charAt(0));},//根据首字母分组
-						TreeMap::new,//有序map实现排序
-						Collectors.toList()
-				)
-		);
-		List<UserListVO> vos=res.entrySet().stream().map(e -> new UserListVO(e.getKey().toString(),e.getValue())).collect(Collectors.toList());
-		return R.ok( ).put("users",vos);
-	}
-
-	/**
-	 * 获取用户信息
-	 * @param request
-	 * @return
-	 */
-	@Login
-	@ApiOperation("获取用户信息")
-	@GetMapping("/getUserInfo")
-	public R getUserInfo(HttpServletRequest request){
-		try {
-			CompanyUser companyUser=companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
-			List<String> postList=postService.selectPostNameListByUserId(Long.parseLong(getUserId()));
-			if(companyUser==null){
-				return R.error(40001,"用户不存在");
-			}
-			if(companyUser.getStatus().equals("1")){
-				return R.error(40002,"用户已停用");
-			}
-			return R.ok().put("user",companyUser).put("post",postList);
-		} catch (Exception e){
-
-			return R.error("操作异常");
-		}
-	}
-	@Login
-	@ApiOperation("获取用户信息ByUserId")
-	@GetMapping("/getUserInfoByUserId")
-	public R getUserInfoByUserId(
-			@ApiParam(required = true, name = "userId", value = "用户ID") @RequestParam(value = "userId", required = false) Long userId,
-			HttpServletRequest request){
-		try {
-			CompanyUser user=companyUserService.selectCompanyUserById(userId);
-			List<String> postList=postService.selectPostNameListByUserId(userId);
-			if(user==null){
-				return R.error(40001,"用户不存在");
-			}
-			if(user.getStatus().equals("1")){
-				return R.error(40002,"用户已停用");
-			}
-			return R.ok().put("user",user).put("post",postList);
-		} catch (Exception e){
-
-			return R.error("操作异常");
-		}
-	}
-
-
-}

+ 44 - 0
fs-company-app/src/main/java/com/fs/app/controller/WxMpController.java

@@ -0,0 +1,44 @@
+package com.fs.app.controller;
+
+import com.fs.common.core.domain.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.Synchronized;
+import me.chanjar.weixin.common.bean.WxJsapiSignature;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+
+@RestController
+@Api("微信配置")
+@RequestMapping("/app/wx/mp")
+public class WxMpController {
+
+    @Autowired
+    private WxMpService wxMpService;
+
+
+    @GetMapping("/getWeiXinConfig")
+    @ApiOperation("微信url验证")
+    @Synchronized
+    public R getWxConfig(@RequestParam String url) throws WxErrorException {
+        try {
+            String sLink = URLDecoder.decode(url, "UTF-8");
+            final WxJsapiSignature jsapiSignature = wxMpService.createJsapiSignature(sLink);
+            return R.ok().put("data", jsapiSignature);
+        } catch (UnsupportedEncodingException e) {
+            // URL解码异常
+            return R.error(e.getMessage());
+        }
+    }
+
+
+}

+ 26 - 0
fs-company-app/src/main/java/com/fs/app/param/ChangeUserDeptAndPostParam.java

@@ -0,0 +1,26 @@
+package com.fs.app.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class ChangeUserDeptAndPostParam {
+    /**
+     * 销售ID
+     */
+    @NotBlank(message = "销售ID不能为空")
+    private String companyUserIds;
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+    /**
+     * 岗位ID
+     */
+    private String postIds;
+    /**
+     * 角色ID
+     */
+    private String roleIds;
+}

+ 29 - 0
fs-company-app/src/main/java/com/fs/app/param/CompanyUserChangeApplyParam.java

@@ -0,0 +1,29 @@
+package com.fs.app.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@Data
+public class CompanyUserChangeApplyParam {
+    /**
+     * 原归属销售
+     */
+    @NotNull(message = "原归属销售ID不能为空")
+    private Long from;
+    /**
+     * 申请归属销售
+     */
+    @NotNull(message = "申请更换归属销售ID不能为空")
+    private Long to;
+    /**
+     * 类型  0全部 1部分
+     */
+    @NotNull(message = "类型不能为空")
+    private Integer type;
+    /**
+     * 需更换归属会员id集合
+     */
+    private List<Long> ids;
+}

+ 32 - 0
fs-company-app/src/main/java/com/fs/app/param/CompanyUserParam.java

@@ -0,0 +1,32 @@
+package com.fs.app.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+@Data
+public class CompanyUserParam {
+
+    /**
+     * 公司ID
+     */
+    @NotNull(message = "公司ID不能为空")
+    private Long companyId;
+    /**
+     * 手机号码
+     */
+    @NotBlank(message = "手机号码不能为空")
+    private String phoneNumber;
+    /**
+     * 昵称
+     */
+    @NotBlank(message = "昵称不能为空")
+    private String nickName;
+    /**
+     * 密码
+     */
+    @NotBlank(message = "用户密码不能为空")
+    private String password;
+
+}

+ 27 - 0
fs-company-app/src/main/java/com/fs/app/param/CompanyUserUpdateParam.java

@@ -0,0 +1,27 @@
+package com.fs.app.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+public class CompanyUserUpdateParam {
+
+    /**
+     * 用户ID
+     */
+    @NotNull(message = "用户ID不能为空")
+    private Long userId;
+    /**
+     * 昵称
+     */
+    private String nickName;
+    /**
+     * 电话号码
+     */
+    private String phoneNumber;
+    /**
+     * 备注
+     */
+    private String remark;
+}

+ 39 - 0
fs-company-app/src/main/java/com/fs/app/service/IAppService.java

@@ -0,0 +1,39 @@
+package com.fs.app.service;
+
+import com.fs.app.param.ChangeUserDeptAndPostParam;
+import com.fs.app.vo.CompanyRoleVO;
+import com.fs.app.vo.UserPostVO;
+import com.fs.company.domain.CompanyDeptTreeSelect;
+
+import java.util.List;
+
+
+public interface IAppService {
+
+    /**
+     * 修改用户部门和岗位
+     * @param param 参数
+     */
+    void changeUserDeptAndPost(ChangeUserDeptAndPostParam param);
+
+    /**
+     * 获取公司部门树
+     * @param companyId 公司ID
+     * @return  list
+     */
+    List<CompanyDeptTreeSelect> getDeptTreeSelect(Long companyId);
+
+    /**
+     * 获取公司岗位列表
+     * @param companyId 公司ID
+     * @return list
+     */
+    List<UserPostVO> postList(Long companyId);
+
+    /**
+     * 获取公司角色列表
+     * @param companyId 公司ID
+     * @return  list
+     */
+    List<CompanyRoleVO> roleList(Long companyId);
+}

+ 102 - 0
fs-company-app/src/main/java/com/fs/app/service/impl/AppServiceImpl.java

@@ -0,0 +1,102 @@
+package com.fs.app.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.app.param.ChangeUserDeptAndPostParam;
+import com.fs.app.service.IAppService;
+import com.fs.app.vo.CompanyRoleVO;
+import com.fs.app.vo.UserPostVO;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.*;
+import com.fs.company.service.*;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+@AllArgsConstructor
+public class AppServiceImpl implements IAppService {
+
+    private final ICompanyUserService companyUserService;
+    private final ICompanyDeptService companyDeptService;
+    private final ICompanyPostService companyPostService;
+    private final ICompanyUserPostService companyUserPostService;
+    private final ICompanyRoleService companyRoleService;
+    private final ICompanyUserRoleService companyUserRoleService;
+
+
+    @Override
+    public void changeUserDeptAndPost(ChangeUserDeptAndPostParam param) {
+
+    }
+
+    /**
+     * 获取公司部门树
+     * @param companyId 公司ID
+     * @return  list
+     */
+    @Override
+    public List<CompanyDeptTreeSelect> getDeptTreeSelect(Long companyId) {
+        log.debug("getDeptTreeSelect companyId: {}", companyId);
+        if (Objects.isNull(companyId)) {
+            throw new CustomException("获取companyId失败, 请重新登录");
+        }
+        CompanyDept dept = new CompanyDept();
+        dept.setCompanyId(companyId);
+        dept.setStatus("0");
+        List<CompanyDept> depts = companyDeptService.selectCompanyDeptList(dept);
+        return companyDeptService.buildDeptTreeSelect(depts);
+    }
+
+    /**
+     * 获取公司岗位列表
+     * @param companyId 公司ID
+     * @return list
+     */
+    @Override
+    public List<UserPostVO> postList(Long companyId) {
+        log.debug("postList companyId: {}", companyId);
+        if (Objects.isNull(companyId)) {
+            throw new CustomException("获取companyId失败, 请重新登录");
+        }
+        CompanyPost companyPost = new CompanyPost();
+        companyPost.setCompanyId(companyId);
+        return companyPostService.selectCompanyPostList(companyPost).stream().map(p -> {
+            UserPostVO userPostVO = new UserPostVO();
+            userPostVO.setPostId(p.getPostId());
+            userPostVO.setPostName(p.getPostName());
+            return userPostVO;
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * 获取公司角色列表
+     * @param companyId 公司ID
+     * @return  list
+     */
+    @Override
+    public List<CompanyRoleVO> roleList(Long companyId) {
+        log.debug("roleList companyId: {}", companyId);
+        if (Objects.isNull(companyId)) {
+            throw new CustomException("获取companyId失败, 请重新登录");
+        }
+        CompanyRole companyRole = new CompanyRole();
+        companyRole.setCompanyId(companyId);
+        companyRole.setStatus("0");
+        companyRole.setDelFlag("0");
+        return companyRoleService.selectCompanyRoleList(companyRole).stream().map(r -> {
+            CompanyRoleVO companyRoleVO = new CompanyRoleVO();
+            companyRoleVO.setRoleId(r.getRoleId());
+            companyRoleVO.setRoleName(r.getRoleName());
+            return companyRoleVO;
+        }).collect(Collectors.toList());
+    }
+}

+ 15 - 0
fs-company-app/src/main/java/com/fs/app/vo/CompanyRoleVO.java

@@ -0,0 +1,15 @@
+package com.fs.app.vo;
+
+import lombok.Data;
+
+@Data
+public class CompanyRoleVO {
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+    /**
+     * 角色名称
+     */
+    private String roleName;
+}

+ 32 - 0
fs-company-app/src/main/java/com/fs/app/vo/CompanySubUserVO.java

@@ -0,0 +1,32 @@
+package com.fs.app.vo;
+
+import com.fs.company.domain.CompanyUser;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class CompanySubUserVO extends CompanyUser {
+    /**
+     * 今日观看人数
+     */
+    private Integer todayWatchCount;
+    /**
+     * 今日完播人数
+     */
+    private Integer todayFinishCount;
+    /**
+     * 今日完播率
+     */
+    private BigDecimal todayFinishRate;
+    /**
+     * 今日红包金额
+     */
+    private BigDecimal todayRedPacketAmount;
+    /**
+     * 审核状态
+     */
+    private Integer applyStatus;
+}

+ 17 - 0
fs-company-app/src/main/java/com/fs/app/vo/UserPostVO.java

@@ -0,0 +1,17 @@
+package com.fs.app.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class UserPostVO {
+    /**
+     * 岗位ID
+     */
+    private Long postId;
+    /**
+     * 岗位名称
+     */
+    private String postName;
+}

+ 78 - 0
fs-company-app/src/main/java/com/fs/core/PermissionService.java

@@ -0,0 +1,78 @@
+package com.fs.core;
+
+import com.alibaba.druid.support.json.JSONUtils;
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import io.jsonwebtoken.Claims;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+
+/**
+ * 自定义权限实现,ss取自SpringSecurity首字母
+ */
+@Service("ss")
+public class PermissionService
+{
+    @Autowired
+    JwtUtils jwtUtils;
+    @Autowired
+    RedisCache redisCache;
+    /** 所有权限标识 */
+    private static final String ALL_PERMISSION = "*:*:*";
+
+
+
+    /**
+     * 验证用户是否具备某权限
+     *
+     * @param permission 权限字符串
+     * @return 用户是否具备某权限
+     */
+    public boolean hasPermi(String permission)
+    {
+
+        String headValue = ServletUtils.getRequest().getHeader("APPToken");
+        Claims claims=jwtUtils.getClaimByToken(headValue);
+        String userId = claims.getSubject().toString();
+        //获取用户所有的权限
+        String permss=redisCache.getCacheObject("perms:"+userId);
+        if(StringUtils.isNotEmpty(permss)){
+            List<String> perms= (List<String>) JSONUtils.parse(permss);
+            if (StringUtils.isEmpty(permission))
+            {
+                return false;
+            }
+            if (StringUtils.isNull(userId) || CollectionUtils.isEmpty(perms))
+            {
+                return false;
+            }
+            return hasPermissions(perms, permission);
+        }
+        else{
+            return false;
+        }
+
+
+    }
+
+
+
+    /**
+     * 判断是否包含权限
+     *
+     * @param permissions 权限列表
+     * @param permission 权限字符串
+     * @return 用户是否具备某权限
+     */
+    private boolean hasPermissions(List<String> permissions, String permission)
+    {
+        return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
+    }
+
+
+}

+ 186 - 0
fs-company-app/src/main/java/com/fs/core/aspectj/DataScopeAspect.java

@@ -0,0 +1,186 @@
+package com.fs.core.aspectj;
+
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.annotation.DataScope;
+import com.fs.common.core.domain.BaseEntity;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.spring.SpringUtils;
+import com.fs.company.domain.CompanyRole;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyUserService;
+import io.jsonwebtoken.Claims;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+
+/**
+ * 数据过滤处理
+ *
+
+ */
+@Aspect
+@Component
+public class DataScopeAspect
+{
+    @Autowired
+    JwtUtils jwtUtils;
+
+    /**
+     * 全部数据权限
+     */
+    public static final String DATA_SCOPE_ALL = "1";
+
+    /**
+     * 自定数据权限
+     */
+    public static final String DATA_SCOPE_CUSTOM = "2";
+
+    /**
+     * 部门数据权限
+     */
+    public static final String DATA_SCOPE_DEPT = "3";
+
+    /**
+     * 部门及以下数据权限
+     */
+    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
+
+    /**
+     * 仅本人数据权限
+     */
+    public static final String DATA_SCOPE_SELF = "5";
+
+    /**
+     * 数据权限过滤关键字
+     */
+    public static final String DATA_SCOPE = "dataScope";
+
+    // 配置织入点
+    @Pointcut("@annotation(com.fs.common.annotation.DataScope)")
+    public void dataScopePointCut()
+    {
+    }
+
+    @Before("dataScopePointCut()")
+    public void doBefore(JoinPoint point) throws Throwable
+    {
+        handleDataScope(point);
+    }
+
+    protected void handleDataScope(final JoinPoint joinPoint)
+    {
+        // 获得注解
+        DataScope controllerDataScope = getAnnotationLog(joinPoint);
+        if (controllerDataScope == null)
+        {
+            return;
+        }
+        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
+        ServletRequestAttributes sra = (ServletRequestAttributes)ra;
+        HttpServletRequest request = sra.getRequest();
+
+        String headValue = request.getHeader("APPToken");
+        Claims claims = jwtUtils.getClaimByToken(headValue);
+        Long userId =Long.parseLong( claims.getSubject().toString());
+
+        // 获取当前的用户
+        CompanyUser user = SpringUtils.getBean(ICompanyUserService.class).selectCompanyUserById(userId);
+        if (StringUtils.isNotNull(user))
+        {
+            // 如果是超级管理员,则不过滤数据
+            if (StringUtils.isNotNull(user) && !user.isAdmin())
+            {
+                dataScopeFilter(joinPoint, user, controllerDataScope.deptAlias(),
+                        controllerDataScope.userAlias());
+            }
+        }
+    }
+
+    /**
+     * 数据范围过滤
+     *
+     * @param joinPoint 切点
+     * @param user 用户
+     * @param userAlias 别名
+     */
+    public static void dataScopeFilter(JoinPoint joinPoint, CompanyUser user, String deptAlias, String userAlias)
+    {
+        StringBuilder sqlString = new StringBuilder();
+
+        for (CompanyRole role : user.getRoles())
+        {
+            String dataScope = role.getDataScope();
+            if (DATA_SCOPE_ALL.equals(dataScope))
+            {
+                sqlString = new StringBuilder();
+                break;
+            }
+            else if (DATA_SCOPE_CUSTOM.equals(dataScope))
+            {
+                sqlString.append(StringUtils.format(
+                        " OR {}.dept_id IN ( SELECT dept_id FROM company_role_dept WHERE role_id = {} ) ", deptAlias,
+                        role.getRoleId()));
+            }
+            else if (DATA_SCOPE_DEPT.equals(dataScope))
+            {
+                sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
+            }
+            else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
+            {
+                sqlString.append(StringUtils.format(
+                        " OR {}.dept_id IN ( SELECT dept_id FROM company_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
+                        deptAlias, user.getDeptId(), user.getDeptId()));
+            }
+            else if (DATA_SCOPE_SELF.equals(dataScope))
+            {
+                if (StringUtils.isNotBlank(userAlias))
+                {
+                    sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
+                }
+                else
+                {
+                    // 数据权限为仅本人且没有userAlias别名不查询任何数据
+//                    sqlString.append(" OR 1=0 ");
+                    sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
+                }
+            }
+        }
+
+        if (StringUtils.isNotBlank(sqlString.toString()))
+        {
+            Object params = joinPoint.getArgs()[0];
+            if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
+            {
+                BaseEntity baseEntity = (BaseEntity) params;
+                baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
+            }
+        }
+    }
+
+    /**
+     * 是否存在注解,如果存在就获取
+     */
+    private DataScope getAnnotationLog(JoinPoint joinPoint)
+    {
+        Signature signature = joinPoint.getSignature();
+        MethodSignature methodSignature = (MethodSignature) signature;
+        Method method = methodSignature.getMethod();
+
+        if (method != null)
+        {
+            return method.getAnnotation(DataScope.class);
+        }
+        return null;
+    }
+}

+ 31 - 0
fs-company-app/src/main/java/com/fs/core/config/ApplicationConfig.java

@@ -0,0 +1,31 @@
+package com.fs.core.config;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+
+import java.util.TimeZone;
+
+/**
+ * 程序注解配置
+ *
+ 
+ */
+@Configuration
+// 表示通过aop框架暴露该代理对象,AopContext能够访问
+@EnableAspectJAutoProxy(exposeProxy = true)
+// 指定要扫描的Mapper类的包的路径
+@MapperScan("com.fs.**.mapper")
+public class ApplicationConfig
+{
+    /**
+     * 时区配置
+     */
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()
+    {
+        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
+    }
+}

+ 123 - 0
fs-company-app/src/main/java/com/fs/core/config/DruidConfig.java

@@ -0,0 +1,123 @@
+package com.fs.core.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
+import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
+import com.alibaba.druid.util.Utils;
+import com.fs.common.enums.DataSourceType;
+import com.fs.common.utils.spring.SpringUtils;
+import com.fs.core.config.properties.DruidProperties;
+import com.fs.core.datasource.DynamicDataSource;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+import javax.servlet.*;
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * druid 配置多数据源
+ * 
+
+ */
+@Configuration
+public class DruidConfig
+{
+    @Bean
+    @ConfigurationProperties("spring.datasource.mysql.druid.master")
+    public DataSource masterDataSource(DruidProperties druidProperties)
+    {
+        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
+        return druidProperties.dataSource(dataSource);
+    }
+
+    @Bean
+    @ConfigurationProperties("spring.datasource.mysql.druid.slave")
+    @ConditionalOnProperty(prefix = "spring.datasource.mysql.druid.slave", name = "enabled", havingValue = "true")
+    public DataSource slaveDataSource(DruidProperties druidProperties)
+    {
+        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
+        return druidProperties.dataSource(dataSource);
+    }
+
+    @Bean(name = "dynamicDataSource")
+    @Primary
+    public DynamicDataSource dataSource(DataSource masterDataSource)
+    {
+        Map<Object, Object> targetDataSources = new HashMap<>();
+        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
+        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
+        return new DynamicDataSource(masterDataSource, targetDataSources);
+    }
+    
+    /**
+     * 设置数据源
+     * 
+     * @param targetDataSources 备选数据源集合
+     * @param sourceName 数据源名称
+     * @param beanName bean名称
+     */
+    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
+    {
+        try
+        {
+            DataSource dataSource = SpringUtils.getBean(beanName);
+            targetDataSources.put(sourceName, dataSource);
+        }
+        catch (Exception e)
+        {
+        }
+    }
+
+    /**
+     * 去除监控页面底部的广告
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Bean
+    @ConditionalOnProperty(name = "spring.datasource.mysql.druid.statViewServlet.enabled", havingValue = "true")
+    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
+    {
+        // 获取web监控页面的参数
+        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
+        // 提取common.js的配置路径
+        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
+        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
+        final String filePath = "support/http/resources/js/common.js";
+        // 创建filter进行过滤
+        Filter filter = new Filter()
+        {
+            @Override
+            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException
+            {
+            }
+            @Override
+            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+                    throws IOException, ServletException
+            {
+                chain.doFilter(request, response);
+                // 重置缓冲区,响应头不会被重置
+                response.resetBuffer();
+                // 获取common.js
+                String text = Utils.readFromResource(filePath);
+                // 正则替换banner, 除去底部的广告信息
+                text = text.replaceAll("<a.*?banner\"></a><br/>", "");
+                text = text.replaceAll("powered.*?shrek.wang</a>", "");
+                response.getWriter().write(text);
+            }
+            @Override
+            public void destroy()
+            {
+            }
+        };
+        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
+        registrationBean.setFilter(filter);
+        registrationBean.addUrlPatterns(commonJsPattern);
+        return registrationBean;
+    }
+}

+ 72 - 0
fs-company-app/src/main/java/com/fs/core/config/FastJson2JsonRedisSerializer.java

@@ -0,0 +1,72 @@
+package com.fs.core.config;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.parser.ParserConfig;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.SerializationException;
+import org.springframework.util.Assert;
+
+import java.nio.charset.Charset;
+
+/**
+ * Redis使用FastJson序列化
+ * 
+ 
+ */
+public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
+{
+    @SuppressWarnings("unused")
+    private ObjectMapper objectMapper = new ObjectMapper();
+
+    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
+
+    private Class<T> clazz;
+
+    static
+    {
+        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
+    }
+
+    public FastJson2JsonRedisSerializer(Class<T> clazz)
+    {
+        super();
+        this.clazz = clazz;
+    }
+
+    @Override
+    public byte[] serialize(T t) throws SerializationException
+    {
+        if (t == null)
+        {
+            return new byte[0];
+        }
+        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
+    }
+
+    @Override
+    public T deserialize(byte[] bytes) throws SerializationException
+    {
+        if (bytes == null || bytes.length <= 0)
+        {
+            return null;
+        }
+        String str = new String(bytes, DEFAULT_CHARSET);
+
+        return JSON.parseObject(str, clazz);
+    }
+
+    public void setObjectMapper(ObjectMapper objectMapper)
+    {
+        Assert.notNull(objectMapper, "'objectMapper' must not be null");
+        this.objectMapper = objectMapper;
+    }
+
+    protected JavaType getJavaType(Class<?> clazz)
+    {
+        return TypeFactory.defaultInstance().constructType(clazz);
+    }
+}

+ 61 - 0
fs-company-app/src/main/java/com/fs/core/config/FilterConfig.java

@@ -0,0 +1,61 @@
+package com.fs.core.config;
+
+import com.fs.common.filter.RepeatableFilter;
+import com.fs.common.filter.XssFilter;
+import com.fs.common.utils.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.servlet.DispatcherType;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Filter配置
+ *
+ 
+ */
+@Configuration
+public class FilterConfig
+{
+    @Value("${xss.enabled}")
+    private String enabled;
+
+    @Value("${xss.excludes}")
+    private String excludes;
+
+    @Value("${xss.urlPatterns}")
+    private String urlPatterns;
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Bean
+    public FilterRegistrationBean xssFilterRegistration()
+    {
+        FilterRegistrationBean registration = new FilterRegistrationBean();
+        registration.setDispatcherTypes(DispatcherType.REQUEST);
+        registration.setFilter(new XssFilter());
+        registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
+        registration.setName("xssFilter");
+        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
+        Map<String, String> initParameters = new HashMap<String, String>();
+        initParameters.put("excludes", excludes);
+        initParameters.put("enabled", enabled);
+        registration.setInitParameters(initParameters);
+        return registration;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Bean
+    public FilterRegistrationBean someFilterRegistration()
+    {
+        FilterRegistrationBean registration = new FilterRegistrationBean();
+        registration.setFilter(new RepeatableFilter());
+        registration.addUrlPatterns("/*");
+        registration.setName("repeatableFilter");
+        registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
+        return registration;
+    }
+
+}

+ 106 - 0
fs-company-app/src/main/java/com/fs/core/config/MyBatisConfig.java

@@ -0,0 +1,106 @@
+package com.fs.core.config;
+
+import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
+import org.apache.ibatis.io.VFS;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.util.ClassUtils;
+
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Mybatis支持*匹配扫描包
+ */
+@Configuration
+public class MyBatisConfig {
+    @Autowired
+    private Environment env;
+
+    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
+
+    public static String setTypeAliasesPackage(String typeAliasesPackage) {
+        ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
+        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
+        List<String> allResult = new ArrayList<String>();
+        try {
+            for (String aliasesPackage : typeAliasesPackage.split(",")) {
+                List<String> result = new ArrayList<String>();
+                aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+                        + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
+                Resource[] resources = resolver.getResources(aliasesPackage);
+                if (resources != null && resources.length > 0) {
+                    MetadataReader metadataReader = null;
+                    for (Resource resource : resources) {
+                        if (resource.isReadable()) {
+                            metadataReader = metadataReaderFactory.getMetadataReader(resource);
+                            try {
+                                result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
+                            } catch (ClassNotFoundException e) {
+                                e.printStackTrace();
+                            }
+                        }
+                    }
+                }
+                if (result.size() > 0) {
+                    HashSet<String> hashResult = new HashSet<String>(result);
+                    allResult.addAll(hashResult);
+                }
+            }
+            if (allResult.size() > 0) {
+                typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
+            } else {
+                throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return typeAliasesPackage;
+    }
+
+    //    @Bean
+//    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
+//    {
+//        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
+//        String mapperLocations = env.getProperty("mybatis.mapperLocations");
+//        String configLocation = env.getProperty("mybatis.configLocation");
+//        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
+//        VFS.addImplClass(SpringBootVFS.class);
+//
+//        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
+//        sessionFactory.setDataSource(dataSource);
+//        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
+//        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
+//        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
+//        return sessionFactory.getObject();
+//    }
+    @Bean
+    public SqlSessionFactory sqlSessionFactorys(DataSource dataSource) throws Exception {
+        String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage");
+        String mapperLocations = env.getProperty("mybatis-plus.mapperLocations");
+        String configLocation = env.getProperty("mybatis-plus.configLocation");
+        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
+        VFS.addImplClass(SpringBootVFS.class);
+
+        final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
+        sessionFactory.setDataSource(dataSource);
+        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
+        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
+        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
+        return sessionFactory.getObject();
+    }
+}

+ 85 - 0
fs-company-app/src/main/java/com/fs/core/config/RedisConfig.java

@@ -0,0 +1,85 @@
+package com.fs.core.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.GenericToStringSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * redis配置
+ *
+
+ */
+@Configuration
+@EnableCaching
+public class RedisConfig extends CachingConfigurerSupport
+{
+    @Bean
+    @SuppressWarnings(value = { "unchecked", "rawtypes" })
+    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
+    {
+        RedisTemplate<Object, Object> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        serializer.setObjectMapper(mapper);
+
+        template.setValueSerializer(serializer);
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+        template.afterPropertiesSet();
+        return template;
+    }
+    @Bean
+    public RedisTemplate<String, Boolean> redisTemplateForBoolean(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Boolean> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+        template.setValueSerializer(new GenericToStringSerializer<>(Boolean.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Boolean.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
+    @Bean
+    @SuppressWarnings(value = { "unchecked", "rawtypes" })
+    public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Object> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        serializer.setObjectMapper(mapper);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+        template.setValueSerializer(serializer);
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(serializer);
+
+        template.afterPropertiesSet();
+        return template;
+    }
+}

+ 78 - 0
fs-company-app/src/main/java/com/fs/core/config/ResourcesConfig.java

@@ -0,0 +1,78 @@
+package com.fs.core.config;
+
+import com.fs.common.config.FSConfig;
+import com.fs.common.constant.Constants;
+import com.fs.core.interceptor.RepeatSubmitInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * 通用配置
+ *
+ */
+@Configuration
+public class ResourcesConfig implements WebMvcConfigurer
+{
+    @Autowired
+    private RepeatSubmitInterceptor repeatSubmitInterceptor;
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry)
+    {
+        /** 本地文件上传路径 */
+        registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + FSConfig.getProfile() + "/");
+
+        /** swagger配置 */
+        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
+    }
+
+    /**
+     * 自定义拦截规则
+     */
+    @Override
+    public void addInterceptors(InterceptorRegistry registry)
+    {
+        registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
+    }
+
+    /**
+     * 跨域配置
+     */
+    @Bean
+    public CorsFilter corsFilter() {
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        CorsConfiguration config = new CorsConfiguration();
+        config.setAllowCredentials(true);
+        // 设置访问源地址
+        config.addAllowedOrigin("*");
+        // 设置访问源请求头
+        config.addAllowedHeader("*");
+        // 设置访问源请求方法
+        config.addAllowedMethod("*");
+        // 对接口配置跨域设置
+        source.registerCorsConfiguration("/**", config);
+        return new CorsFilter(source);
+    }
+
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**")
+                .allowedOrigins("*")
+                //.allowedOriginPatterns("*")
+                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
+                .allowCredentials(true)
+                .maxAge(3600)
+                .allowedHeaders("*");
+    }
+
+}
+

+ 40 - 0
fs-company-app/src/main/java/com/fs/core/config/SecurityConfig.java

@@ -0,0 +1,40 @@
+package com.fs.core.config;
+
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+
+/**
+ * spring security配置
+ *
+
+ */
+@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
+public class SecurityConfig extends WebSecurityConfigurerAdapter
+{
+
+    /**
+     * anyRequest          |   匹配所有请求路径
+     * access              |   SpringEl表达式结果为true时可以访问
+     * anonymous           |   匿名可以访问
+     * denyAll             |   用户不能访问
+     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
+     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
+     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
+     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
+     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
+     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
+     * permitAll           |   用户可以任意访问
+     * rememberMe          |   允许通过remember-me登录的用户访问
+     * authenticated       |   用户登录后可访问
+     */
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http.authorizeRequests()
+                .antMatchers("/**").permitAll()
+                .anyRequest().authenticated()
+                .and().csrf().disable();
+    }
+
+}

+ 33 - 0
fs-company-app/src/main/java/com/fs/core/config/ServerConfig.java

@@ -0,0 +1,33 @@
+package com.fs.core.config;
+
+import com.fs.common.utils.ServletUtils;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 服务相关配置
+ * 
+ 
+ */
+@Component
+public class ServerConfig
+{
+    /**
+     * 获取完整的请求路径,包括:域名,端口,上下文访问路径
+     * 
+     * @return 服务地址
+     */
+    public String getUrl()
+    {
+        HttpServletRequest request = ServletUtils.getRequest();
+        return getDomain(request);
+    }
+
+    public static String getDomain(HttpServletRequest request)
+    {
+        StringBuffer url = request.getRequestURL();
+        String contextPath = request.getServletContext().getContextPath();
+        return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
+    }
+}

+ 124 - 0
fs-company-app/src/main/java/com/fs/core/config/SwaggerConfig.java

@@ -0,0 +1,124 @@
+package com.fs.core.config;
+
+import com.fs.common.config.FSConfig;
+import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Swagger2的接口配置
+ *
+
+ */
+@Configuration
+@EnableSwagger2
+@EnableSwaggerBootstrapUI
+public class SwaggerConfig
+{
+    /** 系统基础配置 */
+    @Autowired
+    private FSConfig fsConfig;
+
+    /** 是否开启swagger */
+    @Value("${swagger.enabled}")
+    private boolean enabled;
+
+    /** 设置请求的统一前缀 */
+    @Value("${swagger.pathMapping}")
+    private String pathMapping;
+
+    /**
+     * 创建API
+     */
+    @Bean
+    public Docket createRestApi()
+    {
+        return new Docket(DocumentationType.SWAGGER_2)
+                // 是否启用Swagger
+                .enable(enabled)
+                // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
+                .apiInfo(apiInfo())
+                // 设置哪些接口暴露给Swagger展示
+                .select()
+                // 扫描所有有注解的api,用这种方式更灵活
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                // 扫描指定包中的swagger注解
+                // .apis(RequestHandlerSelectors.basePackage("com.fs.project.tool.swagger"))
+                // 扫描所有 .apis(RequestHandlerSelectors.any())
+                .paths(PathSelectors.any())
+                .build()
+                /* 设置安全模式,swagger可以设置访问token */
+                .securitySchemes(securitySchemes())
+                .securityContexts(securityContexts())
+                .pathMapping(pathMapping);
+    }
+
+    /**
+     * 安全模式,这里指定token通过Authorization头请求头传递
+     */
+    private List<ApiKey> securitySchemes()
+    {
+        List<ApiKey> apiKeyList = new ArrayList<ApiKey>();
+        apiKeyList.add(new ApiKey("Authorization", "AppToken", "header"));
+        return apiKeyList;
+    }
+
+    /**
+     * 安全上下文
+     */
+    private List<SecurityContext> securityContexts()
+    {
+        List<SecurityContext> securityContexts = new ArrayList<>();
+        securityContexts.add(
+                SecurityContext.builder()
+                        .securityReferences(defaultAuth())
+                        .forPaths(PathSelectors.regex("^(?!auth).*$"))
+                        .build());
+        return securityContexts;
+    }
+
+    /**
+     * 默认的安全上引用
+     */
+    private List<SecurityReference> defaultAuth()
+    {
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+        authorizationScopes[0] = authorizationScope;
+        List<SecurityReference> securityReferences = new ArrayList<>();
+        securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
+        return securityReferences;
+    }
+
+    /**
+     * 添加摘要信息
+     */
+    private ApiInfo apiInfo()
+    {
+        // 用ApiInfoBuilder进行定制
+        return new ApiInfoBuilder()
+                // 设置标题
+                .title("标题:销售APP_接口文档")
+                // 描述
+                .description("描述:销售APP接口文档")
+                // 作者信息
+                .contact(new Contact(fsConfig.getName(), null, null))
+                // 版本
+                .version("版本号:" + fsConfig.getVersion())
+                .build();
+    }
+}

+ 63 - 0
fs-company-app/src/main/java/com/fs/core/config/ThreadPoolConfig.java

@@ -0,0 +1,63 @@
+package com.fs.core.config;
+
+import com.fs.common.utils.Threads;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 线程池配置
+ *
+ 
+ **/
+@Configuration
+public class ThreadPoolConfig
+{
+    // 核心线程池大小
+    private int corePoolSize = 50;
+
+    // 最大可创建的线程数
+    private int maxPoolSize = 200;
+
+    // 队列最大长度
+    private int queueCapacity = 1000;
+
+    // 线程池维护线程所允许的空闲时间
+    private int keepAliveSeconds = 300;
+
+    @Bean(name = "threadPoolTaskExecutor")
+    public ThreadPoolTaskExecutor threadPoolTaskExecutor()
+    {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setMaxPoolSize(maxPoolSize);
+        executor.setCorePoolSize(corePoolSize);
+        executor.setQueueCapacity(queueCapacity);
+        executor.setKeepAliveSeconds(keepAliveSeconds);
+        // 线程池对拒绝任务(无线程可用)的处理策略
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        return executor;
+    }
+
+    /**
+     * 执行周期性或定时任务
+     */
+    @Bean(name = "scheduledExecutorService")
+    protected ScheduledExecutorService scheduledExecutorService()
+    {
+        return new ScheduledThreadPoolExecutor(corePoolSize,
+                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build())
+        {
+            @Override
+            protected void afterExecute(Runnable r, Throwable t)
+            {
+                super.afterExecute(r, t);
+                Threads.printException(r, t);
+            }
+        };
+    }
+}

+ 77 - 0
fs-company-app/src/main/java/com/fs/core/config/properties/DruidProperties.java

@@ -0,0 +1,77 @@
+package com.fs.core.config.properties;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * druid 配置属性
+ * 
+
+ */
+@Configuration
+public class DruidProperties
+{
+    @Value("${spring.datasource.mysql.druid.initialSize}")
+    private int initialSize;
+
+    @Value("${spring.datasource.mysql.druid.minIdle}")
+    private int minIdle;
+
+    @Value("${spring.datasource.mysql.druid.maxActive}")
+    private int maxActive;
+
+    @Value("${spring.datasource.mysql.druid.maxWait}")
+    private int maxWait;
+
+    @Value("${spring.datasource.mysql.druid.timeBetweenEvictionRunsMillis}")
+    private int timeBetweenEvictionRunsMillis;
+
+    @Value("${spring.datasource.mysql.druid.minEvictableIdleTimeMillis}")
+    private int minEvictableIdleTimeMillis;
+
+    @Value("${spring.datasource.mysql.druid.maxEvictableIdleTimeMillis}")
+    private int maxEvictableIdleTimeMillis;
+
+    @Value("${spring.datasource.mysql.druid.validationQuery}")
+    private String validationQuery;
+
+    @Value("${spring.datasource.mysql.druid.testWhileIdle}")
+    private boolean testWhileIdle;
+
+    @Value("${spring.datasource.mysql.druid.testOnBorrow}")
+    private boolean testOnBorrow;
+
+    @Value("${spring.datasource.mysql.druid.testOnReturn}")
+    private boolean testOnReturn;
+
+    public DruidDataSource dataSource(DruidDataSource datasource)
+    {
+        /** 配置初始化大小、最小、最大 */
+        datasource.setInitialSize(initialSize);
+        datasource.setMaxActive(maxActive);
+        datasource.setMinIdle(minIdle);
+
+        /** 配置获取连接等待超时的时间 */
+        datasource.setMaxWait(maxWait);
+
+        /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */
+        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
+
+        /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */
+        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
+        datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
+
+        /**
+         * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
+         */
+        datasource.setValidationQuery(validationQuery);
+        /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */
+        datasource.setTestWhileIdle(testWhileIdle);
+        /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
+        datasource.setTestOnBorrow(testOnBorrow);
+        /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
+        datasource.setTestOnReturn(testOnReturn);
+        return datasource;
+    }
+}

+ 27 - 0
fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSource.java

@@ -0,0 +1,27 @@
+package com.fs.core.datasource;
+
+import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
+
+import javax.sql.DataSource;
+import java.util.Map;
+
+/**
+ * 动态数据源
+ * 
+ 
+ */
+public class DynamicDataSource extends AbstractRoutingDataSource
+{
+    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
+    {
+        super.setDefaultTargetDataSource(defaultTargetDataSource);
+        super.setTargetDataSources(targetDataSources);
+        super.afterPropertiesSet();
+    }
+
+    @Override
+    protected Object determineCurrentLookupKey()
+    {
+        return DynamicDataSourceContextHolder.getDataSourceType();
+    }
+}

+ 45 - 0
fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSourceContextHolder.java

@@ -0,0 +1,45 @@
+package com.fs.core.datasource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 数据源切换处理
+ * 
+ 
+ */
+public class DynamicDataSourceContextHolder
+{
+    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
+
+    /**
+     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
+     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
+     */
+    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
+
+    /**
+     * 设置数据源的变量
+     */
+    public static void setDataSourceType(String dsType)
+    {
+        log.info("切换到{}数据源", dsType);
+        CONTEXT_HOLDER.set(dsType);
+    }
+
+    /**
+     * 获得数据源的变量
+     */
+    public static String getDataSourceType()
+    {
+        return CONTEXT_HOLDER.get();
+    }
+
+    /**
+     * 清空数据源变量
+     */
+    public static void clearDataSourceType()
+    {
+        CONTEXT_HOLDER.remove();
+    }
+}

+ 56 - 0
fs-company-app/src/main/java/com/fs/core/interceptor/RepeatSubmitInterceptor.java

@@ -0,0 +1,56 @@
+package com.fs.core.interceptor;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.utils.ServletUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+
+/**
+ * 防止重复提交拦截器
+ *
+
+ */
+@Component
+public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter
+{
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
+    {
+        if (handler instanceof HandlerMethod)
+        {
+            HandlerMethod handlerMethod = (HandlerMethod) handler;
+            Method method = handlerMethod.getMethod();
+            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
+            if (annotation != null)
+            {
+                if (this.isRepeatSubmit(request))
+                {
+                    AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
+                    ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
+                    return false;
+                }
+            }
+            return true;
+        }
+        else
+        {
+            return super.preHandle(request, response, handler);
+        }
+    }
+
+    /**
+     * 验证是否重复提交由子类实现具体的防重复提交的规则
+     *
+     * @param request
+     * @return
+     * @throws Exception
+     */
+    public abstract boolean isRepeatSubmit(HttpServletRequest request);
+}

+ 130 - 0
fs-company-app/src/main/java/com/fs/core/interceptor/impl/SameUrlDataInterceptor.java

@@ -0,0 +1,130 @@
+package com.fs.core.interceptor.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.fs.common.constant.Constants;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.filter.RepeatedlyRequestWrapper;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.http.HttpHelper;
+import com.fs.core.interceptor.RepeatSubmitInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 判断请求url和数据是否和上一次相同,
+ * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
+ *
+
+ */
+@Component
+public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
+{
+    private final String REPEAT_PARAMS = "repeatParams";
+
+    private final String REPEAT_TIME = "repeatTime";
+
+    // 令牌自定义标识
+    @Value("${fs.jwt.header}")
+    private String header;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    /**
+     * 间隔时间,单位:秒 默认10秒
+     *
+     * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
+     */
+    private int intervalTime = 10;
+
+    public void setIntervalTime(int intervalTime)
+    {
+        this.intervalTime = intervalTime;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean isRepeatSubmit(HttpServletRequest request)
+    {
+        String nowParams = "";
+        if (request instanceof RepeatedlyRequestWrapper)
+        {
+            RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
+            nowParams = HttpHelper.getBodyString(repeatedlyRequest);
+        }
+
+        // body参数为空,获取Parameter的数据
+        if (StringUtils.isEmpty(nowParams))
+        {
+            // TreeMap保证有序
+            Map<String, String[]> sortedParams = new TreeMap<>(request.getParameterMap());
+            nowParams = JSON.toJSONString(sortedParams, SerializerFeature.WriteMapNullValue);
+        }
+        Map<String, Object> nowDataMap = new HashMap<String, Object>();
+        nowDataMap.put(REPEAT_PARAMS, nowParams);
+        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
+
+        // 请求地址(作为存放cache的key值)
+        String url = request.getRequestURI();
+
+        // 唯一值(没有消息头则使用请求地址)
+        String submitKey = request.getHeader(header);
+        if (StringUtils.isEmpty(submitKey))
+        {
+            submitKey = url;
+        }
+
+        // 唯一标识(指定key + 消息头)
+        String cache_repeat_key = Constants.REPEAT_SUBMIT_KEY + submitKey;
+
+        Object sessionObj = redisCache.getCacheObject(cache_repeat_key);
+        if (sessionObj != null)
+        {
+            Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
+            if (sessionMap.containsKey(url))
+            {
+                Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
+                if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap))
+                {
+                    return true;
+                }
+            }
+        }
+        Map<String, Object> cacheMap = new HashMap<String, Object>();
+        cacheMap.put(url, nowDataMap);
+        redisCache.setCacheObject(cache_repeat_key, cacheMap, intervalTime, TimeUnit.SECONDS);
+        return false;
+    }
+
+    /**
+     * 判断参数是否相同
+     */
+    private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
+    {
+        String nowParams = (String) nowMap.get(REPEAT_PARAMS);
+        String preParams = (String) preMap.get(REPEAT_PARAMS);
+        return nowParams.equals(preParams);
+    }
+
+    /**
+     * 判断两次间隔时间
+     */
+    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap)
+    {
+        long time1 = (Long) nowMap.get(REPEAT_TIME);
+        long time2 = (Long) preMap.get(REPEAT_TIME);
+        if ((time1 - time2) < (this.intervalTime * 1000))
+        {
+            return true;
+        }
+        return false;
+    }
+}

+ 50 - 0
fs-company-app/src/main/java/com/fs/core/security/SecurityUtils.java

@@ -0,0 +1,50 @@
+package com.fs.core.security;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+
+/**
+ * 安全服务工具类
+ *
+
+ */
+public class SecurityUtils
+{
+
+    /**
+     * 生成BCryptPasswordEncoder密码
+     *
+     * @param password 密码
+     * @return 加密字符串
+     */
+    public static String encryptPassword(String password)
+    {
+
+        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        return passwordEncoder.encode(password);
+    }
+
+    /**
+     * 判断密码是否相同
+     *
+     * @param rawPassword 真实密码
+     * @param encodedPassword 加密后字符
+     * @return 结果
+     */
+    public static boolean matchesPassword(String rawPassword, String encodedPassword)
+    {
+        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        return passwordEncoder.matches(rawPassword, encodedPassword);
+    }
+
+    /**
+     * 是否为管理员
+     *
+     * @param userId 用户ID
+     * @return 结果
+     */
+    public static boolean isAdmin(Long userId)
+    {
+        return userId != null && 1L == userId;
+    }
+}

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

@@ -140,6 +140,9 @@ public class CompanyUser extends BaseEntity
     /** 看课域名 */
     private String domain;
 
+    /** 是否审核 */
+    private Integer isAudit;
+
     public String getDomain() {
         return domain;
     }
@@ -451,5 +454,13 @@ public class CompanyUser extends BaseEntity
         return isDel;
     }
 
+    public Integer getIsAudit() {
+        return isAudit;
+    }
+
+    public void setIsAudit(Integer isAudit) {
+        this.isAudit = isAudit;
+    }
+
 
 }

+ 57 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyUserChangeApply.java

@@ -0,0 +1,57 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("company_user_change_apply")
+public class CompanyUserChangeApply {
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+    /**
+     * 公司ID
+     */
+    private Long companyId;
+    /**
+     * 原归属销售
+     */
+    @TableField("`from`")
+    private Long from;
+    /**
+     * 申请归属销售
+     */
+    @TableField("`to`")
+    private Long to;
+    /**
+     * 审核状态 0待审核 1通过 2拒绝
+     */
+    private Integer status;
+    /**
+     * 申请人
+     */
+    private String applyBy;
+    /**
+     * 申请时间
+     */
+    private LocalDateTime applyTime;
+    /**
+     * 审核人
+     */
+    private String auditBy;
+    /**
+     * 审核时间
+     */
+    private LocalDateTime auditTime;
+    /**
+     * 被拒原因
+     */
+    private String reason;
+}

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

@@ -0,0 +1,24 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+@Data
+@TableName("company_user_change_apply_user")
+public class CompanyUserChangeApplyUser {
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    /**
+     * 申请ID
+     */
+    private Long applyId;
+    /**
+     * 用户ID
+     */
+    private Long userId;
+}

+ 16 - 8
fs-service/src/main/java/com/fs/company/mapper/CompanyDeptMapper.java

@@ -8,15 +8,15 @@ import java.util.List;
 
 /**
  * 部门Mapper接口
- * 
+ *
  * @author fs
  * @date 2021-05-25
  */
-public interface CompanyDeptMapper 
+public interface CompanyDeptMapper
 {
     /**
      * 查询部门
-     * 
+     *
      * @param deptId 部门ID
      * @return 部门
      */
@@ -24,7 +24,7 @@ public interface CompanyDeptMapper
 
     /**
      * 查询部门列表
-     * 
+     *
      * @param companyDept 部门
      * @return 部门集合
      */
@@ -32,7 +32,7 @@ public interface CompanyDeptMapper
 
     /**
      * 新增部门
-     * 
+     *
      * @param companyDept 部门
      * @return 结果
      */
@@ -40,7 +40,7 @@ public interface CompanyDeptMapper
 
     /**
      * 修改部门
-     * 
+     *
      * @param companyDept 部门
      * @return 结果
      */
@@ -48,7 +48,7 @@ public interface CompanyDeptMapper
 
     /**
      * 删除部门
-     * 
+     *
      * @param deptId 部门ID
      * @return 结果
      */
@@ -56,7 +56,7 @@ public interface CompanyDeptMapper
 
     /**
      * 批量删除部门
-     * 
+     *
      * @param deptIds 需要删除的数据ID
      * @return 结果
      */
@@ -83,4 +83,12 @@ public interface CompanyDeptMapper
 
     @Select("select company_id from company_dept where dept_id =#{deptId} ")
     Long selectCompanyDeptByIdCompany(Long deptId);
+
+    /**
+     * 获取公司默认部门
+     * @param companyId 公司ID
+     * @return  公司部门
+     */
+    @Select("select cd.* from company_dept cd where cd.company_id = #{companyId} and cd.dept_name = '默认' and cd.parent_id = 0 limit 1")
+    CompanyDept getTopCompanyDeptByCompanyId(@Param("companyId") Long companyId);
 }

+ 26 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyUserChangeApplyMapper.java

@@ -0,0 +1,26 @@
+package com.fs.company.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyUserChangeApply;
+import com.fs.company.vo.CompanyUserChangeApplyVO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+public interface CompanyUserChangeApplyMapper extends BaseMapper<CompanyUserChangeApply> {
+
+    /**
+     * 查询申请列表
+     * @param map 条件
+     * @return  list
+     */
+    List<CompanyUserChangeApplyVO> selectApplyListByMap(@Param("map") Map<String, Object> map);
+
+    /**
+     * 查询申请详情
+     * @param id    申请记录ID
+     * @return  CompanyUserChangeApplyVO
+     */
+    CompanyUserChangeApplyVO detailById(@Param("id") Long id);
+}

+ 24 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyUserChangeApplyUserMapper.java

@@ -0,0 +1,24 @@
+package com.fs.company.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyUserChangeApplyUser;
+import com.fs.company.vo.CompanyUserChangeApplyUserVO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface CompanyUserChangeApplyUserMapper extends BaseMapper<CompanyUserChangeApplyUser> {
+
+    /**
+     * 查询申请记录关联用户
+     * @param applyId   申请记录ID
+     * @return  list
+     */
+    List<CompanyUserChangeApplyUserVO> getApplyUsers(@Param("applyId") Long applyId);
+
+    /**
+     * 修改申请记录关联用户销售
+     * @param applyId 申请记录ID
+     */
+    void changeUser(@Param("applyId") Long applyId);
+}

+ 25 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyUserMapper.java

@@ -7,6 +7,7 @@ import com.fs.company.vo.CompanyQwUserByIdsVo;
 import com.fs.company.vo.CompanyUserQwListVO;
 import com.fs.company.vo.CompanyUserVO;
 import com.fs.company.vo.DocCompanyUserVO;
+import com.fs.his.vo.OptionsVO;
 import com.fs.qw.vo.CompanyUserQwVO;
 import com.fs.qw.vo.QwUserVO;
 import com.fs.wxUser.domain.CompanyWxUser;
@@ -15,6 +16,7 @@ import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -248,4 +250,27 @@ public interface CompanyUserMapper
 
     List<CompanyUser> getAllUserListLimit(@Param("companyId") Long companyId,
                                           @Param("keywords") String keywords);
+
+    /**
+     * 根据部门ID查询销售列表
+     * @param deptId    部门ID
+     * @return  list
+     */
+    List<CompanyUser> selectAllCompanyUserByDeptId(@Param("deptId") Long deptId);
+
+    @Select("select * from company_user where ma_open_id = #{maOpenId}")
+    CompanyUser getCompanyUserByOpenId(String openId);
+
+    @Select("select * from company_user where phonenumber = #{phoneNumber}")
+    CompanyUser getCompanyUserByPhone(String phoneNumber);
+
+    /**
+     * 查询销售选项列表
+     * @param params    参数
+     * @return  list
+     */
+    List<OptionsVO> selectCompanyUserListByMap(@Param("params") Map<String, Object> params);
+
+    int setIsRegisterMember(@Param("status") boolean status, @Param("userIds")List<Long> userIds);
+
 }

+ 16 - 8
fs-service/src/main/java/com/fs/company/service/ICompanyDeptService.java

@@ -7,15 +7,15 @@ import java.util.List;
 
 /**
  * 部门Service接口
- * 
+ *
  * @author fs
  * @date 2021-05-25
  */
-public interface ICompanyDeptService 
+public interface ICompanyDeptService
 {
     /**
      * 查询部门
-     * 
+     *
      * @param deptId 部门ID
      * @return 部门
      */
@@ -23,7 +23,7 @@ public interface ICompanyDeptService
 
     /**
      * 查询部门列表
-     * 
+     *
      * @param companyDept 部门
      * @return 部门集合
      */
@@ -31,7 +31,7 @@ public interface ICompanyDeptService
 
     /**
      * 新增部门
-     * 
+     *
      * @param companyDept 部门
      * @return 结果
      */
@@ -39,7 +39,7 @@ public interface ICompanyDeptService
 
     /**
      * 修改部门
-     * 
+     *
      * @param companyDept 部门
      * @return 结果
      */
@@ -47,7 +47,7 @@ public interface ICompanyDeptService
 
     /**
      * 批量删除部门
-     * 
+     *
      * @param deptIds 需要删除的部门ID
      * @return 结果
      */
@@ -55,7 +55,7 @@ public interface ICompanyDeptService
 
     /**
      * 删除部门信息
-     * 
+     *
      * @param deptId 部门ID
      * @return 结果
      */
@@ -77,4 +77,12 @@ public interface ICompanyDeptService
     int selectNormalChildrenDeptById(Long deptId);
 
     List<String> selectCompanyDeptNamesByIds(String ids);
+
+
+    /**
+     * 获取公司默认部门
+     * @param companyId 公司ID
+     * @return 部门
+     */
+    CompanyDept getDefaultCompanyDeptByCompanyId(Long companyId);
 }

+ 46 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyUserChangeApplyService.java

@@ -0,0 +1,46 @@
+package com.fs.company.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.CompanyUserChangeApply;
+import com.fs.company.vo.CompanyUserChangeApplyVO;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ICompanyUserChangeApplyService extends IService<CompanyUserChangeApply> {
+
+    /**
+     * 申请更换会员归属
+     *
+     * @param from      原归属
+     * @param to        新归属
+     * @param type      类型  0全部 1部分
+     * @param ids       会员
+     * @param companyId 公司ID
+     * @param userName  操作用户
+     */
+    void apply(Long from, Long to, Integer type, List<Long> ids, Long companyId, String userName);
+
+    /**
+     * 查询申请列表
+     * @param map 条件
+     * @return  list
+     */
+    List<CompanyUserChangeApplyVO> selectApplyListByMap(Map<String, Object> map);
+
+    /**
+     * 查询申请详情
+     * @param id    申请记录ID
+     * @return  CompanyUserChangeApplyVO
+     */
+    CompanyUserChangeApplyVO selectApplyDetailById(Long id);
+
+    /**
+     * 申请记录审核
+     * @param id        申请记录ID
+     * @param status    审核状态 1通过 2拒绝
+     * @param reason    拒绝理由
+     * @param username  审核人
+     */
+    void audit(Long id, Integer status, String reason, String username);
+}

+ 23 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyUserChangeApplyUserService.java

@@ -0,0 +1,23 @@
+package com.fs.company.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.CompanyUserChangeApplyUser;
+import com.fs.company.vo.CompanyUserChangeApplyUserVO;
+
+import java.util.List;
+
+public interface ICompanyUserChangeApplyUserService extends IService<CompanyUserChangeApplyUser> {
+
+    /**
+     * 查询申请记录关联用户
+     * @param applyId   申请记录ID
+     * @return  list
+     */
+    List<CompanyUserChangeApplyUserVO> getApplyUsers(Long applyId);
+
+    /**
+     * 修改申请记录关联用户销售
+     * @param applyId 申请记录ID
+     */
+    void changeUser(Long applyId);
+}

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

@@ -9,8 +9,10 @@ import com.fs.company.vo.CompanyUserQwListVO;
 import com.fs.company.vo.CompanyUserVO;
 import com.fs.company.vo.DocCompanyUserVO;
 import com.fs.his.vo.CitysAreaVO;
+import com.fs.his.vo.OptionsVO;
 import com.fs.qw.vo.CompanyUserQwVO;
 import com.fs.qw.vo.QwUserVO;
+import com.fs.store.vo.h5.UserListPageVO;
 import com.fs.wxUser.domain.CompanyWxUser;
 
 import java.util.List;
@@ -144,4 +146,50 @@ public interface ICompanyUserService {
     List<Long> selectUserAllCompanyUserId(Long companyUserId);
 
     List<CompanyUser> getAllUserListLimit(Long companyId, String keywords);
+
+    List<CompanyUser> selectAllCompanyUserAndSelf(Long userId);
+
+    /**
+     * 查询销售列表
+     * @param deptId 部门ID
+     * @return list
+     */
+    List<CompanyUser> getCompanyUserListByDeptId(Long deptId);
+
+    /**
+     * 根据openid获取销售
+     * @param openId 微信小程序标识
+     * @return
+     */
+    CompanyUser getCompanyUserByOpenId(String openId);
+
+    /**
+     * 根据电话获取销售信息
+     * @param phoneNumber 用户电话
+     * @return
+     */
+    CompanyUser getCompanyUserByPhone(String phoneNumber);
+
+    /**
+     * 更改会员归属
+     * @param userIds
+     * @return
+     */
+    int changeCompanyUser(List<Long> userIds, Long companyUserId, Long companyId);
+
+    /**
+     * 查询销售选项列表
+     * @param params    参数
+     * @return  list
+     */
+    List<OptionsVO> selectCompanyUserListByMap(Map<String, Object> params);
+
+    /**
+     * 批量设置销售的会员是否需要单独注册
+     * @param status true-是,false-否
+     * @param userIds 销售ids
+     * @return
+     */
+    Boolean setIsRegisterMember(boolean status,  List<Long> userIds);
+
 }

+ 17 - 7
fs-service/src/main/java/com/fs/company/service/impl/CompanyDeptServiceImpl.java

@@ -21,7 +21,7 @@ import java.util.stream.Collectors;
 
 /**
  * 部门Service业务层处理
- * 
+ *
  * @author fs
  * @date 2021-05-25
  */
@@ -35,7 +35,7 @@ public class CompanyDeptServiceImpl implements ICompanyDeptService
     private CompanyRoleMapper roleMapper;
     /**
      * 查询部门
-     * 
+     *
      * @param deptId 部门ID
      * @return 部门
      */
@@ -47,7 +47,7 @@ public class CompanyDeptServiceImpl implements ICompanyDeptService
 
     /**
      * 查询部门列表
-     * 
+     *
      * @param companyDept 部门
      * @return 部门
      */
@@ -59,7 +59,7 @@ public class CompanyDeptServiceImpl implements ICompanyDeptService
 
     /**
      * 新增部门
-     * 
+     *
      * @param companyDept 部门
      * @return 结果
      */
@@ -78,7 +78,7 @@ public class CompanyDeptServiceImpl implements ICompanyDeptService
 
     /**
      * 修改部门
-     * 
+     *
      * @param companyDept 部门
      * @return 结果
      */
@@ -139,7 +139,7 @@ public class CompanyDeptServiceImpl implements ICompanyDeptService
 
     /**
      * 批量删除部门
-     * 
+     *
      * @param deptIds 需要删除的部门ID
      * @return 结果
      */
@@ -151,7 +151,7 @@ public class CompanyDeptServiceImpl implements ICompanyDeptService
 
     /**
      * 删除部门信息
-     * 
+     *
      * @param deptId 部门ID
      * @return 结果
      */
@@ -224,6 +224,16 @@ public class CompanyDeptServiceImpl implements ICompanyDeptService
         return companyDeptMapper.selectCompanyDeptNamesByIds(ids);
     }
 
+    /**
+     * 获取公司默认部门
+     * @param companyId 公司ID
+     * @return 部门
+     */
+    @Override
+    public CompanyDept getDefaultCompanyDeptByCompanyId(Long companyId) {
+        return companyDeptMapper.getTopCompanyDeptByCompanyId(companyId);
+    }
+
     /**
      * 递归列表
      */

+ 148 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyUserChangeApplyServiceImpl.java

@@ -0,0 +1,148 @@
+package com.fs.company.service.impl;
+
+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.mapper.CompanyUserChangeApplyMapper;
+import com.fs.company.service.ICompanyUserChangeApplyService;
+import com.fs.company.service.ICompanyUserChangeApplyUserService;
+import com.fs.company.vo.CompanyUserChangeApplyVO;
+import com.fs.his.domain.FsUser;
+import com.fs.his.mapper.FsUserMapper;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+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;
+
+    /**
+     * 申请更换会员归属
+     *
+     * @param from      原归属
+     * @param to        新归属
+     * @param type      类型  0全部 1部分
+     * @param ids       会员
+     * @param companyId 公司ID
+     * @param userName  操作用户
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void apply(Long from, Long to, Integer type, List<Long> 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) {
+                CompanyUserChangeApplyUser uu = new CompanyUserChangeApplyUser();
+                uu.setUserId(user.getUserId());
+                users.add(uu);
+            }
+        }
+        // 部分
+        else {
+            for (Long id : ids) {
+                FsUser user = userMapper.selectFsUserById(id);
+                if (Objects.isNull(user)) {
+                    throw new ServiceException("会员不存在");
+                }
+
+                CompanyUserChangeApplyUser uu = new CompanyUserChangeApplyUser();
+                uu.setUserId(user.getUserId());
+                users.add(uu);
+            }
+        }
+
+        if (users.isEmpty()) {
+            throw new ServiceException("不存在可更换会员");
+        }
+
+        // 添加申请
+        CompanyUserChangeApply apply = new CompanyUserChangeApply();
+        apply.setCompanyId(companyId);
+        apply.setFrom(from);
+        apply.setTo(to);
+        apply.setApplyTime(LocalDateTime.now());
+        apply.setApplyBy(userName);
+        save(apply);
+
+        // 添加申请关联会员
+        users.forEach(user -> user.setApplyId(apply.getId()));
+        companyUserChangeApplyUserService.saveBatch(users);
+    }
+
+    /**
+     * 查询申请列表
+     * @param map 条件
+     * @return  list
+     */
+    @Override
+    public List<CompanyUserChangeApplyVO> selectApplyListByMap(Map<String, Object> map) {
+        List<CompanyUserChangeApplyVO> list = baseMapper.selectApplyListByMap(map);
+        list.forEach(user -> user.setUsers(companyUserChangeApplyUserService.getApplyUsers(user.getId())));
+        return list;
+    }
+
+    /**
+     * 查询申请详情
+     * @param id    申请记录ID
+     * @return  CompanyUserChangeApplyVO
+     */
+    @Override
+    public CompanyUserChangeApplyVO selectApplyDetailById(Long id) {
+        CompanyUserChangeApplyVO companyUserChangeApplyVO = baseMapper.detailById(id);
+        if (Objects.nonNull(companyUserChangeApplyVO)) {
+            companyUserChangeApplyVO.setUsers(companyUserChangeApplyUserService.getApplyUsers(id));
+        }
+        return companyUserChangeApplyVO;
+    }
+
+    /**
+     * 申请记录审核
+     * @param id        申请记录ID
+     * @param status    审核状态
+     * @param reason    拒绝理由
+     * @param username  审核人
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void audit(Long id, Integer status, String reason, String username) {
+        CompanyUserChangeApply companyUserChangeApply = baseMapper.selectById(id);
+        if (Objects.isNull(companyUserChangeApply)) {
+            throw new ServiceException("申请记录不存在");
+        }
+
+        if (companyUserChangeApply.getStatus() != 0) {
+            throw new ServiceException("请勿重复审核");
+        }
+
+        // 通过
+        if (status == 1) {
+            // 修改会员关联销售
+            companyUserChangeApplyUserService.changeUser(id);
+        }
+        // 拒绝
+        else {
+            companyUserChangeApply.setReason(reason);
+        }
+
+        companyUserChangeApply.setAuditBy(username);
+        companyUserChangeApply.setAuditTime(LocalDateTime.now());
+        companyUserChangeApply.setStatus(status);
+        baseMapper.updateById(companyUserChangeApply);
+    }
+}

+ 33 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyUserChangeApplyUserServiceImpl.java

@@ -0,0 +1,33 @@
+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.CompanyUserChangeApplyUserMapper;
+import com.fs.company.service.ICompanyUserChangeApplyUserService;
+import com.fs.company.vo.CompanyUserChangeApplyUserVO;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class CompanyUserChangeApplyUserServiceImpl extends ServiceImpl<CompanyUserChangeApplyUserMapper, CompanyUserChangeApplyUser> implements ICompanyUserChangeApplyUserService {
+
+    /**
+     * 查询申请记录关联用户
+     * @param applyId   申请记录ID
+     * @return  list
+     */
+    @Override
+    public List<CompanyUserChangeApplyUserVO> getApplyUsers(Long applyId) {
+        return baseMapper.getApplyUsers(applyId);
+    }
+
+    /**
+     * 修改申请记录关联用户销售
+     * @param applyId 申请记录ID
+     */
+    @Override
+    public void changeUser(Long applyId) {
+        baseMapper.changeUser(applyId);
+    }
+}

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

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
 import com.fs.common.annotation.DataScope;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.*;
@@ -15,8 +16,10 @@ import com.fs.company.vo.CompanyQwUserByIdsVo;
 import com.fs.company.vo.CompanyUserQwListVO;
 import com.fs.company.vo.CompanyUserVO;
 import com.fs.company.vo.DocCompanyUserVO;
+import com.fs.his.mapper.FsUserMapper;
 import com.fs.his.service.IFsCityService;
 import com.fs.his.vo.CitysAreaVO;
+import com.fs.his.vo.OptionsVO;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.vo.CompanyUserQwVO;
 import com.fs.qw.vo.QwUserVO;
@@ -65,6 +68,9 @@ public class CompanyUserServiceImpl implements ICompanyUserService
     @Autowired
     private RedisCache redisCache;
 
+    @Autowired
+    private FsUserMapper fsUserMapper;
+
     /**
      * 查询物业公司管理员信息
      *
@@ -443,4 +449,59 @@ public class CompanyUserServiceImpl implements ICompanyUserService
     public List<CompanyUser> getAllUserListLimit(Long companyId, String keywords) {
         return companyUserMapper.getAllUserListLimit(companyId,keywords);
     }
+
+    @Override
+    public List<CompanyUser> selectAllCompanyUserAndSelf(Long userId) {
+        return companyUserMapper.selectAllCompanyUserAndSelf(userId);
+    }
+
+    /**
+     * 查询销售列表
+     * @param deptId 部门ID
+     * @return list
+     */
+    @Override
+    public List<CompanyUser> getCompanyUserListByDeptId(Long deptId) {
+        return companyUserMapper.selectAllCompanyUserByDeptId(deptId);
+    }
+
+    @Override
+    public CompanyUser getCompanyUserByOpenId(String openId) {
+        return companyUserMapper.getCompanyUserByOpenId(openId);
+    }
+
+    @Override
+    public CompanyUser getCompanyUserByPhone(String phoneNumber) {
+        return companyUserMapper.getCompanyUserByPhone(phoneNumber);
+    }
+
+    @Override
+    public int changeCompanyUser(List<Long> userIds, Long companyUserId, Long companyId) {
+
+        CompanyUser toUser = companyUserMapper.selectCompanyUserById(companyUserId);
+        if (Objects.isNull(toUser)) {
+            throw new ServiceException("需要更换归属的销售不存在");
+        }
+        return fsUserMapper.batchUpdateUserCompanyUser(userIds, companyUserId, companyId);
+    }
+
+    /**
+     * 查询销售选项列表
+     * @param params    参数
+     * @return  list
+     */
+    @Override
+    public List<OptionsVO> selectCompanyUserListByMap(Map<String, Object> params) {
+        return companyUserMapper.selectCompanyUserListByMap(params);
+    }
+
+    @Override
+    public Boolean setIsRegisterMember(boolean status, List<Long> userIds) {
+        try {
+            companyUserMapper.setIsRegisterMember(status, userIds);
+        } catch (RuntimeException e) {
+            throw new ServiceException("操作异常");
+        }
+        return true;
+    }
 }

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

@@ -0,0 +1,19 @@
+package com.fs.company.vo;
+
+import lombok.Data;
+
+@Data
+public class CompanyUserChangeApplyUserVO {
+    /**
+     * 用户ID
+     */
+    private Long userId;
+    /**
+     * 用户名称
+     */
+    private String userName;
+    /**
+     * 用户头像
+     */
+    private String avatar;
+}

+ 69 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyUserChangeApplyVO.java

@@ -0,0 +1,69 @@
+package com.fs.company.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+public class CompanyUserChangeApplyVO {
+    /**
+     * 主键ID
+     */
+    private Long id;
+    /**
+     * 原销售
+     */
+    private Long from;
+    /**
+     * 原销售头像
+     */
+    private String fromAvatar;
+    /**
+     * 原销售名称
+     */
+    private String fromName;
+    /**
+     * 申请更换销售
+     */
+    private Long to;
+    /**
+     * 申请更换销售头像
+     */
+    private String toAvatar;
+    /**
+     * 申请更换销售名称
+     */
+    private String toName;
+    /**
+     * 状态
+     */
+    private Integer status;
+    /**
+     * 申请人
+     */
+    private String applyBy;
+    /**
+     * 申请时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime applyTime;
+    /**
+     * 审核人
+     */
+    private String auditBy;
+    /**
+     * 审核时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime auditTime;
+    /**
+     * 被拒原因
+     */
+    private String reason;
+    /**
+     * 需更换用户
+     */
+    private List<CompanyUserChangeApplyUserVO> users;
+}

+ 8 - 0
fs-service/src/main/java/com/fs/course/service/IFsCourseRedPacketLogService.java

@@ -1,5 +1,6 @@
 package com.fs.course.service;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 import com.fs.common.core.domain.R;
@@ -67,4 +68,11 @@ public interface IFsCourseRedPacketLogService
 
     List<FsCourseRedPacketLogListPVO> selectFsCourseRedPacketLogListVO(FsCourseRedPacketLogParam fsCourseRedPacketLog);
     List<FsCourseRedPacketLogListPVO> selectFsCourseRedPacketLogListVONew(FsCourseRedPacketLogParam fsCourseRedPacketLog);
+
+    /**
+     * 查询红包金额数
+     * @param companyUserId 销售ID
+     * @return amount
+     */
+    BigDecimal getSumByCompanyUserIdId(Long companyUserId);
 }

+ 7 - 0
fs-service/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java

@@ -11,6 +11,7 @@ import com.fs.qw.param.QwWatchLogStatisticsListParam;
 import com.fs.qw.vo.QwWatchLogStatisticsListVO;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 短链课程看课记录Service接口
@@ -110,5 +111,11 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
 
     void addCourseWatchLogDayNew();
 
+    /**
+     * 根据条件查询数量
+     * @param params    参数
+     * @return  count
+     */
+    int countByMap(Map<String, Object> params);
 
 }

+ 6 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseRedPacketLogServiceImpl.java

@@ -1,5 +1,6 @@
 package com.fs.course.service.impl;
 
+import java.math.BigDecimal;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
@@ -120,4 +121,9 @@ public class FsCourseRedPacketLogServiceImpl implements IFsCourseRedPacketLogSer
     public List<FsCourseRedPacketLogListPVO> selectFsCourseRedPacketLogListVONew(FsCourseRedPacketLogParam fsCourseRedPacketLog) {
         return fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogListVONew(fsCourseRedPacketLog);
     }
+
+    @Override
+    public BigDecimal getSumByCompanyUserIdId(Long companyUserId) {
+        return null;
+    }
 }

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

@@ -343,6 +343,11 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         }
     }
 
+    @Override
+    public int countByMap(Map<String, Object> params) {
+        return 0;
+    }
+
     @Override
     public FsCourseWatchLog getWatchCourseLogVideoBySop(Long videoId,String qwUserId,Long externalId )
     {

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

@@ -1,6 +1,7 @@
 package com.fs.his.mapper;
 
 import java.util.List;
+import java.util.Map;
 
 import com.fs.course.param.CourseAnalysisParam;
 import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
@@ -8,6 +9,7 @@ import com.fs.his.domain.FsUser;
 import com.fs.his.param.FsUserParam;
 import com.fs.his.vo.FsUserExportListVO;
 import com.fs.his.vo.FsUserVO;
+import com.fs.his.vo.OptionsVO;
 import com.fs.qw.dto.FsUserTransferParamDTO;
 import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.vo.QwFsUserVO;
@@ -269,4 +271,21 @@ public interface FsUserMapper
             "</script>"})
     Long selectFsUserCount(@Param("type") int type,@Param("companyId") Long companyId);
 
+
+    int batchUpdateUserCompanyUser(@Param("userIds") List<Long> userIds, @Param("companyUserId") Long companyUserId, @Param("companyId") Long companyId);
+
+    /**
+     * 查询会员选项列表
+     * @param params    参数
+     * @return  list
+     */
+    List<OptionsVO> selectUserListByMap(@Param("params") Map<String, Object> params);
+
+    /**
+     * 查询重粉用户是否存在
+     * @param userIds 会员ids
+     * @return
+     */
+    Integer selectFsUserByUserIds(@Param("userIds") String[] userIds, @Param("companyUserId") Long companyUserId);
+
 }

+ 2 - 1
fs-service/src/main/java/com/fs/his/service/IFsUserService.java

@@ -18,6 +18,7 @@ import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.vo.QwFsUserVO;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.vo.h5.FsUserPageListVO;
+import com.github.pagehelper.PageInfo;
 
 /**
  * 用户Service接口
@@ -123,7 +124,7 @@ public interface IFsUserService
 
     void setRepeatFansTag(FsUserCourseBeMemberParam param);
 
-    List<FsUserPageListVO> selectFsUserPageList(FsUserPageListParam param);
+    PageInfo<FsUserPageListVO> selectFsUserPageList(FsUserPageListParam param);
 
     Boolean disabledUser(String[] ids, boolean status);
 

+ 25 - 11
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -44,6 +44,7 @@ import com.fs.store.vo.h5.FsUserPageListVO;
 import com.fs.system.service.ISysConfigService;
 import com.fs.watch.domain.WatchUser;
 import com.fs.watch.service.WatchUserService;
+import com.github.pagehelper.PageInfo;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.http.util.Asserts;
 import org.slf4j.Logger;
@@ -486,7 +487,7 @@ public class FsUserServiceImpl implements IFsUserService
     }
 
     @Override
-    public List<FsUserPageListVO> selectFsUserPageList(FsUserPageListParam param) {
+    public PageInfo<FsUserPageListVO> selectFsUserPageList(FsUserPageListParam param) {
         List<FsUserPageListVO> fsUserPageListVOS = fsUserMapper.selectFsUserPageList(param);
 
         // 获取当前销售所有重粉会员
@@ -498,19 +499,32 @@ public class FsUserServiceImpl implements IFsUserService
             List<FsUserCompanyUser> repeatCompanyUserNames = fsUserCompanyUserMapper.selectRepeatCompanyUserName(userIds);
             Map<Long, String> map = repeatCompanyUserNames.stream().collect(Collectors.toMap(FsUserCompanyUser::getUserId, FsUserCompanyUser::getRepeatCompanyUserName));
 
-            return fsUserPageListVOS.stream().map(v -> {
-                FsUserPageListVO fsUserPageListVO = new FsUserPageListVO();
-                BeanUtils.copyProperties(v, fsUserPageListVO);
-                fsUserPageListVO.setRepeatCompanyUserName(map.get(v.getUserId()));
-                if("微信用户".equals(v.getNickname()) && StringUtils.isNotEmpty(v.getPhone())){
-                    fsUserPageListVO.setNickname(v.getPhone());
+            for (FsUserPageListVO fsUserPageListVO : fsUserPageListVOS) {
+                fsUserPageListVO.setRepeatCompanyUserName(map.get(fsUserPageListVO.getUserId()));
+                if ("微信用户".equals(fsUserPageListVO.getNickname()) && StringUtils.isNotEmpty(fsUserPageListVO.getPhone())) {
+                    fsUserPageListVO.setNickname(fsUserPageListVO.getPhone());
                 }
-                return fsUserPageListVO;
-            }).collect(Collectors.toList());
-
+                if(StringUtils.isNotEmpty(fsUserPageListVO.getTag()) && fsUserPageListVO.getIsRepeatFans() == 0){
+                    StringBuilder newTag = removeRepeatFansTag(fsUserPageListVO);
+                    fsUserPageListVO.setTag(newTag.toString());
+                }
+            }
+            return new PageInfo<>(fsUserPageListVOS);
         } else {
-            return fsUserPageListVOS;
+            return new PageInfo<>(fsUserPageListVOS);
+        }
+    }
+
+    private static StringBuilder removeRepeatFansTag(FsUserPageListVO fsUserPageListVO) {
+        String tag = fsUserPageListVO.getTag();
+        String[] splitTag = tag.split(",");
+        StringBuilder newTag = new StringBuilder();
+        for (String s : splitTag) {
+            if(!"重粉".equals(s)){
+                newTag.append(s);
+            }
         }
+        return newTag;
     }
 
     @Override

+ 40 - 0
fs-service/src/main/resources/mapper/company/CompanyUserChangeApplyMapper.xml

@@ -0,0 +1,40 @@
+<?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.company.mapper.CompanyUserChangeApplyMapper">
+
+    <resultMap id="applyMap" type="CompanyUserChangeApplyVO">
+    </resultMap>
+
+    <sql id="applySQL">
+        select
+            cuca.*,
+            fu.avatar    fromAvatar,
+            fu.user_name fromName,
+            tu.avatar    toAvatar,
+            tu.user_name toName
+        from company_user_change_apply cuca
+        left join company_user fu on fu.user_id = cuca.`from`
+        left join company_user tu on tu.user_id = cuca.`to`
+    </sql>
+
+    <!-- 查询申请列表 -->
+    <select id="selectApplyListByMap" resultMap="applyMap">
+        <include refid="applySQL"/>
+        <where>
+            <if test="map.status != null">
+                cuca.status = #{map.status}
+            </if>
+            <if test="map.companyId != null">
+                and cuca.company_id = #{map.companyId}
+            </if>
+        </where>
+    </select>
+
+    <!-- 查询申请详情 -->
+    <select id="detailById" resultMap="applyMap">
+        <include refid="applySQL"/>
+        where cuca.id = #{id}
+    </select>
+</mapper>

+ 27 - 3
fs-service/src/main/resources/mapper/company/CompanyUserMapper.xml

@@ -290,11 +290,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		    left join company_role r on r.role_id = ur.role_id
     </sql>
 
+    <select id="selectAllCompanyUserByDeptId" resultType="com.fs.company.domain.CompanyUser">
+        select * from company_user where del_flag = '0'
+        <if test="deptId != null">
+            and dept_id = #{deptId}
+        </if>
+    </select>
 
+    <select id="selectCompanyUserListByMap" resultType="com.fs.his.vo.OptionsVO">
+        select
+        cu.user_id dictValue,
+        cu.nick_name dictLabel
+        from company_user cu
+        <where>
+            <if test="params.companyUserName != null and params.companyUserName != ''">
+                and cu.nick_name like concat('%', #{params.companyUserName}, '%')
+            </if>
+            <if test="params.companyId != null">
+                and cu.company_id = #{params.companyId}
+            </if>
+        </where>
+    </select>
 
-
-
-
+    <update id="setIsRegisterMember" parameterType="Long">
+        update company_user
+        set is_need_register_member = #{status} where user_id in
+        <foreach item="userId" collection="userIds" open="(" separator="," close=")">
+            #{userId}
+        </foreach>
+    </update>
 
 
 

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

@@ -513,4 +513,40 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{userId}
         </foreach>
     </delete>
+
+    <update id="batchUpdateUserCompanyUser">
+        update fs_user
+        set company_id = #{companyId},
+        company_user_id = #{companyUserId}
+        where
+        <foreach collection="userIds" separator="or" item="userId" index="index">
+            user_id = #{userId}
+        </foreach>
+    </update>
+
+    <select id="selectUserListByMap" resultType="com.fs.his.vo.OptionsVO">
+        select
+        u.user_id dictValue,
+        u.nickname dictLabel
+        from fs_user u
+        <where>
+            <if test="params.nickName != null and params.nickName != ''">
+                u.nickname like concat('%', #{params.nickName}, '%')
+            </if>
+        </where>
+    </select>
+
+    <select id ="selectFsUserByUserIds" resultType="Integer">
+        select count(1) from fs_user_company_user
+        LEFT JOIN company_user ON company_user.user_id = fs_user_company_user.company_user_id
+        where is_repeat_fans = 1
+        and (fs_user_company_user.company_user_id = #{companyUserId} OR company_user.parent_id = #{companyUserId})
+        <if test="userIds != null and userIds.length > 0 ">
+            and fs_user_company_user.user_id in
+            <foreach collection="userIds" open="(" close=")" separator="," item="userId">
+                #{userId}
+            </foreach>
+        </if>
+    </select>
+
 </mapper>

+ 132 - 0
fs-user-app/src/main/java/com/fs/app/controller/CourseWxH5Controller.java

@@ -0,0 +1,132 @@
+package com.fs.app.controller;
+
+
+import com.fs.app.annotation.Login;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.course.param.FsCourseQuestionAnswerUParam;
+import com.fs.course.param.FsCourseSendRewardUParam;
+import com.fs.course.param.FsUserCourseVideoFinishUParam;
+import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
+import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
+import com.fs.course.param.newfs.FsUserCourseVideoUParam;
+import com.fs.course.service.*;
+import com.fs.course.vo.FsUserCourseVideoH5VO;
+import com.fs.course.vo.newfs.FsUserCourseVideoLinkDetailsVO;
+import com.fs.store.service.IFsUserService;
+import com.fs.system.service.ISysConfigService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+@Api("会员-h5看课接口")
+@RestController
+@RequestMapping(value = "/app/course/wx/h5")
+public class CourseWxH5Controller extends AppBaseController {
+    Logger logger = LoggerFactory.getLogger(getClass());
+    @Autowired
+    private IFsUserCourseService courseService;
+
+    @Autowired
+    private IFsUserCourseVideoService courseVideoService;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
+
+    @Autowired
+    private IFsCourseWatchLogService courseWatchLogService;
+
+    @Autowired
+    private IFsCourseQuestionBankService questionBankService;
+
+    @Autowired
+    private IFsUserService fsUserService;
+
+
+    @Login
+    @ApiOperation("判断是否添加客服(是否关联销售)")
+    @PostMapping("/isAddKf")
+    public ResponseResult<Boolean> isAddCompanyUser(@Valid @RequestBody FsUserCourseAddCompanyUserParam param) {
+        Long userId = Long.parseLong(getUserId());
+        param.setUserId(userId);
+        return courseVideoService.isAddCompanyUser(param);
+    }
+
+    @ApiOperation("h5课程简介")
+    @GetMapping("/getH5CourseByVideoId")
+    public R getCourseByVideoId(@RequestParam("videoId") Long videoId)
+    {
+        FsUserCourseVideoH5VO course = courseService.selectFsUserCourseVideoH5VOByVideoId(videoId);
+        return R.ok().put("data",course);
+    }
+
+    @Login
+    @ApiOperation("H5课程详情")
+    @GetMapping("/videoDetails")
+    public ResponseResult<FsUserCourseVideoLinkDetailsVO> getCourseVideoDetails(FsUserCourseVideoLinkParam param) {
+        param.setFsUserId(Long.parseLong(getUserId()));
+        return courseVideoService.getLinkCourseVideoDetails(param);
+    }
+
+    @ApiOperation("获取真实链接")
+    @GetMapping("/getRealLink")
+    public R getRealLink(@RequestParam("sortLink")String link)
+    {
+        return courseLinkService.getRealLinkH5(link);
+    }
+
+    @ApiOperation("更新看课时长")
+    @PostMapping("/updateWatchDuration")
+    public R updateWatchDuration(@RequestBody FsUserCourseVideoUParam param)
+    {
+        param.setUserId(Long.parseLong(getUserId()));
+        return courseVideoService.updateWatchDurationWx(param);
+    }
+
+
+    @ApiOperation("获取缓冲流量")
+    @PostMapping("/getInternetTraffic")
+    public R getInternetTraffic(@RequestBody FsUserCourseVideoFinishUParam param) {
+        param.setUserId(Long.parseLong(getUserId()));
+        return courseVideoService.getInternetTraffic(param);
+    }
+
+
+    @ApiOperation("答题")
+    @PostMapping("/courseAnswer")
+    public R courseAnswer(@RequestBody FsCourseQuestionAnswerUParam param){
+        param.setUserId(Long.parseLong(getUserId()));
+        logger.info("zyp \n【答题】:{}",param.getQuestions());
+        if (param.getDuration()==null){
+            logger.info("zyp \n【未识别到时长】:{}",param.getUserId());
+        }
+        return questionBankService.courseAnswer(param, true);
+    }
+
+    @ApiOperation("发放奖励")
+    @PostMapping("/sendReward")
+    @RepeatSubmit
+    public R sendReward(@RequestBody FsCourseSendRewardUParam param)
+    {
+        param.setUserId(Long.parseLong(getUserId()));
+        logger.info("zyp \n【发放奖励】:{}",param);
+        return courseVideoService.sendRewardByFsUser(param);
+    }
+
+
+    @PostMapping("/getErrMsg")
+    public void getErrMsg(@RequestParam("msg") String msg) {
+        logger.error("zyp \n【h5看课中途报错】:{}",msg);
+    }
+
+
+}

+ 238 - 0
fs-user-app/src/main/java/com/fs/app/controller/WxCompanyUserController.java

@@ -0,0 +1,238 @@
+package com.fs.app.controller;
+
+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.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.ICompanyDeptService;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.store.domain.FsUser;
+import com.fs.store.service.IFsUserService;
+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;
+import me.chanjar.weixin.common.error.WxErrorException;
+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.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;
+
+@Api("微信小程序相关接口")
+@RestController
+@RequestMapping(value = "/app/wx/miniapp")
+@Slf4j
+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 ICompanyDeptService companyDeptService;
+
+    @Autowired
+    private IFsUserService userService;
+
+    @Autowired
+    ICompanyService companyService;
+
+    @ApiOperation("小程序-授权登录")
+    @PostMapping("/loginByMa")
+    public R login(@RequestBody LoginMaWxParam param) {
+        log.info("=====================进入小程序授权登录, 入参: {}", param);
+        if (StringUtils.isBlank(param.getCode())) {
+            return R.error("code不存在");
+        }
+        //获取第二个小程序配置,序号从0开始
+        final WxMaService wxService = WxMaConfiguration.getMaService(maProperties.getConfigs().get(1).getAppid());
+        try {
+            WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(param.getCode());
+            this.logger.info(session.getSessionKey());
+            this.logger.info(session.getOpenid());
+            this.logger.info(session.getUnionid());
+            // 解密
+            WxMaPhoneNumberInfo phoneNoInfo = wxService.getUserService().getPhoneNoInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
+//            WxMaUserInfo userInfo = wxService.getUserService().getUserInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
+            FsUser user = userService.selectFsUserByPhone(phoneNoInfo.getPhoneNumber());
+            //以下暂时注释,不需要往销售表添加数据
+//            CompanyUser companyUser = companyUserService.getCompanyUserByOpenId(session.getOpenid());
+//            String ip = IpUtil.getRequestIp();
+//
+////            // 如果公司id为空(表示可能是该公司的第一位销售管理员),则需要根据电话号码判断是否存在销售,如果不存在则提示
+////            if (param.getCompanyId() == null) {
+////                if (checkPhone == null) {
+////                    throw new CustomException("由于不是管理员,不能直接登录", 401);
+////                }
+////            }
+//            if (companyUser == null) {
+//                CompanyUser checkPhone = companyUserService.getCompanyUserByPhone(phoneNoInfo.getPhoneNumber());
+//                if (checkPhone != null) {
+//                    if (checkPhone.getMaOpenId() == null) {
+//                        companyUser = checkPhone;
+//                        companyUser.setMaOpenId(session.getOpenid());
+//                        companyUser.setUserId(companyUser.getUserId());
+//                        companyUser.setUpdateTime(new DateTime());
+//                        companyUser.setLoginIp(ip);
+//                        companyUserService.updateUserProfile(companyUser);
+//                    } else {
+//                        throw new CustomException("此手机号用户已存在");
+//                    }
+//                } else {
+//                    //新增
+//                    companyUser = new CompanyUser();
+//                    companyUser.setUserName(phoneNoInfo.getPhoneNumber());
+//                    companyUser.setNickName(userInfo.getNickName() == null ? "微信用户" : userInfo.getNickName());
+//                    companyUser.setPhonenumber(phoneNoInfo.getPhoneNumber());
+//                    companyUser.setSex(userInfo.getGender());
+//                    //密码初始化为123456
+//                    String pw = "123456";
+//                    companyUser.setPassword(SecurityUtils.encryptPassword(param.getPassword() == null ? pw : param.getPassword()));
+//                    companyUser.setCreateTime(new Date());
+//                    companyUser.setCompanyId(param.getCompanyId());
+//                    companyUser.setParentId(param.getParentCompanyUseId());
+//                    companyUser.setMaOpenId(session.getOpenid());
+//
+//                    //部门信息
+//                    CompanyDept dept = companyDeptService.getDefaultCompanyDeptByCompanyId(param.getCompanyId());
+//                    if (Objects.nonNull(dept)) {
+//                        companyUser.setDeptId(dept.getDeptId());
+//                    }
+//                    companyUserService.insertUser(companyUser);
+//                }
+//            } else {
+//                CompanyUser companyUserMp = new CompanyUser();
+//                companyUserMp.setPhonenumber(phoneNoInfo.getPhoneNumber());
+//                companyUserMp.setUserId(companyUser.getUserId());
+//                companyUserMp.setUpdateTime(new DateTime());
+//                companyUserMp.setLoginIp(ip);
+//                companyUserService.updateUserProfile(companyUser);
+//            }
+
+            // 特殊(需求设计:需要根据公司是否开启黑名单来设置会员初始化的状态)
+            Company company = null;
+            if(param.getCompanyId() != null){
+                company = companyService.selectCompanyById(param.getCompanyId());
+            }
+
+            // 根据销售后台设置的  是否需要单独注册会员 来判断是否需要设置销售的值
+            CompanyUser companyUser = null;
+            if(param.getCompanyUserId() != null){
+                companyUser = companyUserService.selectCompanyUserById(param.getCompanyUserId());
+            }
+
+            String ip = IpUtil.getRequestIp();
+            if (user == null) {
+                if(StringUtils.isNotEmpty(session.getUnionid())){
+                    user = userService.selectFsUserByUnionId(session.getUnionid());
+                } else {
+                    user = userService.selectFsUserByMaOpenId(session.getOpenid());
+                }
+                if (user != null) {
+                    //修改
+                    FsUser userMap = new FsUser();
+                    userMap.setUserId(user.getUserId());
+//                userMap.setMpOpenId(session.getOpenid());
+                    userMap.setMaOpenId(session.getOpenid());
+                    userMap.setUnionId(session.getUnionid());
+                    userMap.setUpdateTime(new DateTime());
+                    userMap.setNickname(param.getNickname() != null ? param.getNickname() : "微信用户");
+                    userMap.setAvatar(param.getAvatar() != null ? param.getAvatar() : null);
+                    userMap.setPhone(phoneNoInfo.getPhoneNumber());
+                    if(companyUser != null && companyUser.getIsNeedRegisterMember() != null && companyUser.getIsNeedRegisterMember() != 1){
+                        user.setCompanyId(param.getCompanyId());
+                        user.setCompanyUserId(param.getCompanyUserId());
+                    }
+                    userService.updateFsUser(userMap);
+                } else {
+                    //新增
+                    user = new FsUser();
+                    user.setNickname(param.getNickname() != null ? param.getNickname() : "微信用户");
+                    user.setAvatar(param.getAvatar() != null ? param.getAvatar() : null);
+                    user.setStatus((company != null ? company.getFsUserIsDefaultBlack() : 0) == 1 ? 0 : 1);
+                    user.setMaOpenId(session.getOpenid());
+                    user.setUnionId(session.getUnionid());
+                    user.setCreateTime(new Date());
+                    user.setPhone(phoneNoInfo.getPhoneNumber());
+                    if(companyUser != null && companyUser.getIsNeedRegisterMember() != null && companyUser.getIsNeedRegisterMember() != 1){
+                        user.setCompanyId(param.getCompanyId());
+                        user.setCompanyUserId(param.getCompanyUserId());
+                    }
+                    userService.insertFsUser(user);
+                }
+            } else {
+                FsUser userMap = new FsUser();
+                userMap.setUserId(user.getUserId());
+                userMap.setUpdateTime(new DateTime());
+                userMap.setPhone(phoneNoInfo.getPhoneNumber());
+                userMap.setLastIp(ip);
+                if (StringUtils.isNotEmpty(session.getUnionid())) {
+                    userMap.setUnionId(session.getUnionid());
+                }
+                userService.updateFsUser(userMap);
+            }
+            log.info("保存成功的用户信息user: {}, 用户id: {}", user, user.getUserId());
+            String token = jwtUtils.generateToken(user.getUserId());
+            // 返回一个写死的数据到前端
+            return R.ok("登录成功").put("token", token).put("phoneNumber", phoneNoInfo.getPhoneNumber()).put("nickName", "微信用户").put("user", user);
+        } catch (WxErrorException e) {
+            this.logger.error(e.getMessage(), e);
+            return R.error("授权失败," + e.getMessage());
+        }
+    }
+
+//    @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;
+    }
+
+
+}

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

@@ -0,0 +1,129 @@
+package com.fs.app.controller;
+
+import cn.hutool.core.date.DateTime;
+import com.fs.app.param.FsUserLoginByMpParam;
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+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.FsCourseWatchLogMapper;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.store.domain.FsUser;
+import com.fs.store.service.IFsUserService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
+import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
+import me.chanjar.weixin.common.error.WxErrorException;
+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.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;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Api("会员-h5-微信相关接口")
+@RestController
+@RequestMapping("/app/wx/h5/mp")
+@Slf4j
+public class WxH5MpController {
+    Logger logger = LoggerFactory.getLogger(getClass());
+    @Autowired
+    private WxMpService wxMpService;
+
+    @Autowired
+    private IFsUserService userService;
+
+    @Autowired
+    JwtUtils jwtUtils;
+    @Autowired
+    RedisCache redisCache;
+
+    @Autowired
+    FsCourseWatchLogMapper fsCourseWatchLogMapper;
+    @Autowired
+    QwExternalContactMapper qwExternalContactMapper;
+    @Autowired
+    ICompanyService companyService;
+    @Autowired
+    ICompanyUserService companyUserService;
+
+
+    @ApiOperation("课程分享链接公众号登录")
+    @PostMapping("/loginByMp")
+    public R loginByMp(@Valid @RequestBody FsUserLoginByMpParam param) throws WxErrorException {
+//        try {
+            //获取微信用户信息
+            WxOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(param.getCode());
+            WxOAuth2UserInfo wxMpUser = wxMpService.getOAuth2Service().getUserInfo(wxMpOAuth2AccessToken, null);
+            //1、特殊(需求设计:需要根据公司是否开启黑名单来设置会员初始化的状态)
+            Company company = null;
+            if(param.getCompanyId() != null){
+                company = companyService.selectCompanyById(param.getCompanyId());
+            }
+            // 根据销售后台设置的  是否需要单独注册会员 来判断是否需要设置销售的值
+            CompanyUser companyUser = companyUserService.selectCompanyUserById(param.getCompanyUserId());
+
+            FsUser user;
+            if(StringUtils.isNotEmpty(wxMpUser.getUnionId())) {
+                user = userService.selectFsUserByUnionId(wxMpUser.getUnionId());
+            } else {
+                user = userService.selectFsUserByMpOpenId(wxMpUser.getOpenid());
+            }
+            if (user != null) {
+                //修改
+                FsUser userMap = new FsUser();
+                userMap.setUserId(user.getUserId());
+                userMap.setMpOpenId(wxMpUser.getOpenid());
+                userMap.setUnionId(wxMpUser.getUnionId());
+                userMap.setUpdateTime(new DateTime());
+                userMap.setAvatar(wxMpUser.getHeadImgUrl());
+                userMap.setNickname(wxMpUser.getNickname());
+                userService.updateFsUser(userMap);
+            } else {
+                //新增
+                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());
+                if(companyUser.getIsNeedRegisterMember() != 1){
+                    user.setCompanyId(param.getCompanyId());
+                    user.setCompanyUserId(param.getCompanyUserId());
+                }
+                userService.insertFsUser(user);
+            }
+            log.error("用户信息user: {}, 用户id: {}", user, user.getUserId());
+            String token = jwtUtils.generateToken(user.getUserId());
+            redisCache.setCacheObject("token:" + user.getUserId(), token, 604800, TimeUnit.SECONDS);
+            Map<String, Object> map = new HashMap<>();
+            map.put("token", token);
+            map.put("user", user);
+            return R.ok(map);
+//        } catch (WxErrorException e) {
+//            if (e.getError().getErrorCode() == 40163) {
+//                return R.error(40163, e.getError().getErrorMsg());
+//            } else {
+//                return R.error("授权失败," + e.getMessage());
+//            }
+//        }
+
+    }
+
+
+}