This commit is contained in:
淋雨一直走YH
2023-09-28 15:38:29 +08:00
commit 7bc57c75dc
1480 changed files with 144393 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
# 插件模块
####登录鉴权插件: snowy-plugin-auth
####业务功能插件: snowy-plugin-biz
####C端功能插件: snowy-plugin-client
####多数据源插件: snowy-plugin-dbs
####开发工具插件: snowy-plugin-dev
####功能案例插件: snowy-plugin-exm
####工作流程插件: snowy-plugin-flw
####代码生成插件: snowy-plugin-gen
####支付功能插件: snowy-plugin-pay
####代码生成插件: snowy-plugin-gen
####移动端管理插件: snowy-plugin-mobile
####系统功能插件: snowy-plugin-sys
####动态租户插件: snowy-plugin-ten
####报表设计插件: snowy-plugin-urp

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>mjkf-xinke-plugin-auth</artifactId>
<packaging>jar</packaging>
<description>登录鉴权插件</description>
<dependencies>
<!-- 每个插件都要引入自己的对外接口 -->
<dependency>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin-auth-api</artifactId>
</dependency>
<!-- 引入开发工具接口,用于获取配置 -->
<dependency>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin-dev-api</artifactId>
</dependency>
<!-- 引入多租户接口,用于获取租户信息 -->
<dependency>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin-ten-api</artifactId>
</dependency>
<!-- sa-token -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
</dependency>
<!-- sa-token 整合 redis 使用jackson序列化方式 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
</dependency>
<!-- Sa-Token插件权限缓存与业务缓存分离 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-alone-redis</artifactId>
</dependency>
<!-- Sa-Token 插件整合SSO -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-sso</artifactId>
</dependency>
<!-- JustAuth 第三方登录 -->
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,138 @@
package mjkf.xinke.auth.core.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.strategy.SaStrategy;
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import mjkf.xinke.auth.core.enums.SaClientTypeEnum;
import mjkf.xinke.auth.core.util.StpClientLoginUserUtil;
import mjkf.xinke.auth.core.util.StpLoginUserUtil;
import mjkf.xinke.common.pojo.CommonResult;
import javax.annotation.Resource;
import java.util.List;
/**
* SaToken鉴权配置
*
*
* @date 2021/10/9 14:24
**/
@Configuration
public class AuthConfigure implements WebMvcConfigurer {
@Resource
private OpenApiExtensionResolver openApiExtensionResolver;
/**
* 注册Sa-Token的注解拦截器打开注解式鉴权功能
*
* 注解的方式有以下几中注解既可以加在接口方法上也可加在Controller类上
* 1.@SaCheckLogin: 登录认证 —— 只有登录之后才能进入该方法(常用)
* 2.@SaCheckRole("admin"): 角色认证 —— 必须具有指定角色标识才能进入该方法(常用)
* 3.@SaCheckPermission("user:add"): 权限认证 —— 必须具有指定权限才能进入该方法(常用)
* 4.@SaCheckSafe: 二级认证校验 —— 必须二级认证之后才能进入该方法
* 5.@SaCheckBasic: HttpBasic认证 —— 只有通过 Basic 认证后才能进入该方法
*
* 在Controller中创建一个接口默认不需要登录也不需要任何权限都可以访问的只有加了上述注解才会校验
**/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册注解拦截器,并排除不需要注解鉴权的接口地址 (与登录拦截器无关,只是说明哪些接口不需要被拦截器拦截,此处都拦截)
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}
@Bean("stpLogic")
public StpLogic getStpLogic() {
// 重写Sa-Token的StpLogic默认客户端类型为B
return new StpLogic(SaClientTypeEnum.B.getValue());
}
@Bean("stpClientLogic")
public StpLogic getStpClientLogic() {
// 重写Sa-Token的StpLogic默认客户端类型为C
return new StpLogic(SaClientTypeEnum.C.getValue());
}
@Bean
public void rewriteSaStrategy() {
// 重写Sa-Token的注解处理器增加注解合并功能
SaStrategy.me.getAnnotation = AnnotatedElementUtils::getMergedAnnotation;
}
/**
* 权限认证接口实现类,集成权限认证功能
*
*
* @date 2022/7/7 16:16
**/
@Component
public static class StpInterfaceImpl implements StpInterface {
/**
* 返回一个账号所拥有的权限码集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
if (SaClientTypeEnum.B.getValue().equals(loginType)) {
return StpLoginUserUtil.getLoginUser().getPermissionCodeList();
} else {
return StpClientLoginUserUtil.getClientLoginUser().getPermissionCodeList();
}
}
/**
* 返回一个账号所拥有的角色标识集合
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
if (SaClientTypeEnum.B.getValue().equals(loginType)) {
return StpLoginUserUtil.getLoginUser().getRoleCodeList();
} else {
return StpClientLoginUserUtil.getClientLoginUser().getRoleCodeList();
}
}
}
/**
* API文档分组配置
*
*
* @date 2022/7/7 16:18
**/
@Bean(value = "authDocApi")
public Docket authDocApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
.title("登录鉴权AUTH")
.description("登录鉴权AUTH")
.termsOfServiceUrl("https://www.xiaonuo.vip")
.contact(new Contact("SNOWY_TEAM","https://www.xiaonuo.vip", "xuyuxiang29@foxmail.com"))
.version("2.0.0")
.build())
.globalResponseMessage(RequestMethod.GET, CommonResult.responseList())
.globalResponseMessage(RequestMethod.POST, CommonResult.responseList())
.groupName("登录鉴权AUTH")
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.apis(RequestHandlerSelectors.basePackage("mjkf.xinke.auth"))
.paths(PathSelectors.any())
.build().extensions(openApiExtensionResolver.buildExtensions("登录鉴权AUTH"));
}
}

View File

@@ -0,0 +1,56 @@
package mjkf.xinke.auth.core.util;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.exception.*;
import cn.hutool.http.HttpStatus;
import lombok.extern.slf4j.Slf4j;
import mjkf.xinke.common.pojo.CommonResult;
import mjkf.xinke.common.util.CommonServletUtil;
@Slf4j
public class AuthExceptionUtil {
/**
* 根据错误类型获取对应的CommonResult只处理SaToken相关异常
*
*
* @date 2021/10/11 15:52
**/
public static CommonResult<String> getCommonResult(Exception e) {
CommonResult<String> commonResult;
if (e instanceof NotLoginException) {
// 如果是未登录异常 401
NotLoginException notLoginException = (NotLoginException) e;
commonResult = CommonResult.get(HttpStatus.HTTP_UNAUTHORIZED, notLoginException.getMessage(), null);
} else if (e instanceof NotRoleException) {
// 如果是角色异常 403
NotRoleException notRoleException = (NotRoleException) e;
commonResult = CommonResult.get(HttpStatus.HTTP_FORBIDDEN, "无此角色:" + notRoleException.getRole() +
",接口地址:" + CommonServletUtil.getRequest().getServletPath(), null);
} else if (e instanceof NotPermissionException) {
// 如果是权限异常 403
NotPermissionException notPermissionException = (NotPermissionException) e;
commonResult = CommonResult.get(HttpStatus.HTTP_FORBIDDEN, "无此权限:" + notPermissionException.getPermission(), null);
} else if (e instanceof DisableServiceException) {
// 如果是被封禁异常 403
DisableServiceException disableServiceException = (DisableServiceException) e;
commonResult = CommonResult.get(HttpStatus.HTTP_FORBIDDEN, "账号被封禁:" + disableServiceException.getDisableTime() + "秒后解封", null);
} else if (e instanceof SaTokenException) {
// 如果是SaToken异常 直接返回
SaTokenException saTokenException = (SaTokenException) e;
commonResult = CommonResult.error(saTokenException.getMessage());
} else {
// 未知异常才打印
log.error(">>> 服务器未知异常,请求地址:{},具体信息:", CommonServletUtil.getRequest().getRequestURL(), e);
// 未知异常返回服务器异常此处不可能执行进入因为本方法处理的一定是SaToken的异常此处仅为安全性考虑
commonResult = CommonResult.error();
}
return commonResult;
}
}

View File

@@ -0,0 +1,122 @@
package mjkf.xinke.auth.modular.login.controller;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import mjkf.xinke.auth.core.annotation.SaClientCheckLogin;
import mjkf.xinke.auth.core.enums.SaClientTypeEnum;
import mjkf.xinke.auth.core.pojo.SaBaseClientLoginUser;
import mjkf.xinke.auth.core.util.StpClientUtil;
import mjkf.xinke.auth.modular.login.param.AuthAccountPasswordLoginParam;
import mjkf.xinke.auth.modular.login.param.AuthGetPhoneValidCodeParam;
import mjkf.xinke.auth.modular.login.param.AuthPhoneValidCodeLoginParam;
import mjkf.xinke.auth.modular.login.result.AuthPicValidCodeResult;
import mjkf.xinke.auth.modular.login.service.AuthService;
import mjkf.xinke.common.pojo.CommonResult;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* C端登录控制器
*
*
* @date 2021/12/23 21:50
*/
@Api(tags = "C端登录控制器")
@ApiSupport(author = "SNOWY_TEAM", order = 1)
@RestController
@Validated
public class AuthClientController {
@Resource
private AuthService authService;
/**
* C端获取图片验证码
*
*
* @date 2022/7/8 9:26
**/
@ApiOperationSupport(order = 1)
@ApiOperation("C端获取图片验证码")
@GetMapping("/auth/c/getPicCaptcha")
public CommonResult<AuthPicValidCodeResult> getPicCaptcha() {
return CommonResult.data(authService.getPicCaptcha(SaClientTypeEnum.C.getValue()));
}
/**
* C端获取手机验证码
*
*
* @date 2022/7/8 9:26
**/
@ApiOperationSupport(order = 2)
@ApiOperation("C端获取手机验证码")
@GetMapping("/auth/c/getPhoneValidCode")
public CommonResult<String> getPhoneValidCode(@Valid AuthGetPhoneValidCodeParam authGetPhoneValidCodeParam) {
return CommonResult.data(authService.getPhoneValidCode(authGetPhoneValidCodeParam, SaClientTypeEnum.C.getValue()));
}
/**
* C端账号密码登录
*
*
* @date 2021/10/15 13:12
**/
@ApiOperationSupport(order = 3)
@ApiOperation("C端账号密码登录")
@PostMapping("/auth/c/doLogin")
public CommonResult<String> doLogin(@RequestBody @Valid AuthAccountPasswordLoginParam authAccountPasswordLoginParam) {
return CommonResult.data(authService.doLogin(authAccountPasswordLoginParam, SaClientTypeEnum.C.getValue()));
}
/**
* C端手机验证码登录
*
*
* @date 2021/10/15 13:12
**/
@ApiOperationSupport(order = 4)
@ApiOperation("C端手机验证码登录")
@PostMapping("/auth/c/doLoginByPhone")
public CommonResult<String> doLoginByPhone(@RequestBody @Valid AuthPhoneValidCodeLoginParam authPhoneValidCodeLoginParam) {
return CommonResult.data(authService.doLoginByPhone(authPhoneValidCodeLoginParam, SaClientTypeEnum.C.getValue()));
}
/**
* C端退出
*
*
* @date 2021/10/15 13:12
**/
@ApiOperationSupport(order = 5)
@ApiOperation("C端退出")
@SaClientCheckLogin
@GetMapping("/auth/c/doLogout")
public CommonResult<String> doLogout() {
StpClientUtil.logout();
return CommonResult.ok();
}
/**
* C端获取用户信息
*
*
* @date 2021/10/15 13:12
**/
@ApiOperationSupport(order = 6)
@ApiOperation("C端获取用户信息")
@SaClientCheckLogin
@GetMapping("/auth/c/getLoginUser")
public CommonResult<SaBaseClientLoginUser> getLoginUser() {
return CommonResult.data(authService.getClientLoginUser());
}
}

View File

@@ -0,0 +1,122 @@
package mjkf.xinke.auth.modular.login.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.StpUtil;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import mjkf.xinke.auth.core.enums.SaClientTypeEnum;
import mjkf.xinke.auth.core.pojo.SaBaseLoginUser;
import mjkf.xinke.auth.modular.login.param.AuthAccountPasswordLoginParam;
import mjkf.xinke.auth.modular.login.param.AuthGetPhoneValidCodeParam;
import mjkf.xinke.auth.modular.login.param.AuthPhoneValidCodeLoginParam;
import mjkf.xinke.auth.modular.login.result.AuthPicValidCodeResult;
import mjkf.xinke.auth.modular.login.service.AuthService;
import mjkf.xinke.common.pojo.CommonResult;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* B端登录控制器
*
*
* @date 2021/12/23 21:50
*/
@Api(tags = "B端登录控制器")
@ApiSupport(author = "SNOWY_TEAM", order = 2)
@RestController
@Validated
public class AuthController {
@Resource
private AuthService authService;
/**
* B端获取图片验证码
*
*
* @date 2022/7/8 9:26
**/
@ApiOperationSupport(order = 1)
@ApiOperation("B端获取图片验证码")
@GetMapping("/auth/b/getPicCaptcha")
public CommonResult<AuthPicValidCodeResult> getPicCaptcha() {
return CommonResult.data(authService.getPicCaptcha(SaClientTypeEnum.B.getValue()));
}
/**
* B端获取手机验证码
*
*
* @date 2022/7/8 9:26
**/
@ApiOperationSupport(order = 2)
@ApiOperation("B端获取手机验证码")
@GetMapping("/auth/b/getPhoneValidCode")
public CommonResult<String> getPhoneValidCode(@Valid AuthGetPhoneValidCodeParam authGetPhoneValidCodeParam) {
return CommonResult.data(authService.getPhoneValidCode(authGetPhoneValidCodeParam, SaClientTypeEnum.B.getValue()));
}
/**
* B端账号密码登录
*
*
* @date 2021/10/15 13:12
**/
@ApiOperationSupport(order = 3)
@ApiOperation("B端账号密码登录")
@PostMapping("/auth/b/doLogin")
public CommonResult<String> doLogin(@RequestBody @Valid AuthAccountPasswordLoginParam authAccountPasswordLoginParam) {
return CommonResult.data(authService.doLogin(authAccountPasswordLoginParam, SaClientTypeEnum.B.getValue()));
}
/**
* B端手机验证码登录
*
*
* @date 2021/10/15 13:12
**/
@ApiOperationSupport(order = 4)
@ApiOperation("B端手机验证码登录")
@PostMapping("/auth/b/doLoginByPhone")
public CommonResult<String> doLoginByPhone(@RequestBody @Valid AuthPhoneValidCodeLoginParam authPhoneValidCodeLoginParam) {
return CommonResult.data(authService.doLoginByPhone(authPhoneValidCodeLoginParam, SaClientTypeEnum.B.getValue()));
}
/**
* B端退出
*
*
* @date 2021/10/15 13:12
**/
@ApiOperationSupport(order = 5)
@ApiOperation("B端退出")
@SaCheckLogin
@GetMapping("/auth/b/doLogout")
public CommonResult<String> doLogout() {
StpUtil.logout();
return CommonResult.ok();
}
/**
* B端获取用户信息
*
*
* @date 2021/10/15 13:12
**/
@ApiOperationSupport(order = 6)
@ApiOperation("B端获取用户信息")
@SaCheckLogin
@GetMapping("/auth/b/getLoginUser")
public CommonResult<SaBaseLoginUser> getLoginUser() {
return CommonResult.data(authService.getLoginUser());
}
}

View File

@@ -0,0 +1,43 @@
package mjkf.xinke.auth.modular.login.enums;
import lombok.Getter;
import mjkf.xinke.common.exception.CommonException;
/**
* 登录设备类型枚举
*
*
* @date 2021/10/11 14:02
**/
@Getter
public enum AuthDeviceTypeEnum {
/**
* PC端
*/
PC("PC"),
/**
* 移动端
*/
APP("APP"),
/**
* 小程序端
*/
MINI("MINI");
private final String value;
AuthDeviceTypeEnum(String value) {
this.value = value;
}
public static void validate(String value) {
boolean flag = PC.getValue().equals(value) || APP.getValue().equals(value) || MINI.getValue().equals(value);
if(!flag) {
throw new CommonException("不支持的登录设备类型:{}", value);
}
}
}

View File

@@ -0,0 +1,75 @@
package mjkf.xinke.auth.modular.login.enums;
import lombok.Getter;
/**
* 登录异常提示语枚举
*
*
* @date 2021/10/11 14:02
**/
@Getter
public enum AuthExceptionEnum {
/**
* 验证码不能为空
*/
VALID_CODE_EMPTY("验证码不能为空"),
/**
* 验证码请求号不能为空
*/
VALID_CODE_REQ_NO_EMPTY("验证码请求号不能为空"),
/**
* 验证码错误
*/
VALID_CODE_ERROR("验证码错误"),
/**
* 账号错误
*/
ACCOUNT_ERROR("账号错误"),
/**
* 账号已停用
*/
ACCOUNT_DISABLED("账号已停用"),
/**
* 密码错误
*/
PWD_ERROR("密码错误"),
/**
* 手机号格式错误
*/
PHONE_FORMAT_ERROR("手机号格式错误"),
/**
* 手机号不存在
*/
PHONE_ERROR("手机号不存在"),
/**
* 客户端类型不能为空
*/
CLIENT_TYPE_EMPTY("客户端类型不能为空"),
/**
* 客户端类型错误
*/
CLIENT_TYPE_ERROR("客户端类型错误"),
/**
* 密码解密失败,请检查前端公钥
*/
PWD_DECRYPT_ERROR("密码解密失败,请检查前端公钥");
private final String value;
AuthExceptionEnum(String value) {
this.value = value;
}
}

View File

@@ -0,0 +1,107 @@
package mjkf.xinke.auth.modular.login.listener;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import org.springframework.stereotype.Component;
import mjkf.xinke.auth.api.SaBaseLoginUserApi;
import mjkf.xinke.auth.core.enums.SaClientTypeEnum;
import mjkf.xinke.auth.core.pojo.SaBaseLoginUser;
import mjkf.xinke.dev.api.DevLogApi;
import javax.annotation.Resource;
/**
* 自定义登录监听器
*
*
* @date 2021/12/28 11:35
**/
@Component
public class AuthListener implements SaTokenListener {
@Resource(name = "loginUserApi")
private SaBaseLoginUserApi loginUserApi;
@Resource(name = "clientLoginUserApi")
private SaBaseLoginUserApi clientLoginUserApi;
@Resource
private DevLogApi devLogApi;
/** 每次登录时触发 */
@Override
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
// 更新用户的登录时间和登录ip等信息
if(SaClientTypeEnum.B.getValue().equals(loginType)) {
loginUserApi.updateUserLoginInfo(Convert.toStr(loginId), loginModel.getDevice());
// 记录B端登录日志
Object name = loginModel.getExtra("name");
if(ObjectUtil.isNotEmpty(name)) {
devLogApi.executeLoginLog(Convert.toStr(name));
} else {
devLogApi.executeLoginLog(null);
}
} else {
clientLoginUserApi.updateUserLoginInfo(Convert.toStr(loginId), loginModel.getDevice());
}
}
/** 每次注销时触发 */
@Override
public void doLogout(String loginType, Object loginId, String tokenValue) {
if(SaClientTypeEnum.B.getValue().equals(loginType)) {
// 记录B端登出日志
SaBaseLoginUser saBaseLoginUser = loginUserApi.getUserById(Convert.toStr(loginId));
if(ObjectUtil.isNotEmpty(saBaseLoginUser)) {
devLogApi.executeLogoutLog(saBaseLoginUser.getName());
} else {
devLogApi.executeLogoutLog(null);
}
}
}
/** 每次被踢下线时触发 */
@Override
public void doKickout(String loginType, Object loginId, String tokenValue) {
// ...
}
/** 每次被顶下线时触发 */
@Override
public void doReplaced(String loginType, Object loginId, String tokenValue) {
// ...
}
/** 每次被封禁时触发 */
@Override
public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
// ...
}
/** 每次被解封时触发 */
@Override
public void doUntieDisable(String loginType, Object loginId, String service) {
// ...
}
/** 每次创建Session时触发 */
@Override
public void doCreateSession(String id) {
// ...
}
/** 每次注销Session时触发 */
@Override
public void doLogoutSession(String id) {
// ...
}
/** 每次Token续期时触发 */
@Override
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
// ...
}
}

View File

@@ -0,0 +1,41 @@
package mjkf.xinke.auth.modular.login.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* 账号密码登录参数
*
*
* @date 2022/7/7 16:46
**/
@Getter
@Setter
public class AuthAccountPasswordLoginParam {
/** 账号 */
@ApiModelProperty(value = "账号", required = true, position = 1)
@NotBlank(message = "账号不能为空")
private String account;
/** 密码 */
@ApiModelProperty(value = "密码", required = true, position = 2)
@NotBlank(message = "密码不能为空")
private String password;
/** 设备 */
@ApiModelProperty(value = "设备", position = 3)
private String device;
/** 验证码 */
@ApiModelProperty(value = "验证码", position = 4)
private String validCode;
/** 验证码请求号 */
@ApiModelProperty(value = "验证码请求号", position = 5)
private String validCodeReqNo;
}

View File

@@ -0,0 +1,34 @@
package mjkf.xinke.auth.modular.login.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* 获取手机验证码参数
*
*
* @date 2022/8/25 13:45
**/
@Getter
@Setter
public class AuthGetPhoneValidCodeParam {
/** 手机号 */
@ApiModelProperty(value = "手机号", required = true, position = 1)
@NotBlank(message = "手机号不能为空")
private String phone;
/** 验证码 */
@ApiModelProperty(value = "验证码", required = true, position = 2)
@NotBlank(message = "验证码不能为空")
private String validCode;
/** 验证码请求号 */
@ApiModelProperty(value = "验证码请求号", required = true, position = 3)
@NotBlank(message = "验证码请求号不能为空")
private String validCodeReqNo;
}

View File

@@ -0,0 +1,38 @@
package mjkf.xinke.auth.modular.login.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* 手机验证码登录参数
*
*
* @date 2022/7/7 16:46
**/
@Getter
@Setter
public class AuthPhoneValidCodeLoginParam {
/** 手机号 */
@ApiModelProperty(value = "手机号", required = true, position = 1)
@NotBlank(message = "手机号不能为空")
private String phone;
/** 验证码 */
@ApiModelProperty(value = "验证码", required = true, position = 2)
@NotBlank(message = "验证码不能为空")
private String validCode;
/** 验证码请求号 */
@ApiModelProperty(value = "验证码请求号", required = true, position = 3)
@NotBlank(message = "验证码请求号不能为空")
private String validCodeReqNo;
/** 设备 */
@ApiModelProperty(value = "设备", position = 4)
private String device;
}

View File

@@ -0,0 +1,25 @@
package mjkf.xinke.auth.modular.login.result;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 图片验证码结果
*
*
* @date 2022/7/8 9:28
**/
@Getter
@Setter
public class AuthPicValidCodeResult {
/** 验证码图片Base64 */
@ApiModelProperty(value = "验证码图片Base64", position = 1)
private String validCodeBase64;
/** 验证码请求号 */
@ApiModelProperty(value = "验证码请求号", position = 2)
private String validCodeReqNo;
}

View File

@@ -0,0 +1,74 @@
package mjkf.xinke.auth.modular.login.service;
import mjkf.xinke.auth.core.pojo.SaBaseClientLoginUser;
import mjkf.xinke.auth.core.pojo.SaBaseLoginUser;
import mjkf.xinke.auth.modular.login.param.AuthAccountPasswordLoginParam;
import mjkf.xinke.auth.modular.login.param.AuthGetPhoneValidCodeParam;
import mjkf.xinke.auth.modular.login.param.AuthPhoneValidCodeLoginParam;
import mjkf.xinke.auth.modular.login.result.AuthPicValidCodeResult;
/**
* 登录Service接口
*
*
* @date 2021/12/23 21:51
*/
public interface AuthService {
/**
* 获取图片验证码
*
*
* @date 2021/12/28 14:46
**/
AuthPicValidCodeResult getPicCaptcha(String type);
/**
* 获取手机验证码
*
*
* @date 2021/12/28 14:46
**/
String getPhoneValidCode(AuthGetPhoneValidCodeParam authGetPhoneValidCodeParam, String type);
/**
* 账号密码登录
*
*
* @date 2021/12/28 14:46
**/
String doLogin(AuthAccountPasswordLoginParam authAccountPasswordLoginParam, String type);
/**
* 手机验证码登录
*
*
* @date 2021/12/28 14:46
**/
String doLoginByPhone(AuthPhoneValidCodeLoginParam authPhoneValidCodeLoginParam, String type);
/**
* 获取B端登录用户信息
*
*
* @date 2021/10/12 15:59
**/
SaBaseLoginUser getLoginUser();
/**
* 获取C端登录用户信息
*
*
* @date 2021/10/12 15:59
**/
SaBaseClientLoginUser getClientLoginUser();
/**
* 根据用户id和客户端类型登录用于第三方登录
*
*
* @date 2022/7/9 14:44
*/
String doLoginById(String userId, String device, String type);
}

View File

@@ -0,0 +1,406 @@
package mjkf.xinke.auth.modular.login.service.impl;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.springframework.stereotype.Service;
import mjkf.xinke.auth.api.SaBaseLoginUserApi;
import mjkf.xinke.auth.core.enums.SaClientTypeEnum;
import mjkf.xinke.auth.core.pojo.SaBaseClientLoginUser;
import mjkf.xinke.auth.core.pojo.SaBaseLoginUser;
import mjkf.xinke.auth.core.util.StpClientLoginUserUtil;
import mjkf.xinke.auth.core.util.StpClientUtil;
import mjkf.xinke.auth.core.util.StpLoginUserUtil;
import mjkf.xinke.auth.modular.login.enums.AuthDeviceTypeEnum;
import mjkf.xinke.auth.modular.login.enums.AuthExceptionEnum;
import mjkf.xinke.auth.modular.login.param.AuthAccountPasswordLoginParam;
import mjkf.xinke.auth.modular.login.param.AuthGetPhoneValidCodeParam;
import mjkf.xinke.auth.modular.login.param.AuthPhoneValidCodeLoginParam;
import mjkf.xinke.auth.modular.login.result.AuthPicValidCodeResult;
import mjkf.xinke.auth.modular.login.service.AuthService;
import mjkf.xinke.common.cache.CommonCacheOperator;
import mjkf.xinke.common.exception.CommonException;
import mjkf.xinke.common.util.CommonCryptogramUtil;
import mjkf.xinke.common.util.CommonEmailUtil;
import mjkf.xinke.dev.api.DevConfigApi;
import mjkf.xinke.dev.api.DevSmsApi;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;
/**
* 登录Service接口实现类
*
*
* @date 2021/12/23 21:52
*/
@Service
public class AuthServiceImpl implements AuthService {
private static final String SNOWY_SYS_DEFAULT_CAPTCHA_OPEN_KEY = "SNOWY_SYS_DEFAULT_CAPTCHA_OPEN";
private static final String AUTH_VALID_CODE_CACHE_KEY = "auth-validCode:";
@Resource(name = "loginUserApi")
private SaBaseLoginUserApi loginUserApi;
@Resource(name = "clientLoginUserApi")
private SaBaseLoginUserApi clientLoginUserApi;
@Resource
private DevConfigApi devConfigApi;
@Resource
private DevSmsApi devSmsApi;
@Resource
private CommonCacheOperator commonCacheOperator;
@Override
public AuthPicValidCodeResult getPicCaptcha(String type) {
// 生成验证码随机4位字符
CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(100, 38, 4, 10);
// 定义返回结果
AuthPicValidCodeResult authPicValidCodeResult = new AuthPicValidCodeResult();
// 获取验证码的值
String validCode = circleCaptcha.getCode();
// 获取验证码的base64
String validCodeBase64 = circleCaptcha.getImageBase64Data();
// 生成请求号
String validCodeReqNo = IdWorker.getIdStr();
// 将base64返回前端
authPicValidCodeResult.setValidCodeBase64(validCodeBase64);
// 将请求号返回前端
authPicValidCodeResult.setValidCodeReqNo(validCodeReqNo);
// 将请求号作为key验证码的值作为value放到redis用于校验5分钟有效
commonCacheOperator.put(AUTH_VALID_CODE_CACHE_KEY + validCodeReqNo, validCode, 5 * 60);
return authPicValidCodeResult;
}
@Override
public String getPhoneValidCode(AuthGetPhoneValidCodeParam authGetPhoneValidCodeParam, String type) {
// 手机号
String phone = authGetPhoneValidCodeParam.getPhone();
// 验证码
String validCode = authGetPhoneValidCodeParam.getValidCode();
// 验证码请求号
String validCodeReqNo = authGetPhoneValidCodeParam.getValidCodeReqNo();
// 校验参数
validPhoneValidCodeParam(null, validCode, validCodeReqNo, type);
// 生成手机验证码的值随机6为数字
String phoneValidCode = RandomUtil.randomNumbers(6);
// 生成手机验证码的请求号
String phoneValidCodeReqNo = IdWorker.getIdStr();
// TODO 使用阿里云执行发送验证码,将验证码作为短信内容的参数变量放入,
// TODO 签名不传则使用系统默认配置的签名,支持传入多个参数,示例:{"name":"张三","number":"15038****76"}
//devSmsApi.sendSmsAliyun(phone, null, "验证码模板号", JSONUtil.toJsonStr(JSONUtil.createObj().set("validCode", phoneValidCode)));
// TODO 使用腾讯云执行发送验证码,将验证码作为短信内容的参数变量放入,
// TODO sdkAppId和签名不传则使用系统默认配置的sdkAppId和签名支持传入多个参数逗号拼接示例"张三,15038****76,进行中"
devSmsApi.sendSmsTencent("sdkAppId", phone, "签名", "模板编码", phoneValidCode);
// 将请求号作为key验证码的值作为value放到redis用于校验5分钟有效
commonCacheOperator.put(AUTH_VALID_CODE_CACHE_KEY + phone + StrUtil.UNDERLINE + phoneValidCodeReqNo, phoneValidCode, 5 * 60);
// 返回请求号
return phoneValidCodeReqNo;
}
/**
* 校验验证码方法
*
*
* @date 2022/8/25 15:26
**/
private void validValidCode(String phoneOrEmail, String validCode, String validCodeReqNo) {
// 依据请求号,取出缓存中的验证码进行校验
Object existValidCode;
if(ObjectUtil.isEmpty(phoneOrEmail)) {
existValidCode = commonCacheOperator.get(AUTH_VALID_CODE_CACHE_KEY + validCodeReqNo);
} else {
existValidCode = commonCacheOperator.get(AUTH_VALID_CODE_CACHE_KEY + phoneOrEmail + StrUtil.UNDERLINE + validCodeReqNo);
}
// 为空则直接验证码错误
if(ObjectUtil.isEmpty(existValidCode)) {
throw new CommonException(AuthExceptionEnum.VALID_CODE_ERROR.getValue());
}
// 移除该验证码
if(ObjectUtil.isEmpty(phoneOrEmail)) {
commonCacheOperator.remove(AUTH_VALID_CODE_CACHE_KEY + validCodeReqNo);
} else {
commonCacheOperator.remove(AUTH_VALID_CODE_CACHE_KEY + phoneOrEmail + StrUtil.UNDERLINE + validCodeReqNo);
}
// 不一致则直接验证码错误
if (!validCode.equals(Convert.toStr(existValidCode).toLowerCase())) {
throw new CommonException("验证码错误");
}
}
/**
* 校验手机号与验证码等参数
*
*
* @date 2022/8/25 14:29
**/
private void validPhoneValidCodeParam(String phoneOrEmail, String validCode, String validCodeReqNo, String type) {
// 验证码正确则校验手机号格式
if(ObjectUtil.isEmpty(phoneOrEmail)) {
// 执行校验验证码
validValidCode(null, validCode, validCodeReqNo);
} else {
if(!PhoneUtil.isMobile(phoneOrEmail) && !CommonEmailUtil.isEmail(phoneOrEmail)) {
throw new CommonException(AuthExceptionEnum.PHONE_FORMAT_ERROR.getValue());
}
// 执行校验验证码
validValidCode(phoneOrEmail, validCode, validCodeReqNo);
// 根据手机号获取用户信息判断用户是否存在根据B端或C端判断
if(SaClientTypeEnum.B.getValue().equals(type)) {
if(ObjectUtil.isEmpty(loginUserApi.getUserByPhone(phoneOrEmail))) {
throw new CommonException(AuthExceptionEnum.PHONE_ERROR.getValue());
}
} else {
if(ObjectUtil.isEmpty(clientLoginUserApi.getClientUserByPhone(phoneOrEmail))) {
throw new CommonException(AuthExceptionEnum.PHONE_ERROR.getValue());
}
}
}
}
@Override
public String doLogin(AuthAccountPasswordLoginParam authAccountPasswordLoginParam, String type) {
// 获取账号
String account = authAccountPasswordLoginParam.getAccount();
// 获取密码
String password = authAccountPasswordLoginParam.getPassword();
// 获取设备
String device = authAccountPasswordLoginParam.getDevice();
// 默认指定为PC如在小程序跟移动端的情况下自行指定即可
if(ObjectUtil.isEmpty(device)) {
device = AuthDeviceTypeEnum.PC.getValue();
} else {
AuthDeviceTypeEnum.validate(device);
}
// 校验验证码
String defaultCaptchaOpen = devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_CAPTCHA_OPEN_KEY);
if(ObjectUtil.isNotEmpty(defaultCaptchaOpen)) {
if(Convert.toBool(defaultCaptchaOpen)) {
// 获取验证码
String validCode = authAccountPasswordLoginParam.getValidCode();
// 获取验证码请求号
String validCodeReqNo = authAccountPasswordLoginParam.getValidCodeReqNo();
// 开启验证码则必须传入验证码
if(ObjectUtil.isEmpty(validCode)) {
throw new CommonException(AuthExceptionEnum.VALID_CODE_EMPTY.getValue());
}
// 开启验证码则必须传入验证码请求号
if(ObjectUtil.isEmpty(validCodeReqNo)) {
throw new CommonException(AuthExceptionEnum.VALID_CODE_REQ_NO_EMPTY.getValue());
}
// 执行校验验证码
validValidCode(null, validCode, validCodeReqNo);
}
}
// SM2解密并获得前端传来的密码哈希值
String passwordHash;
try {
// 解密,并做哈希值
passwordHash = CommonCryptogramUtil.doHashValue(CommonCryptogramUtil.doSm2Decrypt(password));
} catch (Exception e) {
throw new CommonException(AuthExceptionEnum.PWD_DECRYPT_ERROR.getValue());
}
// 根据账号获取用户信息根据B端或C端判断
if(SaClientTypeEnum.B.getValue().equals(type)) {
SaBaseLoginUser saBaseLoginUser = loginUserApi.getUserByAccount(account);
if(ObjectUtil.isEmpty(saBaseLoginUser)) {
throw new CommonException(AuthExceptionEnum.ACCOUNT_ERROR.getValue());
}
if (!saBaseLoginUser.getPassword().equals(passwordHash)) {
throw new CommonException(AuthExceptionEnum.PWD_ERROR.getValue());
}
// 执行B端登录
return execLoginB(saBaseLoginUser, device);
} else {
SaBaseClientLoginUser saBaseClientLoginUser = clientLoginUserApi.getClientUserByAccount(account);
if(ObjectUtil.isEmpty(saBaseClientLoginUser)) {
throw new CommonException(AuthExceptionEnum.ACCOUNT_ERROR.getValue());
}
if (!saBaseClientLoginUser.getPassword().equals(passwordHash)) {
throw new CommonException(AuthExceptionEnum.PWD_ERROR.getValue());
}
// 执行C端登录
return execLoginC(saBaseClientLoginUser, device);
}
}
@Override
public String doLoginByPhone(AuthPhoneValidCodeLoginParam authPhoneValidCodeLoginParam, String type) {
// 手机号
String phone = authPhoneValidCodeLoginParam.getPhone();
// 校验参数
validPhoneValidCodeParam(phone, authPhoneValidCodeLoginParam.getValidCode(), authPhoneValidCodeLoginParam.getValidCodeReqNo(), type);
// 设备
String device = authPhoneValidCodeLoginParam.getDevice();
// 默认指定为PC如在小程序跟移动端的情况下自行指定即可
if(ObjectUtil.isEmpty(device)) {
device = AuthDeviceTypeEnum.PC.getValue();
} else {
AuthDeviceTypeEnum.validate(device);
}
// 根据手机号获取用户信息根据B端或C端判断
if(SaClientTypeEnum.B.getValue().equals(type)) {
SaBaseLoginUser saBaseLoginUser = loginUserApi.getUserByPhone(phone);
if(ObjectUtil.isEmpty(saBaseLoginUser)) {
throw new CommonException(AuthExceptionEnum.ACCOUNT_ERROR.getValue());
}
// 执行B端登录
return execLoginB(saBaseLoginUser, device);
} else {
SaBaseClientLoginUser saBaseClientLoginUser = clientLoginUserApi.getClientUserByPhone(phone);
if(ObjectUtil.isEmpty(saBaseClientLoginUser)) {
throw new CommonException(AuthExceptionEnum.ACCOUNT_ERROR.getValue());
}
// 执行C端登录
return execLoginC(saBaseClientLoginUser, device);
}
}
/**
* 执行B端登录
*
*
* @date 2022/8/25 14:36
**/
private String execLoginB(SaBaseLoginUser saBaseLoginUser, String device) {
// 校验状态
if(!saBaseLoginUser.getEnabled()) {
throw new CommonException(AuthExceptionEnum.ACCOUNT_DISABLED.getValue());
}
// 执行登录
StpUtil.login(saBaseLoginUser.getId(), new SaLoginModel().setDevice(device).setExtra("name", saBaseLoginUser.getName()));
// 角色集合
List<JSONObject> roleList = loginUserApi.getRoleListByUserId(saBaseLoginUser.getId());
// 角色id集合
List<String> roleIdList = roleList.stream().map(jsonObject -> jsonObject.getStr("id")).collect(Collectors.toList());
// 角色码集合
List<String> roleCodeList = roleList.stream().map(jsonObject -> jsonObject.getStr("code")).collect(Collectors.toList());
// 角色id和用户id集合
List<String> userAndRoleIdList = CollectionUtil.unionAll(roleIdList, CollectionUtil.newArrayList(saBaseLoginUser.getId()));
// 获取按钮码
saBaseLoginUser.setButtonCodeList(loginUserApi.getButtonCodeListListByUserAndRoleIdList(userAndRoleIdList));
// 获取移动端按钮码
saBaseLoginUser.setMobileButtonCodeList(loginUserApi.getMobileButtonCodeListListByUserIdAndRoleIdList(userAndRoleIdList));
// 获取数据范围
saBaseLoginUser.setDataScopeList(Convert.toList(SaBaseLoginUser.DataScope.class,
loginUserApi.getPermissionListByUserIdAndRoleIdList(userAndRoleIdList, saBaseLoginUser.getOrgId())));
// 获取权限码
saBaseLoginUser.setPermissionCodeList(saBaseLoginUser.getDataScopeList().stream()
.map(SaBaseLoginUser.DataScope::getApiUrl).collect(Collectors.toList()));
// 获取角色码
saBaseLoginUser.setRoleCodeList(roleCodeList);
// 缓存用户信息此处使用TokenSession为了指定时间内无操作则自动下线
StpUtil.getTokenSession().set("loginUser", saBaseLoginUser);
// 返回token
return StpUtil.getTokenInfo().tokenValue;
}
/**
* 执行C端登录
*
*
* @date 2022/8/25 14:37
**/
private String execLoginC(SaBaseClientLoginUser saBaseClientLoginUser, String device) {
// 校验状态
if(!saBaseClientLoginUser.getEnabled()) {
throw new CommonException(AuthExceptionEnum.ACCOUNT_DISABLED.getValue());
}
// 执行登录
StpClientUtil.login(saBaseClientLoginUser.getId(), new SaLoginModel().setDevice(device).setExtra("name", saBaseClientLoginUser.getName()));
// 角色集合
List<JSONObject> roleList = loginUserApi.getRoleListByUserId(saBaseClientLoginUser.getId());
// 角色id集合
List<String> roleIdList = roleList.stream().map(jsonObject -> jsonObject.getStr("id")).collect(Collectors.toList());
// 角色码集合
List<String> roleCodeList = roleList.stream().map(jsonObject -> jsonObject.getStr("code")).collect(Collectors.toList());
// 角色id和用户id集合
List<String> userAndRoleIdList = CollectionUtil.unionAll(roleIdList, CollectionUtil.newArrayList(saBaseClientLoginUser.getId()));
// 获取按钮码
saBaseClientLoginUser.setButtonCodeList(clientLoginUserApi.getButtonCodeListListByUserAndRoleIdList(userAndRoleIdList));
// 获取移动端按钮码
saBaseClientLoginUser.setMobileButtonCodeList(clientLoginUserApi.getMobileButtonCodeListListByUserIdAndRoleIdList(userAndRoleIdList));
// 获取数据范围
saBaseClientLoginUser.setDataScopeList(Convert.toList(SaBaseClientLoginUser.DataScope.class,
clientLoginUserApi.getPermissionListByUserIdAndRoleIdList(userAndRoleIdList, null)));
// 获取权限码
saBaseClientLoginUser.setPermissionCodeList(saBaseClientLoginUser.getDataScopeList().stream()
.map(SaBaseClientLoginUser.DataScope::getApiUrl).collect(Collectors.toList()));
// 获取角色码
saBaseClientLoginUser.setRoleCodeList(roleCodeList);
// 缓存用户信息此处使用TokenSession为了指定时间内无操作则自动下线
StpClientUtil.getTokenSession().set("loginUser", saBaseClientLoginUser);
// 返回token
return StpClientUtil.getTokenInfo().tokenValue;
}
/**
* 获取B端登录用户信息
*
*
* @date 2021/10/12 15:59
**/
@Override
public SaBaseLoginUser getLoginUser() {
SaBaseLoginUser saBaseLoginUser = StpLoginUserUtil.getLoginUser();
saBaseLoginUser.setPassword(null);
saBaseLoginUser.setPermissionCodeList(null);
saBaseLoginUser.setDataScopeList(null);
return saBaseLoginUser;
}
/**
* 获取C端登录用户信息
*
*
* @date 2021/10/12 15:59
**/
@Override
public SaBaseClientLoginUser getClientLoginUser() {
SaBaseClientLoginUser saBaseClientLoginUser = StpClientLoginUserUtil.getClientLoginUser();
saBaseClientLoginUser.setPassword(null);
saBaseClientLoginUser.setPermissionCodeList(null);
saBaseClientLoginUser.setDataScopeList(null);
return saBaseClientLoginUser;
}
@Override
public String doLoginById(String userId, String device, String type) {
// 根据id获取用户信息根据B端或C端判断
if(SaClientTypeEnum.B.getValue().equals(type)) {
SaBaseLoginUser saBaseLoginUser = loginUserApi.getUserById(userId);
if (ObjectUtil.isEmpty(saBaseLoginUser)) {
throw new CommonException(AuthExceptionEnum.ACCOUNT_ERROR.getValue());
}
// 执行B端登录
return execLoginB(saBaseLoginUser, device);
} else {
SaBaseClientLoginUser saBaseClientLoginUser = clientLoginUserApi.getClientUserById(userId);
if (ObjectUtil.isEmpty(saBaseClientLoginUser)) {
throw new CommonException(AuthExceptionEnum.ACCOUNT_ERROR.getValue());
}
// 执行C端登录
return execLoginC(saBaseClientLoginUser, device);
}
}
}

View File

@@ -0,0 +1,145 @@
package mjkf.xinke.auth.modular.monitor.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import mjkf.xinke.auth.modular.monitor.param.AuthExitSessionParam;
import mjkf.xinke.auth.modular.monitor.param.AuthExitTokenParam;
import mjkf.xinke.auth.modular.monitor.param.AuthSessionPageParam;
import mjkf.xinke.auth.modular.monitor.result.AuthSessionAnalysisResult;
import mjkf.xinke.auth.modular.monitor.result.AuthSessionPageResult;
import mjkf.xinke.auth.modular.monitor.service.AuthSessionService;
import mjkf.xinke.common.annotation.CommonLog;
import mjkf.xinke.common.pojo.CommonResult;
import mjkf.xinke.common.pojo.CommonValidList;
import javax.annotation.Resource;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
/**
* 会话治理控制器
*
*
* @date 2022/6/24 15:20
**/
@Api(tags = "会话治理控制器")
@ApiSupport(author = "SNOWY_TEAM", order = 3)
@RestController
@Validated
public class AuthSessionController {
@Resource
private AuthSessionService authSessionService;
/**
* 会话统计
*
*
* @date 2022/6/24 22:28
*/
@ApiOperationSupport(order = 1)
@ApiOperation("会话统计")
@GetMapping("/auth/session/analysis")
public CommonResult<AuthSessionAnalysisResult> analysis() {
return CommonResult.data(authSessionService.analysis());
}
/**
* 查询B端会话
*
*
* @date 2022/6/24 22:28
*/
@ApiOperationSupport(order = 2)
@ApiOperation("查询B端会话")
@GetMapping("/auth/session/b/page")
public CommonResult<Page<AuthSessionPageResult>> pageForB(AuthSessionPageParam authSessionPageParam) {
return CommonResult.data(authSessionService.pageForB(authSessionPageParam));
}
/**
* 查询C端会话
*
*
* @date 2022/6/24 22:28
*/
@ApiOperationSupport(order = 3)
@ApiOperation("查询C端会话")
@GetMapping("/auth/session/c/page")
public CommonResult<Page<AuthSessionPageResult>> pageForC(AuthSessionPageParam authSessionPageParam) {
return CommonResult.data(authSessionService.pageForC(authSessionPageParam));
}
/**
* 强退B端会话
*
*
* @date 2021/10/12 10:25
**/
@ApiOperationSupport(order = 4)
@ApiOperation("强退B端会话")
@CommonLog("强退B端会话")
@PostMapping("/auth/session/b/exit")
public CommonResult<String> exitSessionForB(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
CommonValidList<AuthExitSessionParam> authExitSessionParamList) {
authSessionService.exitSessionForB(authExitSessionParamList);
return CommonResult.ok();
}
/**
* 强退C端会话
*
*
* @date 2021/10/12 10:25
**/
@ApiOperationSupport(order = 5)
@ApiOperation("强退C端会话")
@CommonLog("强退C端会话")
@PostMapping("/auth/session/c/exit")
public CommonResult<String> exitSessionForC(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
CommonValidList<AuthExitSessionParam> authExitSessionParamList) {
authSessionService.exitSessionForC(authExitSessionParamList);
return CommonResult.ok();
}
/**
* 强退B端token
*
*
* @date 2021/10/12 10:25
**/
@ApiOperationSupport(order = 6)
@ApiOperation("强退B端token")
@CommonLog("强退B端token")
@PostMapping("/auth/token/b/exit")
public CommonResult<String> exitTokenForB(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
CommonValidList<AuthExitTokenParam> authExitTokenParamList) {
authSessionService.exitTokenForB(authExitTokenParamList);
return CommonResult.ok();
}
/**
* 强退C端token
*
*
* @date 2021/10/12 10:25
**/
@ApiOperationSupport(order = 7)
@ApiOperation("强退C端token")
@CommonLog("强退C端token")
@PostMapping("/auth/token/c/exit")
public CommonResult<String> exitTokenForC(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
CommonValidList<AuthExitTokenParam> authExitTokenParamList) {
authSessionService.exitTokenForC(authExitTokenParamList);
return CommonResult.ok();
}
}

View File

@@ -0,0 +1,24 @@
package mjkf.xinke.auth.modular.monitor.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* Session退出参数
*
*
* @date 2022/6/24 22:28
*/
@Getter
@Setter
public class AuthExitSessionParam {
/** 用户id */
@ApiModelProperty(value = "用户id", required = true)
@NotBlank(message = "userId不能为空")
private String userId;
}

View File

@@ -0,0 +1,24 @@
package mjkf.xinke.auth.modular.monitor.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* Token退出参数
*
*
* @date 2022/6/24 22:28
*/
@Getter
@Setter
public class AuthExitTokenParam {
/** token值 */
@ApiModelProperty(value = "token值", required = true)
@NotBlank(message = "tokenValue不能为空")
private String tokenValue;
}

View File

@@ -0,0 +1,21 @@
package mjkf.xinke.auth.modular.monitor.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 会话查询参数
*
*
* @date 2022/7/28 14:48
**/
@Getter
@Setter
public class AuthSessionPageParam {
/** 用户id */
@ApiModelProperty(value = "用户id")
private String userId;
}

View File

@@ -0,0 +1,33 @@
package mjkf.xinke.auth.modular.monitor.result;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 会话统计结果
*
*
* @date 2022/7/19 9:29
**/
@Getter
@Setter
public class AuthSessionAnalysisResult {
/** 当前会话总数量 */
@ApiModelProperty(value = "当前会话总数量", position = 1)
private String currentSessionTotalCount;
/** 最大签发令牌数 */
@ApiModelProperty(value = "最大签发令牌数", position = 2)
private String maxTokenCount;
/** 最近1小时会话数 */
@ApiModelProperty(value = "最近1小时会话数", position = 3)
private String oneHourNewlyAdded;
/** BC端会话比例 */
@ApiModelProperty(value = "BC端会话比例", position = 4)
private String proportionOfBAndC;
}

View File

@@ -0,0 +1,115 @@
package mjkf.xinke.auth.modular.monitor.result;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.List;
/**
* B端会话结果集
*
*
* @date 2022/7/28 14:46
**/
@Getter
@Setter
public class AuthSessionPageResult {
/** id */
@ApiModelProperty(value = "id", position = 1)
private String id;
/** 头像 */
@ApiModelProperty(value = "头像", position = 2)
private String avatar;
/** 账号 */
@ApiModelProperty(value = "账号", position = 3)
private String account;
/** 姓名 */
@ApiModelProperty(value = "姓名", position = 4)
private String name;
/** 上次登录ip */
@ApiModelProperty(value = "上次登录ip", position = 5)
private String lastLoginIp;
/** 上次登录地点 */
@ApiModelProperty(value = "上次登录地点", position = 6)
private String lastLoginAddress;
/** 上次登录时间 */
@ApiModelProperty(value = "上次登录时间", position = 7)
private Date lastLoginTime;
/** 上次登录设备 */
@ApiModelProperty(value = "上次登录设备", position = 8)
private String lastLoginDevice;
/** 最新登录ip */
@ApiModelProperty(value = "最新登录ip", position = 9)
private String latestLoginIp;
/** 最新登录地点 */
@ApiModelProperty(value = "最新登录地点", position = 10)
private String latestLoginAddress;
/** 最新登录时间 */
@ApiModelProperty(value = "最新登录时间", position = 11)
private Date latestLoginTime;
/** 最新登录设备 */
@ApiModelProperty(value = "最新登录设备", position = 12)
private String latestLoginDevice;
/** 会话id */
@ApiModelProperty(value = "会话id", position = 13)
private String sessionId;
/** 会话创建时间 */
@ApiModelProperty(value = "会话创建时间", position = 14)
private Date sessionCreateTime;
/** 会话剩余有效期 */
@ApiModelProperty(value = "会话剩余有效期", position = 15)
private String sessionTimeout;
/** 令牌数量 */
@ApiModelProperty(value = "令牌数量", position = 16)
private Integer tokenCount;
/** 令牌信息集合 */
@ApiModelProperty(value = "令牌信息集合", position = 17)
private List<TokenSignInfo> tokenSignList;
/**
* 令牌信息类
*
*
* @date 2022/7/28 15:04
**/
@Getter
@Setter
public static class TokenSignInfo {
/** token值 */
@ApiModelProperty(value = "token值", position = 1)
private String tokenValue;
/** 登录设备 */
@ApiModelProperty(value = "登录设备", position = 2)
private String tokenDevice;
/** token剩余有效期 */
@ApiModelProperty(value = "token剩余有效期", position = 3)
private String tokenTimeout;
/** token剩余有效期百分比 */
@ApiModelProperty(value = "token剩余有效期百分比", position = 4)
private Double tokenTimeoutPercent;
}
}

View File

@@ -0,0 +1,76 @@
package mjkf.xinke.auth.modular.monitor.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import mjkf.xinke.auth.modular.monitor.param.AuthExitSessionParam;
import mjkf.xinke.auth.modular.monitor.param.AuthExitTokenParam;
import mjkf.xinke.auth.modular.monitor.param.AuthSessionPageParam;
import mjkf.xinke.auth.modular.monitor.result.AuthSessionAnalysisResult;
import mjkf.xinke.auth.modular.monitor.result.AuthSessionPageResult;
import java.util.List;
/**
* 会话治理Service接口
*
*
* @date 2022/6/24 15:46S
**/
public interface AuthSessionService {
/**
* 会话统计
*
*
* @date 2022/7/19 9:33
**/
AuthSessionAnalysisResult analysis();
/**
* 查询B端会话
*
*
* @date 2022/6/24 22:30
*/
Page<AuthSessionPageResult> pageForB(AuthSessionPageParam authSessionPageParam);
/**
* 查询C端会话
*
*
* @date 2022/6/24 22:30
*/
Page<AuthSessionPageResult> pageForC(AuthSessionPageParam authSessionPageParam);
/**
* 强退B端会话
*
*
* @date 2022/6/29 21:47
*/
void exitSessionForB(List<AuthExitSessionParam> authExitSessionParamList);
/**
* 强退C端会话
*
*
* @date 2022/6/29 21:47
*/
void exitSessionForC(List<AuthExitSessionParam> authExitSessionParamList);
/**
* 强退B端token
*
*
* @date 2022/6/29 21:47
*/
void exitTokenForB(List<AuthExitTokenParam> authExitTokenParamList);
/**
* 强退C端token
*
*
* @date 2022/6/29 21:47
*/
void exitTokenForC(List<AuthExitTokenParam> authExitTokenParamList);
}

View File

@@ -0,0 +1,227 @@
package mjkf.xinke.auth.modular.monitor.service.impl;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Service;
import mjkf.xinke.auth.api.SaBaseLoginUserApi;
import mjkf.xinke.auth.core.util.StpClientUtil;
import mjkf.xinke.auth.modular.monitor.param.AuthExitSessionParam;
import mjkf.xinke.auth.modular.monitor.param.AuthExitTokenParam;
import mjkf.xinke.auth.modular.monitor.param.AuthSessionPageParam;
import mjkf.xinke.auth.modular.monitor.result.AuthSessionAnalysisResult;
import mjkf.xinke.auth.modular.monitor.result.AuthSessionPageResult;
import mjkf.xinke.auth.modular.monitor.service.AuthSessionService;
import mjkf.xinke.common.page.CommonPageRequest;
import mjkf.xinke.common.util.CommonTimeFormatUtil;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 会话治理Service接口实现类
*
*
* @date 2022/6/24 22:25
*/
@Service
public class AuthSessionServiceImpl implements AuthSessionService {
@Resource(name = "loginUserApi")
private SaBaseLoginUserApi loginUserApi;
@Resource(name = "clientLoginUserApi")
private SaBaseLoginUserApi clientLoginUserApi;
@Override
public AuthSessionAnalysisResult analysis() {
AuthSessionAnalysisResult authSessionAnalysisResult = new AuthSessionAnalysisResult();
List<JSONObject> sessionListB = StpUtil.searchSessionId("", -1, -1, true).stream().map(sessionId -> {
JSONObject jsonObject = JSONUtil.createObj();
String userId = StrUtil.split(sessionId, StrUtil.COLON).get(3);
SaSession saSession = StpUtil.getSessionByLoginId(userId, false);
int tokenCount = saSession.getTokenSignList().size();
long createTime = saSession.getCreateTime();
jsonObject.set("userId", userId);
jsonObject.set("tokenCount", tokenCount);
jsonObject.set("createTime", DateTime.of(createTime));
return jsonObject;
}).collect(Collectors.toList());
List<JSONObject> sessionListC = StpClientUtil.searchSessionId("", -1, -1, true).stream().map(sessionId -> {
JSONObject jsonObject = JSONUtil.createObj();
String userId = StrUtil.split(sessionId, StrUtil.COLON).get(3);
SaSession saSession = StpClientUtil.getSessionByLoginId(userId, false);
int tokenCount = saSession.getTokenSignList().size();
long createTime = saSession.getCreateTime();
jsonObject.set("userId", userId);
jsonObject.set("tokenCount", tokenCount);
jsonObject.set("createTime", DateTime.of(createTime));
return jsonObject;
}).collect(Collectors.toList());
List<Integer> tokenCountList = CollectionUtil.newArrayList();
tokenCountList.addAll(sessionListB.stream().map(jsonObject -> jsonObject.getInt("tokenCount")).collect(Collectors.toList()));
tokenCountList.addAll(sessionListC.stream().map(jsonObject -> jsonObject.getInt("tokenCount")).collect(Collectors.toList()));
CollectionUtil.sort(tokenCountList, Comparator.comparingInt(Integer::intValue));
int currentSessionTotalCount = sessionListB.size() + sessionListC.size();
authSessionAnalysisResult.setCurrentSessionTotalCount(Convert.toStr(currentSessionTotalCount));
authSessionAnalysisResult.setMaxTokenCount(Convert.toStr(tokenCountList.get(tokenCountList.size() - 1)));
List<Date> sessionCreateTimeList = CollectionUtil.newArrayList();
sessionCreateTimeList.addAll(sessionListB.stream().map(jsonObject -> jsonObject.getDate("createTime")).collect(Collectors.toList()));
sessionCreateTimeList.addAll(sessionListC.stream().map(jsonObject -> jsonObject.getDate("createTime")).collect(Collectors.toList()));
DateTime oneHourAgo = DateUtil.offset(DateTime.now(), DateField.HOUR, -1);
authSessionAnalysisResult.setOneHourNewlyAdded(Convert.toStr(sessionCreateTimeList.stream().filter(date -> DateUtil.compare(oneHourAgo, date) <= 0).count()));
authSessionAnalysisResult.setProportionOfBAndC(sessionListB.size() + StrUtil.SLASH + sessionListC.size());
return authSessionAnalysisResult;
}
@Override
public Page<AuthSessionPageResult> pageForB(AuthSessionPageParam authSessionPageParam) {
Page<AuthSessionPageResult> defaultPage = CommonPageRequest.defaultPage();
long current = defaultPage.getCurrent();
int total = StpUtil.searchSessionId("", -1, Convert.toInt(defaultPage.getSize()), true).size();
if(ObjectUtil.isNotEmpty(total)) {
defaultPage = new Page<>(current, defaultPage.getSize(), total);
String keyword = "";
if(ObjectUtil.isNotEmpty(authSessionPageParam.getUserId())) {
keyword = authSessionPageParam.getUserId();
}
List<String> userIdList = StpUtil.searchSessionId(keyword,
Convert.toInt((current - 1) * defaultPage.getSize()),
Convert.toInt(defaultPage.getSize()), true).stream().map(sessionId ->
StrUtil.split(sessionId, StrUtil.COLON).get(3)).collect(Collectors.toList());
if (ObjectUtil.isNotEmpty(userIdList)) {
List<AuthSessionPageResult> authSessionPageResultList = loginUserApi.listUserByUserIdList(userIdList).stream().map(userJsonObject -> {
SaSession saSession = StpUtil.getSessionByLoginId(userJsonObject.getStr("id"), false);
AuthSessionPageResult authSessionPageResult = JSONUtil.toBean(userJsonObject, AuthSessionPageResult.class);
authSessionPageResult.setSessionId(saSession.getId());
authSessionPageResult.setSessionCreateTime(DateTime.of(saSession.getCreateTime()));
long sessionTimeOut = saSession.getTimeout();
if(sessionTimeOut == -1) {
authSessionPageResult.setSessionTimeout("永久");
} else {
authSessionPageResult.setSessionTimeout(CommonTimeFormatUtil.formatSeconds(saSession.getTimeout()));
}
List<AuthSessionPageResult.TokenSignInfo> tokenInfoList = saSession.getTokenSignList().stream().map(tokenSign -> {
AuthSessionPageResult.TokenSignInfo tokenSignInfo = new AuthSessionPageResult.TokenSignInfo();
tokenSignInfo.setTokenValue(tokenSign.getValue());
tokenSignInfo.setTokenDevice(tokenSign.getDevice());
long tokenTimeout = SaManager.getSaTokenDao().getTimeout(StpUtil.stpLogic.splicingKeyTokenValue(tokenSign.getValue()));
long tokenTimeoutConfig = StpUtil.stpLogic.getConfig().getTimeout();
if(tokenTimeout == -1) {
tokenSignInfo.setTokenTimeout("永久");
tokenSignInfo.setTokenTimeoutPercent(100d);
} else {
tokenSignInfo.setTokenTimeout(CommonTimeFormatUtil.formatSeconds(SaManager.getSaTokenDao()
.getTimeout(StpUtil.stpLogic.splicingKeyTokenValue(tokenSign.getValue()))));
if(tokenTimeoutConfig == -1) {
tokenSignInfo.setTokenTimeoutPercent(0d);
} else {
tokenSignInfo.setTokenTimeoutPercent(NumberUtil.div(tokenTimeout, tokenTimeoutConfig));
}
}
return tokenSignInfo;
}).collect(Collectors.toList());
authSessionPageResult.setTokenCount(tokenInfoList.size());
authSessionPageResult.setTokenSignList(tokenInfoList);
return authSessionPageResult;
}).collect(Collectors.toList());
defaultPage.setRecords(authSessionPageResultList);
}
}
return defaultPage;
}
@Override
public Page<AuthSessionPageResult> pageForC(AuthSessionPageParam authSessionPageParam) {
Page<AuthSessionPageResult> defaultPage = CommonPageRequest.defaultPage();
long current = defaultPage.getCurrent();
int total = StpClientUtil.searchSessionId("", -1, Convert.toInt(defaultPage.getSize()), true).size();
if(ObjectUtil.isNotEmpty(total)) {
defaultPage = new Page<>(current, defaultPage.getSize(), total);
String keyword = "";
if(ObjectUtil.isNotEmpty(authSessionPageParam.getUserId())) {
keyword = authSessionPageParam.getUserId();
}
List<String> userIdList = StpClientUtil.searchSessionId(keyword,
Convert.toInt((current - 1) * defaultPage.getSize()),
Convert.toInt(defaultPage.getSize()), true).stream().map(sessionId ->
StrUtil.split(sessionId, StrUtil.COLON).get(3)).collect(Collectors.toList());
if (ObjectUtil.isNotEmpty(userIdList)) {
List<AuthSessionPageResult> authSessionPageResultList = clientLoginUserApi.listUserByUserIdList(userIdList).stream().map(userJsonObject -> {
SaSession saSession = StpClientUtil.getSessionByLoginId(userJsonObject.getStr("id"), false);
AuthSessionPageResult authSessionPageResult = JSONUtil.toBean(userJsonObject, AuthSessionPageResult.class);
authSessionPageResult.setSessionId(saSession.getId());
authSessionPageResult.setSessionCreateTime(DateTime.of(saSession.getCreateTime()));
long sessionTimeOut = saSession.getTimeout();
if(sessionTimeOut == -1) {
authSessionPageResult.setSessionTimeout("永久");
} else {
authSessionPageResult.setSessionTimeout(CommonTimeFormatUtil.formatSeconds(saSession.getTimeout()));
}
List<AuthSessionPageResult.TokenSignInfo> tokenInfoList = saSession.getTokenSignList().stream().map(tokenSign -> {
AuthSessionPageResult.TokenSignInfo tokenSignInfo = new AuthSessionPageResult.TokenSignInfo();
tokenSignInfo.setTokenValue(tokenSign.getValue());
tokenSignInfo.setTokenDevice(tokenSign.getDevice());
long tokenTimeout = SaManager.getSaTokenDao().getTimeout(StpClientUtil.stpLogic.splicingKeyTokenValue(tokenSign.getValue()));
long tokenTimeoutConfig = StpClientUtil.stpLogic.getConfig().getTimeout();
if(tokenTimeout == -1) {
tokenSignInfo.setTokenTimeout("永久");
tokenSignInfo.setTokenTimeoutPercent(100d);
} else {
tokenSignInfo.setTokenTimeout(CommonTimeFormatUtil.formatSeconds(SaManager.getSaTokenDao()
.getTimeout(StpClientUtil.stpLogic.splicingKeyTokenValue(tokenSign.getValue()))));
if(tokenTimeoutConfig == -1) {
tokenSignInfo.setTokenTimeoutPercent(0d);
} else {
tokenSignInfo.setTokenTimeoutPercent(NumberUtil.div(tokenTimeout, tokenTimeoutConfig));
}
}
return tokenSignInfo;
}).collect(Collectors.toList());
authSessionPageResult.setTokenCount(tokenInfoList.size());
authSessionPageResult.setTokenSignList(tokenInfoList);
return authSessionPageResult;
}).collect(Collectors.toList());
defaultPage.setRecords(authSessionPageResultList);
}
}
return defaultPage;
}
@Override
public void exitSessionForB(List<AuthExitSessionParam> authExitSessionParamList) {
authExitSessionParamList.forEach(authExitSessionParam -> StpUtil.logout(authExitSessionParam.getUserId()));
}
@Override
public void exitSessionForC(List<AuthExitSessionParam> authExitSessionParamList) {
authExitSessionParamList.forEach(authExitSessionParam -> StpClientUtil.logout(authExitSessionParam.getUserId()));
}
@Override
public void exitTokenForB(List<AuthExitTokenParam> authExitTokenParamList) {
authExitTokenParamList.forEach(authExitTokenParam -> StpUtil.logoutByTokenValue(authExitTokenParam.getTokenValue()));
}
@Override
public void exitTokenForC(List<AuthExitTokenParam> authExitTokenParamList) {
authExitTokenParamList.forEach(authExitTokenParam -> StpClientUtil.logoutByTokenValue(authExitTokenParam.getTokenValue()));
}
}

View File

@@ -0,0 +1,47 @@
package mjkf.xinke.auth.modular.sso.controller;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import mjkf.xinke.auth.core.enums.SaClientTypeEnum;
import mjkf.xinke.auth.modular.sso.param.AuthSsoTicketLoginParam;
import mjkf.xinke.auth.modular.sso.service.AuthSsoService;
import mjkf.xinke.common.pojo.CommonResult;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* 单点登录控制器
*
*
* @date 2022/8/30 9:20
**/
@Api(tags = "单点登录控制器")
@ApiSupport(author = "SNOWY_TEAM", order = 4)
@RestController
@Validated
public class AuthSsoController {
@Resource
private AuthSsoService authSsoService;
/**
* 根据ticket执行单点登录
*
*
* @date 2021/10/15 13:12
**/
@ApiOperationSupport(order = 1)
@ApiOperation("根据ticket执行单点登录")
@PostMapping("/auth/sso/doLogin")
public CommonResult<String> doLogin(@RequestBody @Valid AuthSsoTicketLoginParam authAccountPasswordLoginParam) {
return CommonResult.data(authSsoService.doLogin(authAccountPasswordLoginParam, SaClientTypeEnum.B.getValue()));
}
}

View File

@@ -0,0 +1,28 @@
package mjkf.xinke.auth.modular.sso.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* ticket单点登录登录参数
*
*
* @date 2022/7/7 16:46
**/
@Getter
@Setter
public class AuthSsoTicketLoginParam {
/** ticket */
@ApiModelProperty(value = "ticket", required = true, position = 1)
@NotBlank(message = "ticket不能为空")
private String ticket;
/** 设备 */
@ApiModelProperty(value = "设备", position = 2)
private String device;
}

View File

@@ -0,0 +1,21 @@
package mjkf.xinke.auth.modular.sso.service;
import mjkf.xinke.auth.modular.sso.param.AuthSsoTicketLoginParam;
/**
* 单点登录Service接口
*
*
* @date 2022/8/30 9:20
**/
public interface AuthSsoService {
/**
* 根据ticket执行单点登录
*
*
* @date 2022/8/30 9:36
**/
String doLogin(AuthSsoTicketLoginParam authAccountPasswordLoginParam, String value);
}

View File

@@ -0,0 +1,27 @@
package mjkf.xinke.auth.modular.sso.service.impl;
import org.springframework.stereotype.Service;
import mjkf.xinke.auth.modular.login.service.AuthService;
import mjkf.xinke.auth.modular.sso.param.AuthSsoTicketLoginParam;
import mjkf.xinke.auth.modular.sso.service.AuthSsoService;
import javax.annotation.Resource;
/**
* 单点登录Service接口实现类
*
*
* @date 2022/8/30 9:21
**/
@Service
public class AuthSsoServiceImpl implements AuthSsoService {
@Resource
private AuthService authService;
@Override
public String doLogin(AuthSsoTicketLoginParam authAccountPasswordLoginParam, String device) {
return null;
}
}

View File

@@ -0,0 +1,77 @@
package mjkf.xinke.auth.modular.third.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import me.zhyd.oauth.model.AuthCallback;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import mjkf.xinke.auth.modular.third.entity.AuthThirdUser;
import mjkf.xinke.auth.modular.third.param.AuthThirdCallbackParam;
import mjkf.xinke.auth.modular.third.param.AuthThirdRenderParam;
import mjkf.xinke.auth.modular.third.param.AuthThirdUserPageParam;
import mjkf.xinke.auth.modular.third.result.AuthThirdRenderResult;
import mjkf.xinke.auth.modular.third.service.AuthThirdService;
import mjkf.xinke.common.pojo.CommonResult;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* 第三方登录控制器
*
*
* @date 2022/7/8 16:18
**/
@Api(tags = "三方登录控制器")
@ApiSupport(author = "SNOWY_TEAM", order = 5)
@RestController
@Validated
public class AuthThirdController {
@Resource
private AuthThirdService authThirdService;
/**
* 第三方登录页面渲染
*
*
* @date 2022/7/8 16:19
**/
@ApiOperationSupport(order = 1)
@ApiOperation("第三方登录页面渲染")
@GetMapping("/auth/third/render")
public CommonResult<AuthThirdRenderResult> render(@Valid AuthThirdRenderParam authThirdRenderParam) {
return CommonResult.data(authThirdService.render(authThirdRenderParam));
}
/**
* 第三方登录授权回调
*
*
* @date 2022/7/8 16:42
**/
@ApiOperationSupport(order = 2)
@ApiOperation("第三方登录授权回调")
@GetMapping("/auth/third/callback")
public CommonResult<String> callback(@Valid AuthThirdCallbackParam authThirdCallbackParam, AuthCallback authCallback) {
return CommonResult.data(authThirdService.callback(authThirdCallbackParam, authCallback));
}
/**
* 获取三方用户分页
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 3)
@ApiOperation("获取三方用户分页")
@GetMapping("/auth/third/page")
public CommonResult<Page<AuthThirdUser>> page(AuthThirdUserPageParam authThirdUserPageParam) {
return CommonResult.data(authThirdService.page(authThirdUserPageParam));
}
}

View File

@@ -0,0 +1,66 @@
package mjkf.xinke.auth.modular.third.entity;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import mjkf.xinke.common.pojo.CommonEntity;
/**
* 第三方登录实体
*
*
* @date 2022/7/9 14:29
*/
@Getter
@Setter
@TableName("AUTH_THIRD_USER")
public class AuthThirdUser extends CommonEntity {
/** id */
@ApiModelProperty(value = "id", position = 1)
private String id;
/** 租户id */
@ApiModelProperty(value = "租户id", position = 2)
private String tenantId;
/** 三方用户id */
@ApiModelProperty(value = "三方用户id", position = 3)
private String thirdId;
/** 系统用户id */
@ApiModelProperty(value = "系统用户id", position = 4)
private String userId;
/** 头像 */
@ApiModelProperty(value = "头像", position = 5)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String avatar;
/** 姓名 */
@ApiModelProperty(value = "姓名", position = 6)
private String name;
/** 昵称 */
@ApiModelProperty(value = "昵称", position = 7)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String nickname;
/** 性别 */
@ApiModelProperty(value = "性别", position = 8)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String gender;
/** 分类 */
@ApiModelProperty(value = "分类", position = 9)
private String category;
/** 扩展信息 */
@ApiModelProperty(value = "扩展信息", position = 10)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String extJson;
}

View File

@@ -0,0 +1,38 @@
package mjkf.xinke.auth.modular.third.enums;
import lombok.Getter;
import mjkf.xinke.common.exception.CommonException;
/**
* 第三方登录平台枚举
*
*
* @date 2021/10/11 14:02
**/
@Getter
public enum AuthThirdPlatformEnum {
/**
* GITEE
*/
GITEE("GITEE"),
/**
* WECHAT
*/
WECHAT("WECHAT");
private final String value;
AuthThirdPlatformEnum(String value) {
this.value = value;
}
public static void validate(String value) {
boolean flag = GITEE.getValue().equals(value) || WECHAT.getValue().equals(value);
if(!flag) {
throw new CommonException("不支持的第三方平台:{}", value);
}
}
}

View File

@@ -0,0 +1,14 @@
package mjkf.xinke.auth.modular.third.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import mjkf.xinke.auth.modular.third.entity.AuthThirdUser;
/**
* 第三方登录Mapper接口
*
*
* @date 2022/7/9 14:35
*/
public interface AuthThirdMapper extends BaseMapper<AuthThirdUser> {
}

View File

@@ -0,0 +1,6 @@
<?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="mjkf.xinke.auth.modular.third.mapper.AuthThirdMapper">
</mapper>

View File

@@ -0,0 +1,34 @@
package mjkf.xinke.auth.modular.third.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* 第三方登录回调参数
*
*
* @date 2022/7/8 20:38
*/
@Getter
@Setter
public class AuthThirdCallbackParam {
/** 第三方平台标识 */
@ApiModelProperty(value = "第三方平台标识", required = true, position = 1)
@NotBlank(message = "platform不能为空")
private String platform;
/** 第三方回调code */
@ApiModelProperty(value = "第三方回调code", required = true, position = 2)
@NotBlank(message = "code不能为空")
private String code;
/** 第三方回调state */
@ApiModelProperty(value = "第三方回调state", required = true, position = 3)
@NotBlank(message = "state不能为空")
private String state;
}

View File

@@ -0,0 +1,24 @@
package mjkf.xinke.auth.modular.third.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* 第三方登录授权参数
*
*
* @date 2022/7/8 20:38
*/
@Getter
@Setter
public class AuthThirdRenderParam {
/** 第三方平台标识 */
@ApiModelProperty(value = "第三方平台标识", required = true)
@NotBlank(message = "platform不能为空")
private String platform;
}

View File

@@ -0,0 +1,41 @@
package mjkf.xinke.auth.modular.third.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 三方用户查询参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class AuthThirdUserPageParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 排序字段 */
@ApiModelProperty(value = "排序字段字段驼峰名称userName")
private String sortField;
/** 排序方式 */
@ApiModelProperty(value = "排序方式升序ASCEND降序DESCEND")
private String sortOrder;
/** 三方用户分类 */
@ApiModelProperty(value = "分类")
private String category;
/** 用户名或昵称关键词 */
@ApiModelProperty(value = "用户名或昵称关键词")
private String searchKey;
}

View File

@@ -0,0 +1,25 @@
package mjkf.xinke.auth.modular.third.result;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 第三方登录授权结果
*
*
* @date 2022/7/8 20:39
*/
@Getter
@Setter
public class AuthThirdRenderResult {
/** 授权地址 */
@ApiModelProperty(value = "授权地址")
private String authorizeUrl;
/** 状态码 */
@ApiModelProperty(value = "状态码")
private String state;
}

View File

@@ -0,0 +1,44 @@
package mjkf.xinke.auth.modular.third.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import me.zhyd.oauth.model.AuthCallback;
import mjkf.xinke.auth.modular.third.entity.AuthThirdUser;
import mjkf.xinke.auth.modular.third.param.AuthThirdCallbackParam;
import mjkf.xinke.auth.modular.third.param.AuthThirdRenderParam;
import mjkf.xinke.auth.modular.third.param.AuthThirdUserPageParam;
import mjkf.xinke.auth.modular.third.result.AuthThirdRenderResult;
/**
* 第三方登录Service接口
*
*
* @date 2022/7/8 16:20
**/
public interface AuthThirdService extends IService<AuthThirdUser> {
/**
* 第三方登录页面渲染,返回授权结果
*
*
* @date 2022/7/8 16:37
**/
AuthThirdRenderResult render(AuthThirdRenderParam authThirdRenderParam);
/**
* 第三方登录授权回调返回登录token
*
*
* @date 2022/7/8 16:42
**/
String callback(AuthThirdCallbackParam authThirdCallbackParam, AuthCallback authCallback);
/**
* 获取三方用户分页
*
*
* @date 2022/4/24 20:08
*/
Page<AuthThirdUser> page(AuthThirdUserPageParam authThirdUserPageParam);
}

View File

@@ -0,0 +1,218 @@
package mjkf.xinke.auth.modular.third.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xkcoding.http.HttpUtil;
import com.xkcoding.http.support.hutool.HutoolImpl;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthGiteeRequest;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.request.AuthWeChatOpenRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import mjkf.xinke.auth.api.SaBaseLoginUserApi;
import mjkf.xinke.auth.core.enums.SaClientTypeEnum;
import mjkf.xinke.auth.core.pojo.SaBaseLoginUser;
import mjkf.xinke.auth.modular.login.enums.AuthDeviceTypeEnum;
import mjkf.xinke.auth.modular.login.service.AuthService;
import mjkf.xinke.auth.modular.third.entity.AuthThirdUser;
import mjkf.xinke.auth.modular.third.enums.AuthThirdPlatformEnum;
import mjkf.xinke.auth.modular.third.mapper.AuthThirdMapper;
import mjkf.xinke.auth.modular.third.param.AuthThirdCallbackParam;
import mjkf.xinke.auth.modular.third.param.AuthThirdRenderParam;
import mjkf.xinke.auth.modular.third.param.AuthThirdUserPageParam;
import mjkf.xinke.auth.modular.third.result.AuthThirdRenderResult;
import mjkf.xinke.auth.modular.third.service.AuthThirdService;
import mjkf.xinke.common.enums.CommonSortOrderEnum;
import mjkf.xinke.common.exception.CommonException;
import mjkf.xinke.common.page.CommonPageRequest;
import mjkf.xinke.dev.api.DevConfigApi;
import mjkf.xinke.ten.api.TenApi;
import javax.annotation.Resource;
/**
* 第三方登录Service接口实现类
*
*
* @date 2022/7/8 16:20
**/
@Service
public class AuthThirdServiceImpl extends ServiceImpl<AuthThirdMapper, AuthThirdUser> implements AuthThirdService {
private static final String SNOWY_THIRD_GITEE_CLIENT_ID_KEY = "SNOWY_THIRD_GITEE_CLIENT_ID";
private static final String SNOWY_THIRD_GITEE_CLIENT_SECRET_KEY = "SNOWY_THIRD_GITEE_CLIENT_SECRET";
private static final String SNOWY_THIRD_GITEE_REDIRECT_URL_KEY = "SNOWY_THIRD_GITEE_REDIRECT_URL";
private static final String SNOWY_THIRD_WECHAT_CLIENT_ID_KEY = "SNOWY_THIRD_WECHAT_CLIENT_ID";
private static final String SNOWY_THIRD_WECHAT_CLIENT_SECRET_KEY = "SNOWY_THIRD_WECHAT_CLIENT_SECRET";
private static final String SNOWY_THIRD_WECHAT_REDIRECT_URL_KEY = "SNOWY_THIRD_WECHAT_REDIRECT_URL";
@Resource
private TenApi tenApi;
@Resource
private DevConfigApi devConfigApi;
@Resource
private AuthService authService;
@Resource(name = "loginUserApi")
private SaBaseLoginUserApi loginUserApi;
@Resource(name = "clientLoginUserApi")
private SaBaseLoginUserApi clientLoginUserApi;
@Override
public AuthThirdRenderResult render(AuthThirdRenderParam authThirdRenderParam) {
// 获取请求
AuthRequest authRequest = this.getAuthRequest(authThirdRenderParam.getPlatform());
// 获取状态
String state = AuthStateUtils.createState();
// 构造授权地址
String authorizeUrl = authRequest.authorize(state);
// 获取当前租户的域名拼接
authorizeUrl = authorizeUrl + "&domain=" + tenApi.getCurrentTenDomain();
// 构造结果
AuthThirdRenderResult authThirdRenderResult = new AuthThirdRenderResult();
// 返回授权地址
authThirdRenderResult.setAuthorizeUrl(authorizeUrl);
// 返回状态码
authThirdRenderResult.setState(state);
return authThirdRenderResult;
}
@SuppressWarnings("ALL")
@Transactional(rollbackFor = Exception.class)
@Override
public String callback(AuthThirdCallbackParam authThirdCallbackParam, AuthCallback authCallback) {
// 获取请求
AuthRequest authRequest = this.getAuthRequest(authThirdCallbackParam.getPlatform());
// 执行请求
AuthResponse<AuthUser> authResponse = authRequest.login(authCallback);
if (authResponse.ok()) {
// 授权的用户信息
AuthUser authUser = authResponse.getData();
// 获取第三方用户id
String uuid = authUser.getUuid();
// 获取第三方用户来源
String source = authUser.getSource();
// 根据第三方用户id和用户来源获取用户信息
AuthThirdUser authThirdUser = this.getOne(new LambdaQueryWrapper<AuthThirdUser>().eq(AuthThirdUser::getThirdId, uuid)
.eq(AuthThirdUser::getCategory, source));
// 定义系统用户id
String userId;
if(ObjectUtil.isEmpty(authThirdUser)) {
// 如果用户不存在,则绑定用户并登录
userId = this.bindUser(authUser);
} else {
// 否则直接获取用户id登录
userId = authThirdUser.getUserId();
}
// TODO 此处使用PC端执行B端登录返回token
return authService.doLoginById(userId, AuthDeviceTypeEnum.PC.getValue(), SaClientTypeEnum.B.getValue());
} else {
throw new CommonException("第三方登录授权回调失败,原因:{}", authResponse.getMsg());
}
}
@Override
public Page<AuthThirdUser> page(AuthThirdUserPageParam authThirdUserPageParam) {
QueryWrapper<AuthThirdUser> queryWrapper = new QueryWrapper<>();
if(ObjectUtil.isNotEmpty(authThirdUserPageParam.getCategory())) {
queryWrapper.lambda().eq(AuthThirdUser::getCategory, authThirdUserPageParam.getCategory());
}
if(ObjectUtil.isNotEmpty(authThirdUserPageParam.getSearchKey())) {
queryWrapper.and(q -> q.lambda().like(AuthThirdUser::getName, authThirdUserPageParam.getSearchKey())
.or().like(AuthThirdUser::getNickname, authThirdUserPageParam.getSearchKey()));
}
if(ObjectUtil.isAllNotEmpty(authThirdUserPageParam.getSortField(), authThirdUserPageParam.getSortOrder())) {
CommonSortOrderEnum.validate(authThirdUserPageParam.getSortOrder());
queryWrapper.orderBy(true, authThirdUserPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
StrUtil.toUnderlineCase(authThirdUserPageParam.getSortField()));
} else {
queryWrapper.lambda().orderByDesc(AuthThirdUser::getCreateTime);
}
return this.page(CommonPageRequest.defaultPage(), queryWrapper);
}
/**
* 绑定用户并返回用户id
*
*
* @date 2022/7/9 14:58
*/
private String bindUser(AuthUser authUser) {
// TODO 此处固定绑定超管
SaBaseLoginUser saBaseLoginUser = loginUserApi.getUserByAccount("admin");
if(ObjectUtil.isEmpty(saBaseLoginUser)) {
throw new CommonException("第三方登录失败无法绑定账号admin原因账户admin不存在");
}
AuthThirdUser authThirdUser = new AuthThirdUser();
authThirdUser.setThirdId(authUser.getUuid());
authThirdUser.setUserId(saBaseLoginUser.getId());
authThirdUser.setAvatar(authUser.getAvatar());
authThirdUser.setName(authUser.getUsername());
authThirdUser.setNickname(authUser.getNickname());
authThirdUser.setGender(authUser.getGender().getDesc());
authThirdUser.setCategory(authUser.getSource());
authThirdUser.setExtJson(JSONUtil.toJsonStr(authUser.getRawUserInfo()));
this.save(authThirdUser);
return authThirdUser.getUserId();
}
/**
* 创建授权请求
*
*
* @date 2022/7/8 16:48
**/
private AuthRequest getAuthRequest(String source) {
AuthRequest authRequest = null;
source = source.toUpperCase();
HttpUtil.setHttp(new HutoolImpl());
AuthThirdPlatformEnum.validate(source);
if (source.equals(AuthThirdPlatformEnum.GITEE.getValue())) {
// GITEE登录
authRequest = new AuthGiteeRequest(AuthConfig.builder()
.clientId(devConfigApi.getValueByKey(SNOWY_THIRD_GITEE_CLIENT_ID_KEY))
.clientSecret(devConfigApi.getValueByKey(SNOWY_THIRD_GITEE_CLIENT_SECRET_KEY))
.redirectUri(devConfigApi.getValueByKey(SNOWY_THIRD_GITEE_REDIRECT_URL_KEY))
.build());
}
if(source.equals(AuthThirdPlatformEnum.WECHAT.getValue())){
// 微信登录
authRequest = new AuthWeChatOpenRequest(AuthConfig.builder()
.clientId(devConfigApi.getValueByKey(SNOWY_THIRD_WECHAT_CLIENT_ID_KEY))
.clientSecret(devConfigApi.getValueByKey(SNOWY_THIRD_WECHAT_CLIENT_SECRET_KEY))
.redirectUri(devConfigApi.getValueByKey(SNOWY_THIRD_WECHAT_REDIRECT_URL_KEY))
.build());
}
return authRequest;
}
}

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>mjkf-xinke-plugin-biz</artifactId>
<packaging>jar</packaging>
<description>业务功能插件</description>
<dependencies>
<!-- 每个插件都要引入自己的对外接口 -->
<dependency>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin-biz-api</artifactId>
</dependency>
<!-- 引入登录鉴权接口,用于获取登录用户 -->
<dependency>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin-auth-api</artifactId>
</dependency>
<!-- 引入系统接口,用于授权角色等功能 -->
<dependency>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin-sys-api</artifactId>
</dependency>
<!-- 引入开发工具接口,用于配置信息 -->
<dependency>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin-dev-api</artifactId>
</dependency>
<!-- 引入多数据源接口,用于提供数据源 -->
<dependency>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin-dbs-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,56 @@
package mjkf.xinke.biz.core.config;
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import mjkf.xinke.common.pojo.CommonResult;
import javax.annotation.Resource;
/**
* 业务相关配置
*
*
* @date 2022/7/7 16:18
**/
@Configuration
public class BizConfigure {
@Resource
private OpenApiExtensionResolver openApiExtensionResolver;
/**
* API文档分组配置
*
*
* @date 2022/7/7 16:18
**/
@Bean(value = "bizDocApi")
public Docket bizDocApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
.title("业务功能BIZ")
.description("业务功能BIZ")
.termsOfServiceUrl("https://www.xiaonuo.vip")
.contact(new Contact("SNOWY_TEAM","https://www.xiaonuo.vip", "xuyuxiang29@foxmail.com"))
.version("2.0.0")
.build())
.globalResponseMessage(RequestMethod.GET, CommonResult.responseList())
.globalResponseMessage(RequestMethod.POST, CommonResult.responseList())
.groupName("业务功能BIZ")
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.apis(RequestHandlerSelectors.basePackage("mjkf.xinke.biz"))
.paths(PathSelectors.any())
.build().extensions(openApiExtensionResolver.buildExtensions("业务功能BIZ"));
}
}

View File

@@ -0,0 +1,28 @@
package mjkf.xinke.biz.core.enums;
import lombok.Getter;
/**
* 系统内置的不可删除的标识枚举
*
*
* @date 2022/4/21 19:56
**/
@Getter
public enum BizBuildInEnum {
/**
* 超管用户账号
*/
BUILD_IN_USER_ACCOUNT("superAdmin", "超管");
private final String value;
private final String name;
BizBuildInEnum(String value, String name) {
this.value = value;
this.name = name;
}
}

View File

@@ -0,0 +1,36 @@
package mjkf.xinke.biz.core.enums;
import lombok.Getter;
/**
* 业务模块数据类型枚举
*
*
* @date 2023/3/3 10:40
**/
@Getter
public enum BizDataTypeEnum {
/**
* 机构
*/
ORG("ORG"),
/**
* 岗位
*/
POSITION("POSITION"),
/**
* 人员
*/
USER("USER");
private final String value;
BizDataTypeEnum(String value) {
this.value = value;
}
}

View File

@@ -0,0 +1,98 @@
package mjkf.xinke.biz.modular.dict.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.lang.tree.Tree;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import mjkf.xinke.biz.modular.dict.entity.BizDict;
import mjkf.xinke.biz.modular.dict.param.BizDictEditParam;
import mjkf.xinke.biz.modular.dict.param.BizDictPageParam;
import mjkf.xinke.biz.modular.dict.service.BizDictService;
import mjkf.xinke.common.annotation.CommonLog;
import mjkf.xinke.common.pojo.CommonResult;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
/**
* 业务字典控制器
*
*
* @date 2022/6/21 14:58
**/
@Api(tags = "业务字典控制器")
@ApiSupport(author = "SNOWY_TEAM", order = 4)
@RestController
@Validated
public class BizDictController {
@Resource
private BizDictService bizDictService;
/**
* 获取业务字典分页
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 1)
@ApiOperation("获取业务字典分页")
@SaCheckPermission("/biz/dict/page")
@GetMapping("/biz/dict/page")
public CommonResult<Page<BizDict>> page(BizDictPageParam bizDictPageParam) {
return CommonResult.data(bizDictService.page(bizDictPageParam));
}
/**
* 获取业务字典树
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 2)
@ApiOperation("获取业务字典树")
@SaCheckPermission("/biz/dict/tree")
@GetMapping("/biz/dict/tree")
public CommonResult<List<Tree<String>>> tree() {
return CommonResult.data(bizDictService.tree());
}
/**
* 获取所有字典树
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 3)
@ApiOperation("获取所有字典树")
@GetMapping("/biz/dict/treeAll")
public CommonResult<List<Tree<String>>> treeAll() {
return CommonResult.data(bizDictService.treeAll());
}
/**
* 编辑业务字典
*
*
* @date 2022/4/24 20:47
*/
@ApiOperationSupport(order = 4)
@ApiOperation("编辑业务字典")
@CommonLog("编辑业务字典")
@SaCheckPermission("/biz/dict/edit")
@PostMapping("/biz/dict/edit")
public CommonResult<String> edit(@RequestBody @Valid BizDictEditParam bizDictEditParam) {
bizDictService.edit(bizDictEditParam);
return CommonResult.ok();
}
}

View File

@@ -0,0 +1,55 @@
package mjkf.xinke.biz.modular.dict.entity;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import mjkf.xinke.common.pojo.CommonEntity;
/**
* 业务字典实体
*
*
* @date 2022/2/23 18:27
**/
@Getter
@Setter
@TableName("DEV_DICT")
public class BizDict extends CommonEntity {
/** id */
@ApiModelProperty(value = "id", position = 1)
private String id;
/** 租户id */
@ApiModelProperty(value = "租户id", position = 2)
private String tenantId;
/** 父id */
@ApiModelProperty(value = "父id", position = 3)
private String parentId;
/** 字典文字 */
@ApiModelProperty(value = "字典文字", position = 4)
private String dictLabel;
/** 字典值 */
@ApiModelProperty(value = "字典值", position = 5)
private String dictValue;
/** 分类 */
@ApiModelProperty(value = "分类", position = 6)
private String category;
/** 排序码 */
@ApiModelProperty(value = "排序码", position = 7)
private Integer sortCode;
/** 扩展信息 */
@ApiModelProperty(value = "扩展信息", position = 8)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String extJson;
}

View File

@@ -0,0 +1,33 @@
package mjkf.xinke.biz.modular.dict.enums;
import lombok.Getter;
import mjkf.xinke.common.exception.CommonException;
/**
* 业务字典分类枚举
*
*
* @date 2022/7/6 22:21
*/
@Getter
public enum BizDictCategoryEnum {
/**
* 业务
*/
BIZ("BIZ");
private final String value;
BizDictCategoryEnum(String value) {
this.value = value;
}
public static void validate(String value) {
boolean flag = BIZ.getValue().equals(value);
if(!flag) {
throw new CommonException("不支持的字典分类:{}", value);
}
}
}

View File

@@ -0,0 +1,14 @@
package mjkf.xinke.biz.modular.dict.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import mjkf.xinke.biz.modular.dict.entity.BizDict;
/**
* 业务字典Mapper接口
*
*
* @date 2022/4/22 10:40
**/
public interface BizDictMapper extends BaseMapper<BizDict> {
}

View File

@@ -0,0 +1,6 @@
<?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="mjkf.xinke.biz.modular.dict.mapper.BizDictMapper">
</mapper>

View File

@@ -0,0 +1,39 @@
package mjkf.xinke.biz.modular.dict.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 业务字典编辑参数
*
*
* @date 2022/7/30 21:48
*/
@Getter
@Setter
public class BizDictEditParam {
/** id */
@ApiModelProperty(value = "id", position = 1)
@NotBlank(message = "id不能为空")
private String id;
/** 字典文字 */
@ApiModelProperty(value = "字典文字", position = 2)
@NotBlank(message = "dictLabel不能为空")
private String dictLabel;
/** 排序码 */
@ApiModelProperty(value = "排序码", position = 3)
@NotNull(message = "sortCode不能为空")
private Integer sortCode;
/** 扩展信息 */
@ApiModelProperty(value = "扩展信息", position = 4)
private String extJson;
}

View File

@@ -0,0 +1,41 @@
package mjkf.xinke.biz.modular.dict.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 业务字典查询参数
*
*
* @date 2022/7/30 21:49
*/
@Getter
@Setter
public class BizDictPageParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 排序字段 */
@ApiModelProperty(value = "排序字段字段驼峰名称userName")
private String sortField;
/** 排序方式 */
@ApiModelProperty(value = "排序方式升序ASCEND降序DESCEND")
private String sortOrder;
/** 父id */
@ApiModelProperty(value = "父id")
private String parentId;
/** 字典文字关键词 */
@ApiModelProperty(value = "字典文字关键词")
private String searchKey;
}

View File

@@ -0,0 +1,60 @@
package mjkf.xinke.biz.modular.dict.service;
import cn.hutool.core.lang.tree.Tree;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import mjkf.xinke.biz.modular.dict.entity.BizDict;
import mjkf.xinke.biz.modular.dict.param.BizDictEditParam;
import mjkf.xinke.biz.modular.dict.param.BizDictPageParam;
import java.util.List;
/**
* 业务字典Service接口
*
*
* @date 2022/4/22 10:41
**/
public interface BizDictService extends IService<BizDict> {
/**
* 获取业务字典分页
*
*
* @date 2022/4/24 20:08
*/
Page<BizDict> page(BizDictPageParam bizDictPageParam);
/**
* 获取业务字典树
*
*
* @date 2022/4/24 20:08
*/
List<Tree<String>> tree();
/**
* 获取所有字典树
*
*
* @date 2022/4/24 20:08
*/
List<Tree<String>> treeAll();
/**
* 编辑业务字典
*
*
* @date 2022/4/24 21:13
*/
void edit(BizDictEditParam bizDictEditParam);
/**
* 获取业务字典详情
*
*
* @date 2022/4/24 21:18
*/
BizDict queryEntity(String id);
}

View File

@@ -0,0 +1,146 @@
package mjkf.xinke.biz.modular.dict.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fhs.trans.service.impl.DictionaryTransService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import mjkf.xinke.biz.modular.dict.entity.BizDict;
import mjkf.xinke.biz.modular.dict.enums.BizDictCategoryEnum;
import mjkf.xinke.biz.modular.dict.mapper.BizDictMapper;
import mjkf.xinke.biz.modular.dict.param.BizDictEditParam;
import mjkf.xinke.biz.modular.dict.param.BizDictPageParam;
import mjkf.xinke.biz.modular.dict.service.BizDictService;
import mjkf.xinke.common.enums.CommonSortOrderEnum;
import mjkf.xinke.common.exception.CommonException;
import mjkf.xinke.common.page.CommonPageRequest;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
* 字典Service接口实现类
*
*
* @date 2022/4/22 10:41
**/
@Service
public class BizDictServiceImpl extends ServiceImpl<BizDictMapper, BizDict> implements BizDictService, InitializingBean {
private static final String ROOT_PARENT_ID = "0";
@Resource
private DictionaryTransService dictionaryTransService;
@Override
public Page<BizDict> page(BizDictPageParam bizDictPageParam) {
QueryWrapper<BizDict> queryWrapper = new QueryWrapper<>();
// 查询部分字段
queryWrapper.lambda().select(BizDict::getId, BizDict::getParentId, BizDict::getCategory, BizDict::getDictLabel,
BizDict::getDictValue, BizDict::getSortCode).eq(BizDict::getCategory, BizDictCategoryEnum.BIZ.getValue());
if (ObjectUtil.isNotEmpty(bizDictPageParam.getParentId())) {
queryWrapper.lambda().eq(BizDict::getParentId, bizDictPageParam.getParentId())
.or().eq(BizDict::getId, bizDictPageParam.getParentId());
}
if (ObjectUtil.isNotEmpty(bizDictPageParam.getSearchKey())) {
queryWrapper.lambda().like(BizDict::getDictLabel, bizDictPageParam.getSearchKey());
}
if (ObjectUtil.isAllNotEmpty(bizDictPageParam.getSortField(), bizDictPageParam.getSortOrder())) {
CommonSortOrderEnum.validate(bizDictPageParam.getSortOrder());
queryWrapper.orderBy(true, bizDictPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
StrUtil.toUnderlineCase(bizDictPageParam.getSortField()));
} else {
queryWrapper.lambda().orderByAsc(BizDict::getSortCode);
}
return this.page(CommonPageRequest.defaultPage(), queryWrapper);
}
@Override
public List<Tree<String>> tree() {
LambdaQueryWrapper<BizDict> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(BizDict::getCategory, BizDictCategoryEnum.BIZ.getValue()).orderByAsc(BizDict::getSortCode);
List<BizDict> bizDictList = this.list(lambdaQueryWrapper);
List<TreeNode<String>> treeNodeList = bizDictList.stream().map(bizDict ->
new TreeNode<>(bizDict.getId(), bizDict.getParentId(),
bizDict.getDictLabel(), bizDict.getSortCode()).setExtra(JSONUtil.parseObj(bizDict)))
.collect(Collectors.toList());
return TreeUtil.build(treeNodeList, "0");
}
@Override
public List<Tree<String>> treeAll() {
List<BizDict> bizDictList = this.list();
List<TreeNode<String>> treeNodeList = bizDictList.stream().map(bizDict ->
new TreeNode<>(bizDict.getId(), bizDict.getParentId(),
bizDict.getDictLabel(), bizDict.getSortCode()).setExtra(JSONUtil.parseObj(bizDict)))
.collect(Collectors.toList());
return TreeUtil.build(treeNodeList, "0");
}
@Override
public void edit(BizDictEditParam bizDictEditParam) {
BizDict bizDict = this.queryEntity(bizDictEditParam.getId());
checkParam(bizDictEditParam);
BeanUtil.copyProperties(bizDictEditParam, bizDict);
this.updateById(bizDict);
refreshTransCache();
}
private void checkParam(BizDictEditParam bizDictEditParam) {
boolean hasSameDictLabel = this.count(new LambdaQueryWrapper<BizDict>()
.eq(BizDict::getCategory, BizDictCategoryEnum.BIZ.getValue())
.eq(BizDict::getDictLabel, bizDictEditParam.getDictLabel())
.ne(BizDict::getId, bizDictEditParam.getId())) > 0;
if (hasSameDictLabel) {
throw new CommonException("存在重复的字典文字,名称为:{}", bizDictEditParam.getDictLabel());
}
}
@Override
public BizDict queryEntity(String id) {
BizDict bizDict = this.getById(id);
if (ObjectUtil.isEmpty(bizDict)) {
throw new CommonException("字典不存在id值为{}", id);
}
return bizDict;
}
@Override
public void afterPropertiesSet() {
refreshTransCache();
}
private void refreshTransCache() {
// 异步不阻塞主线程,不会 增加启动用时
CompletableFuture.supplyAsync(() -> {
// 使用redis能解决共享问题但是性能没有直接取缓存的好。
dictionaryTransService.makeUseRedis();
List<BizDict> bizDictList = super.list(new LambdaQueryWrapper<>());
// 非root级别的字典根据ParentId分组
Map<String,List<BizDict>> bizDictGroupByPIDMap = bizDictList.stream().filter(dict -> !ROOT_PARENT_ID
.equals(dict.getParentId())).collect(Collectors.groupingBy(BizDict::getParentId));
Map<String,String> parentDictIdValMap = bizDictList.stream().filter(dict -> ROOT_PARENT_ID
.equals(dict.getParentId())).collect(Collectors.toMap(BizDict::getId, BizDict::getDictValue));
for (String parentId : parentDictIdValMap.keySet()) {
if(bizDictGroupByPIDMap.containsKey(parentId)){
dictionaryTransService.refreshCache(parentDictIdValMap.get(parentId), bizDictGroupByPIDMap.get(parentId).stream()
.collect(Collectors.toMap(BizDict::getDictValue, BizDict::getDictLabel)));
}
}
return null;
});
}
}

View File

@@ -0,0 +1,180 @@
package mjkf.xinke.biz.modular.org.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import mjkf.xinke.biz.modular.org.entity.BizOrg;
import mjkf.xinke.biz.modular.org.param.*;
import mjkf.xinke.biz.modular.org.service.BizOrgService;
import mjkf.xinke.biz.modular.user.entity.BizUser;
import mjkf.xinke.common.annotation.CommonLog;
import mjkf.xinke.common.pojo.CommonResult;
import mjkf.xinke.common.pojo.CommonValidList;
import javax.annotation.Resource;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.util.List;
/**
* 机构控制器
*
*
* @date 2022/4/24 19:55
*/
@Api(tags = "机构控制器")
@ApiSupport(author = "SNOWY_TEAM", order = 1)
@RestController
@Validated
public class BizOrgController {
@Resource
private BizOrgService bizOrgService;
/**
* 获取机构分页
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 1)
@ApiOperation("获取机构分页")
@SaCheckPermission("/biz/org/page")
@GetMapping("/biz/org/page")
public CommonResult<Page<BizOrg>> page(BizOrgPageParam bizOrgPageParam) {
return CommonResult.data(bizOrgService.page(bizOrgPageParam));
}
/**
* 获取机构树
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 2)
@ApiOperation("获取机构树")
@SaCheckPermission("/biz/org/tree")
@GetMapping("/biz/org/tree")
public CommonResult<List<Tree<String>>> tree() {
return CommonResult.data(bizOrgService.tree());
}
/**
* 添加机构
*
*
* @date 2022/4/24 20:47
*/
@ApiOperationSupport(order = 3)
@ApiOperation("添加机构")
@CommonLog("添加机构")
@SaCheckPermission("/biz/org/add")
@PostMapping("/biz/org/add")
public CommonResult<String> add(@RequestBody @Valid BizOrgAddParam bizOrgAddParam) {
bizOrgService.add(bizOrgAddParam);
return CommonResult.ok();
}
/**
* 编辑机构
*
*
* @date 2022/4/24 20:47
*/
@ApiOperationSupport(order = 4)
@ApiOperation("编辑机构")
@CommonLog("编辑机构")
@SaCheckPermission("/biz/org/edit")
@PostMapping("/biz/org/edit")
public CommonResult<String> edit(@RequestBody @Valid BizOrgEditParam bizOrgEditParam) {
bizOrgService.edit(bizOrgEditParam);
return CommonResult.ok();
}
/**
* 删除机构
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 5)
@ApiOperation("删除机构")
@CommonLog("删除机构")
@SaCheckPermission("/biz/org/delete")
@PostMapping("/biz/org/delete")
public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
CommonValidList<BizOrgIdParam> bizOrgIdParamList) {
bizOrgService.delete(bizOrgIdParamList);
return CommonResult.ok();
}
/**
* 获取机构详情
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 6)
@ApiOperation("获取机构详情")
@SaCheckPermission("/biz/org/detail")
@GetMapping("/biz/org/detail")
public CommonResult<BizOrg> detail(@Valid BizOrgIdParam bizOrgIdParam) {
return CommonResult.data(bizOrgService.detail(bizOrgIdParam));
}
/* ====机构部分所需要用到的选择器==== */
/**
* 获取机构树选择器
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 7)
@ApiOperation("获取机构树选择器")
@SaCheckPermission("/biz/org/orgTreeSelector")
@GetMapping("/biz/org/orgTreeSelector")
public CommonResult<List<Tree<String>>> orgTreeSelector() {
return CommonResult.data(bizOrgService.orgTreeSelector());
}
/**
* 获取人员选择器
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 8)
@ApiOperation("获取人员选择器")
@SaCheckPermission("/biz/org/userSelector")
@GetMapping("/biz/org/userSelector")
public CommonResult<Page<BizUser>> userSelector(BizOrgSelectorUserParam bizOrgSelectorUserParam) {
return CommonResult.data(bizOrgService.userSelector(bizOrgSelectorUserParam));
}
/**
* 获取机构动态字段的配置
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 9)
@ApiOperation("获取机构动态字段的配置")
@SaCheckPermission("/biz/org/dynamicFieldConfigList")
@GetMapping("/biz/org/dynamicFieldConfigList")
public CommonResult<List<JSONObject>> dynamicFieldConfigList() {
return CommonResult.data(bizOrgService.dynamicFieldConfigList());
}
}

View File

@@ -0,0 +1,65 @@
package mjkf.xinke.biz.modular.org.entity;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fhs.core.trans.anno.Trans;
import com.fhs.core.trans.constant.TransType;
import com.fhs.core.trans.vo.TransPojo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import mjkf.xinke.biz.modular.user.entity.BizUser;
import mjkf.xinke.common.pojo.CommonEntity;
/**
* 机构实体
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
@TableName("SYS_ORG")
public class BizOrg extends CommonEntity implements TransPojo {
/** id */
@ApiModelProperty(value = "id", position = 1)
private String id;
/** 租户id */
@ApiModelProperty(value = "租户id", position = 2)
private String tenantId;
/** 父id */
@ApiModelProperty(value = "父id", position = 3)
private String parentId;
/** 主管id */
@ApiModelProperty(value = "主管id", position = 4)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
@Trans(type = TransType.SIMPLE, target = BizUser.class, fields = "name", alias = "director", ref = "directorName")
private String directorId;
/** 名称 */
@ApiModelProperty(value = "名称", position = 5)
private String name;
/** 编码 */
@ApiModelProperty(value = "编码", position = 6)
private String code;
/** 分类 */
@ApiModelProperty(value = "分类", position = 7)
private String category;
/** 排序码 */
@ApiModelProperty(value = "排序码", position = 8)
private Integer sortCode;
/** 扩展信息 */
@ApiModelProperty(value = "扩展信息", position = 9)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String extJson;
}

View File

@@ -0,0 +1,34 @@
package mjkf.xinke.biz.modular.org.enums;
import lombok.Getter;
import mjkf.xinke.common.exception.CommonException;
/**
* 机构分类枚举
*
*
* @date 2022/4/21 19:56
**/
@Getter
public enum BizOrgCategoryEnum {
/** 公司 */
COMPANY("COMPANY"),
/** 部门 */
DEPT("DEPT");
private final String value;
BizOrgCategoryEnum(String value) {
this.value = value;
}
public static void validate(String value) {
boolean flag = COMPANY.getValue().equals(value) || DEPT.getValue().equals(value);
if(!flag) {
throw new CommonException("不支持的机构分类:{}", value);
}
}
}

View File

@@ -0,0 +1,14 @@
package mjkf.xinke.biz.modular.org.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import mjkf.xinke.biz.modular.org.entity.BizOrg;
/**
* 机构Mapper接口
*
*
* @date 2022/4/21 18:37
**/
public interface BizOrgMapper extends BaseMapper<BizOrg> {
}

View File

@@ -0,0 +1,6 @@
<?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="mjkf.xinke.biz.modular.org.mapper.BizOrgMapper">
</mapper>

View File

@@ -0,0 +1,48 @@
package mjkf.xinke.biz.modular.org.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 机构添加参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizOrgAddParam {
/** 父id */
@ApiModelProperty(value = "父id", required = true, position = 1)
@NotBlank(message = "parentId不能为空")
private String parentId;
/** 名称 */
@ApiModelProperty(value = "名称", required = true, position = 2)
@NotBlank(message = "name不能为空")
private String name;
/** 分类 */
@ApiModelProperty(value = "分类", required = true, position = 3)
@NotBlank(message = "category不能为空")
private String category;
/** 排序码 */
@ApiModelProperty(value = "排序码", required = true, position = 4)
@NotNull(message = "sortCode不能为空")
private Integer sortCode;
/** 主管id */
@ApiModelProperty(value = "主管id", position = 5)
private String directorId;
/** 扩展JSON */
@ApiModelProperty(value = "扩展JSON", position = 6)
private String extJson;
}

View File

@@ -0,0 +1,53 @@
package mjkf.xinke.biz.modular.org.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 机构编辑参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizOrgEditParam {
/** id */
@ApiModelProperty(value = "id", required = true, position = 1)
@NotBlank(message = "id不能为空")
private String id;
/** 父id */
@ApiModelProperty(value = "父id", required = true, position = 2)
@NotBlank(message = "parentId不能为空")
private String parentId;
/** 名称 */
@ApiModelProperty(value = "名称", required = true, position = 3)
@NotBlank(message = "name不能为空")
private String name;
/** 分类 */
@ApiModelProperty(value = "分类", required = true, position = 4)
@NotBlank(message = "category不能为空")
private String category;
/** 排序码 */
@ApiModelProperty(value = "排序码", required = true, position = 5)
@NotNull(message = "sortCode不能为空")
private Integer sortCode;
/** 主管id */
@ApiModelProperty(value = "主管id", position = 6)
private String directorId;
/** 扩展JSON */
@ApiModelProperty(value = "扩展JSON", position = 7)
private String extJson;
}

View File

@@ -0,0 +1,24 @@
package mjkf.xinke.biz.modular.org.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* 机构Id参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizOrgIdParam {
/** id */
@ApiModelProperty(value = "id", required = true)
@NotBlank(message = "id不能为空")
private String id;
}

View File

@@ -0,0 +1,41 @@
package mjkf.xinke.biz.modular.org.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 机构查询参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizOrgPageParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 排序字段 */
@ApiModelProperty(value = "排序字段字段驼峰名称userName")
private String sortField;
/** 排序方式 */
@ApiModelProperty(value = "排序方式升序ASCEND降序DESCEND")
private String sortOrder;
/** 父id */
@ApiModelProperty(value = "父id")
private String parentId;
/** 名称关键词 */
@ApiModelProperty(value = "名称关键词")
private String searchKey;
}

View File

@@ -0,0 +1,33 @@
package mjkf.xinke.biz.modular.org.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 机构列表选择器参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizOrgSelectorOrgListParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 父id */
@ApiModelProperty(value = "父id")
private String parentId;
/** 名称关键词 */
@ApiModelProperty(value = "名称关键词")
private String searchKey;
}

View File

@@ -0,0 +1,33 @@
package mjkf.xinke.biz.modular.org.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 人员选择器参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizOrgSelectorUserParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 机构id */
@ApiModelProperty(value = "机构id")
private String orgId;
/** 姓名关键词 */
@ApiModelProperty(value = "姓名关键词")
private String searchKey;
}

View File

@@ -0,0 +1,173 @@
package mjkf.xinke.biz.modular.org.service;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import mjkf.xinke.biz.modular.org.entity.BizOrg;
import mjkf.xinke.biz.modular.org.param.*;
import mjkf.xinke.biz.modular.user.entity.BizUser;
import java.util.List;
/**
* 机构Service接口
*
*
* @date 2022/4/21 18:35
**/
public interface BizOrgService extends IService<BizOrg> {
/**
* 获取机构分页
*
*
* @date 2022/4/24 20:08
*/
Page<BizOrg> page(BizOrgPageParam bizOrgPageParam);
/**
* 获取机构树
*
*
* @date 2022/4/24 20:08
*/
List<Tree<String>> tree();
/**
* 添加机构
*
*
* @date 2022/4/24 20:48
*/
void add(BizOrgAddParam bizOrgAddParam);
/**
* 编辑机构
*
*
* @date 2022/4/24 21:13
*/
void edit(BizOrgEditParam bizOrgEditParam);
/**
* 删除机构
*
*
* @date 2022/4/24 21:18
*/
void delete(List<BizOrgIdParam> bizOrgIdParamList);
/**
* 获取机构详情
*
*
* @date 2022/4/24 21:18
*/
BizOrg detail(BizOrgIdParam bizOrgIdParam);
/**
* 获取机构详情
*
*
* @date 2022/7/25 19:42
**/
BizOrg queryEntity(String id);
/**
* 获取所有机构
*
*
* @date 2022/7/25 19:42
**/
List<BizOrg> getAllOrgList();
/**
* 根据组织全名称获取组织id有则返回无则创建
*
*
* @date 2023/3/7 15:44
**/
String getOrgIdByOrgFullNameWithCreate(String orgFullName);
/**
* 根据id获取父子数据列表
*
*
* @date 2022/8/15 14:55
**/
List<BizOrg> getParentAndChildListById(List<BizOrg> originDataList, String id, boolean includeSelf);
/**
* 根据id获取所有的子数据列表
*
*
* @date 2022/8/15 14:55
**/
List<BizOrg> getChildListById(List<BizOrg> originDataList, String id, boolean includeSelf);
/**
* 根据id获取所有的父数据列表
*
*
* @date 2022/8/15 14:55
**/
List<BizOrg> getParentListById(List<BizOrg> originDataList, String id, boolean includeSelf);
/**
* 根据id获取数据
*
*
* @date 2022/8/15 14:55
**/
BizOrg getById(List<BizOrg> originDataList, String id);
/**
* 根据id获取父数据
*
*
* @date 2022/8/15 14:55
**/
BizOrg getParentById(List<BizOrg> originDataList, String id);
/**
* 根据id获取子数据
*
*
* @date 2022/8/15 14:55
**/
BizOrg getChildById(List<BizOrg> originDataList, String id);
/**
* 获取机构树选择器
*
*
* @date 2022/4/24 20:08
*/
List<Tree<String>> orgTreeSelector();
/**
* 获取机构列表选择器
*
*
* @date 2022/7/22 13:34
**/
List<BizOrg> orgListSelector(BizOrgSelectorOrgListParam bizOrgSelectorOrgListParam);
/**
* 获取人员选择器
*
*
* @date 2022/4/24 20:08
*/
Page<BizUser> userSelector(BizOrgSelectorUserParam bizOrgSelectorUserParam);
/**
* 获取动态字段配置
*
*
* @date 2022/8/5 323
*/
List<JSONObject> dynamicFieldConfigList();
}

View File

@@ -0,0 +1,468 @@
package mjkf.xinke.biz.modular.org.service.impl;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import mjkf.xinke.auth.core.util.StpLoginUserUtil;
import mjkf.xinke.biz.core.enums.BizDataTypeEnum;
import mjkf.xinke.biz.modular.org.entity.BizOrg;
import mjkf.xinke.biz.modular.org.enums.BizOrgCategoryEnum;
import mjkf.xinke.biz.modular.org.mapper.BizOrgMapper;
import mjkf.xinke.biz.modular.org.param.*;
import mjkf.xinke.biz.modular.org.service.BizOrgService;
import mjkf.xinke.biz.modular.position.entity.BizPosition;
import mjkf.xinke.biz.modular.position.service.BizPositionService;
import mjkf.xinke.biz.modular.user.entity.BizUser;
import mjkf.xinke.biz.modular.user.service.BizUserService;
import mjkf.xinke.common.enums.CommonSortOrderEnum;
import mjkf.xinke.common.exception.CommonException;
import mjkf.xinke.common.listener.CommonDataChangeEventCenter;
import mjkf.xinke.common.page.CommonPageRequest;
import mjkf.xinke.dbs.api.DbsApi;
import mjkf.xinke.dev.api.DevDfcApi;
import mjkf.xinke.sys.api.SysRoleApi;
import javax.annotation.Resource;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 机构Service接口实现类
*
*
* @date 2022/2/23 18:43
**/
@Service
public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> implements BizOrgService {
@Resource
private SysRoleApi sysRoleApi;
@Resource
private BizPositionService bizPositionService;
@Resource
private BizUserService bizUserService;
@Resource
private DbsApi dbsApi;
@Resource
private DevDfcApi devDfcApi;
@Override
public Page<BizOrg> page(BizOrgPageParam bizOrgPageParam) {
QueryWrapper<BizOrg> queryWrapper = new QueryWrapper<>();
// 查询部分字段
queryWrapper.lambda().select(BizOrg::getId, BizOrg::getParentId, BizOrg::getName,
BizOrg::getCategory, BizOrg::getSortCode);
if(ObjectUtil.isNotEmpty(bizOrgPageParam.getParentId())) {
queryWrapper.lambda().eq(BizOrg::getParentId, bizOrgPageParam.getParentId());
}
if(ObjectUtil.isNotEmpty(bizOrgPageParam.getSearchKey())) {
queryWrapper.lambda().like(BizOrg::getName, bizOrgPageParam.getSearchKey());
}
if(ObjectUtil.isAllNotEmpty(bizOrgPageParam.getSortField(), bizOrgPageParam.getSortOrder())) {
CommonSortOrderEnum.validate(bizOrgPageParam.getSortOrder());
queryWrapper.orderBy(true, bizOrgPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
StrUtil.toUnderlineCase(bizOrgPageParam.getSortField()));
} else {
queryWrapper.lambda().orderByAsc(BizOrg::getSortCode);
}
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
queryWrapper.lambda().in(BizOrg::getId, loginUserDataScope);
} else {
return new Page<>();
}
return this.page(CommonPageRequest.defaultPage(), queryWrapper);
}
@Override
public List<Tree<String>> tree() {
// 获取所有机构
List<BizOrg> allOrgList = this.list();
// 定义机构集合
Set<BizOrg> bizOrgSet = CollectionUtil.newHashSet();
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
loginUserDataScope.forEach(orgId -> bizOrgSet.addAll(this.getParentListById(allOrgList, orgId, true)));
} else {
return CollectionUtil.newArrayList();
}
List<TreeNode<String>> treeNodeList = bizOrgSet.stream().map(bizOrg ->
new TreeNode<>(bizOrg.getId(), bizOrg.getParentId(),
bizOrg.getName(), bizOrg.getSortCode()).setExtra(JSONUtil.parseObj(bizOrg)))
.collect(Collectors.toList());
return TreeUtil.build(treeNodeList, "0");
}
@Transactional(rollbackFor = Exception.class)
@Override
public void add(BizOrgAddParam bizOrgAddParam) {
BizOrgCategoryEnum.validate(bizOrgAddParam.getCategory());
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.contains(bizOrgAddParam.getParentId())) {
throw new CommonException("您没有权限在该机构下增加机构机构id{}", bizOrgAddParam.getParentId());
}
} else {
throw new CommonException("您没有权限增加机构");
}
BizOrg bizOrg = BeanUtil.toBean(bizOrgAddParam, BizOrg.class);
// 重复名称
boolean repeatName = this.count(new LambdaQueryWrapper<BizOrg>().eq(BizOrg::getParentId, bizOrg.getParentId())
.eq(BizOrg::getName, bizOrg.getName())) > 0;
if(repeatName) {
throw new CommonException("存在重复的同级机构,名称为:{}", bizOrg.getName());
}
bizOrg.setCode(RandomUtil.randomString(10));
this.save(bizOrg);
// 发布增加事件
CommonDataChangeEventCenter.doAddWithData(BizDataTypeEnum.ORG.getValue(), JSONUtil.createArray().put(bizOrg));
}
@Transactional(rollbackFor = Exception.class)
@Override
public void edit(BizOrgEditParam bizOrgEditParam) {
BizOrgCategoryEnum.validate(bizOrgEditParam.getCategory());
BizOrg bizOrg = this.queryEntity(bizOrgEditParam.getId());
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.contains(bizOrg.getId())) {
throw new CommonException("您没有权限编辑该机构机构id{}", bizOrg.getId());
}
if(!loginUserDataScope.contains(bizOrg.getParentId())) {
throw new CommonException("您没有权限编辑该机构下的机构机构id{}", bizOrg.getParentId());
}
} else {
throw new CommonException("您没有权限编辑该机构机构id{}", bizOrg.getId());
}
BeanUtil.copyProperties(bizOrgEditParam, bizOrg);
boolean repeatName = this.count(new LambdaQueryWrapper<BizOrg>().eq(BizOrg::getParentId, bizOrg.getParentId())
.eq(BizOrg::getName, bizOrg.getName()).ne(BizOrg::getId, bizOrg.getId())) > 0;
if(repeatName) {
throw new CommonException("存在重复的同级机构,名称为:{}", bizOrg.getName());
}
List<BizOrg> originDataList = this.list();
boolean errorLevel = this.getChildListById(originDataList, bizOrg.getId(), true).stream()
.map(BizOrg::getId).collect(Collectors.toList()).contains(bizOrg.getParentId());
if(errorLevel) {
throw new CommonException("不可选择上级机构:{}", this.getById(originDataList, bizOrg.getParentId()).getName());
}
this.updateById(bizOrg);
// 发布更新事件
CommonDataChangeEventCenter.doUpdateWithData(BizDataTypeEnum.ORG.getValue(), JSONUtil.createArray().put(bizOrg));
}
@Transactional(rollbackFor = Exception.class)
@Override
public void delete(List<BizOrgIdParam> bizOrgIdParamList) {
List<String> orgIdList = CollStreamUtil.toList(bizOrgIdParamList, BizOrgIdParam::getId);
if(ObjectUtil.isNotEmpty(orgIdList)) {
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.containsAll(orgIdList)) {
throw new CommonException("您没有权限删除这些机构机构id{}", orgIdList);
}
} else {
throw new CommonException("您没有权限删除这些机构机构id{}", orgIdList);
}
List<BizOrg> allOrgList = this.list();
// 获取所有子机构
List<String> toDeleteOrgIdList = CollectionUtil.newArrayList();
orgIdList.forEach(orgId -> toDeleteOrgIdList.addAll(this.getChildListById(allOrgList, orgId, true).stream()
.map(BizOrg::getId).collect(Collectors.toList())));
// 机构下有人不能删除(直属机构)
boolean hasOrgUser = bizUserService.count(new LambdaQueryWrapper<BizUser>().in(BizUser::getOrgId, toDeleteOrgIdList)) > 0;
if(hasOrgUser) {
throw new CommonException("请先删除机构下的人员");
}
// 机构下有人不能删除(兼任机构)
List<String> positionJsonList = bizUserService.list(new LambdaQueryWrapper<BizUser>()
.isNotNull(BizUser::getPositionJson)).stream().map(BizUser::getPositionJson).collect(Collectors.toList());
if(ObjectUtil.isNotEmpty(positionJsonList)) {
List<String> positionOrgIdList = CollectionUtil.newArrayList();
positionJsonList.forEach(positionJson -> JSONUtil.toList(JSONUtil.parseArray(positionJson), JSONObject.class)
.forEach(jsonObject -> positionOrgIdList.add(jsonObject.getStr("orgId"))));
boolean hasPositionUser = CollectionUtil.intersectionDistinct(toDeleteOrgIdList, CollectionUtil.removeNull(positionOrgIdList)).size() > 0;
if(hasPositionUser) {
throw new CommonException("请先删除机构下的人员");
}
}
// 机构下有角色不能删除
boolean hasRole = sysRoleApi.orgHasRole(toDeleteOrgIdList);
if(hasRole) {
throw new CommonException("请先删除机构下的角色");
}
// 机构下有岗位不能删除
boolean hasPosition = bizPositionService.count(new LambdaQueryWrapper<BizPosition>().in(BizPosition::getOrgId, toDeleteOrgIdList)) > 0;
if(hasPosition) {
throw new CommonException("请先删除机构下的岗位");
}
// 执行删除
this.removeByIds(toDeleteOrgIdList);
// 发布删除事件
CommonDataChangeEventCenter.doDeleteWithDataId(BizDataTypeEnum.ORG.getValue(), toDeleteOrgIdList);
}
}
@Override
public BizOrg detail(BizOrgIdParam bizOrgIdParam) {
return this.queryEntity(bizOrgIdParam.getId());
}
@Override
public BizOrg queryEntity(String id) {
BizOrg bizOrg = this.getById(id);
if(ObjectUtil.isEmpty(bizOrg)) {
throw new CommonException("机构不存在id值为{}", id);
}
return bizOrg;
}
@Override
public List<BizOrg> getAllOrgList() {
return this.list(new LambdaQueryWrapper<BizOrg>().orderByAsc(BizOrg::getSortCode));
}
@Override
public String getOrgIdByOrgFullNameWithCreate(String orgFullName) {
List<BizOrg> allOrgList = this.getAllOrgList();
List<Tree<String>> treeList = TreeUtil.build(allOrgList.stream().map(bizOrg ->
new TreeNode<>(bizOrg.getId(), bizOrg.getParentId(), bizOrg.getName(), bizOrg.getSortCode()))
.collect(Collectors.toList()), "0");
return findOrgIdByOrgName("0", StrUtil.split(orgFullName, StrUtil.DASHED).iterator(), allOrgList, treeList);
}
public String findOrgIdByOrgName(String parentId, Iterator<String> iterator, List<BizOrg> allOrgList, List<Tree<String>> treeList) {
String orgName = iterator.next();
if(ObjectUtil.isNotEmpty(treeList)) {
List<Tree<String>> findList = treeList.stream().filter(tree -> tree.getName().equals(orgName)).collect(Collectors.toList());
if(ObjectUtil.isNotEmpty(findList)) {
if(iterator.hasNext()) {
return findOrgIdByOrgName(findList.get(0).getId(), iterator, allOrgList, findList.get(0).getChildren());
} else {
return findList.get(0).getId();
}
}
}
String orgId = this.doCreateOrg(parentId, orgName, allOrgList);
if(iterator.hasNext()) {
return findOrgIdByOrgName(orgId, iterator, allOrgList, CollectionUtil.newArrayList());
} else {
return orgId;
}
}
/**
* 执行创建机构
*
*
* @date 2023/3/8 9:38
**/
public String doCreateOrg(String parentId, String orgName, List<BizOrg> allOrgList) {
//创建该机构
BizOrg bizOrg = new BizOrg();
bizOrg.setName(orgName);
bizOrg.setCode(RandomUtil.randomString(10));
bizOrg.setParentId(parentId);
bizOrg.setCategory("0".equals(parentId)?BizOrgCategoryEnum.COMPANY.getValue():BizOrgCategoryEnum.DEPT.getValue());
bizOrg.setSortCode(99);
this.save(bizOrg);
// 发布增加事件
CommonDataChangeEventCenter.doAddWithData(BizDataTypeEnum.ORG.getValue(), JSONUtil.createArray().put(bizOrg));
return bizOrg.getId();
}
/* ====机构部分所需要用到的选择器==== */
@Override
public List<Tree<String>> orgTreeSelector() {
LambdaQueryWrapper<BizOrg> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
// 定义机构集合
Set<BizOrg> bizOrgSet = CollectionUtil.newHashSet();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
// 获取所有机构
List<BizOrg> allOrgList = this.list();
loginUserDataScope.forEach(orgId -> bizOrgSet.addAll(this.getParentListById(allOrgList, orgId, true)));
List<String> loginUserDataScopeFullList = bizOrgSet.stream().map(BizOrg::getId).collect(Collectors.toList());
lambdaQueryWrapper.in(BizOrg::getId, loginUserDataScopeFullList);
} else {
return CollectionUtil.newArrayList();
}
lambdaQueryWrapper.orderByAsc(BizOrg::getSortCode);
List<BizOrg> bizOrgList = this.list(lambdaQueryWrapper);
List<TreeNode<String>> treeNodeList = bizOrgList.stream().map(bizOrg ->
new TreeNode<>(bizOrg.getId(), bizOrg.getParentId(), bizOrg.getName(), bizOrg.getSortCode()))
.collect(Collectors.toList());
return TreeUtil.build(treeNodeList, "0");
}
@Override
public List<BizOrg> orgListSelector(BizOrgSelectorOrgListParam bizOrgSelectorOrgListParam) {
LambdaQueryWrapper<BizOrg> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
lambdaQueryWrapper.in(BizOrg::getId, loginUserDataScope);
} else {
return CollectionUtil.newArrayList();
}
// 查询部分字段
lambdaQueryWrapper.select(BizOrg::getId, BizOrg::getParentId, BizOrg::getName,
BizOrg::getCategory, BizOrg::getSortCode);
if(ObjectUtil.isNotEmpty(bizOrgSelectorOrgListParam.getParentId())) {
lambdaQueryWrapper.eq(BizOrg::getParentId, bizOrgSelectorOrgListParam.getParentId());
}
if(ObjectUtil.isNotEmpty(bizOrgSelectorOrgListParam.getSearchKey())) {
lambdaQueryWrapper.like(BizOrg::getName, bizOrgSelectorOrgListParam.getSearchKey());
}
lambdaQueryWrapper.orderByAsc(BizOrg::getSortCode);
return this.list(lambdaQueryWrapper);
}
@Override
public Page<BizUser> userSelector(BizOrgSelectorUserParam bizOrgSelectorUserParam) {
LambdaQueryWrapper<BizUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
lambdaQueryWrapper.in(BizUser::getOrgId, loginUserDataScope);
} else {
return new Page<>();
}
// 只查询部分字段
lambdaQueryWrapper.select(BizUser::getId, BizUser::getAvatar, BizUser::getOrgId, BizUser::getPositionId, BizUser::getAccount,
BizUser::getName, BizUser::getSortCode, BizUser::getGender, BizUser::getEntryDate);
if (ObjectUtil.isNotEmpty(bizOrgSelectorUserParam.getOrgId())) {
// 如果机构id不为空则查询该机构及其子机构下的所有人
List<String> childOrgIdList = CollStreamUtil.toList(this.getChildListById(this
.getAllOrgList(), bizOrgSelectorUserParam.getOrgId(), true), BizOrg::getId);
if (ObjectUtil.isNotEmpty(childOrgIdList)) {
lambdaQueryWrapper.in(BizUser::getOrgId, childOrgIdList);
} else {
return new Page<>();
}
}
if(ObjectUtil.isNotEmpty(bizOrgSelectorUserParam.getSearchKey())) {
lambdaQueryWrapper.like(BizUser::getName, bizOrgSelectorUserParam.getSearchKey());
}
lambdaQueryWrapper.orderByAsc(BizUser::getSortCode);
return bizUserService.page(CommonPageRequest.defaultPage(), lambdaQueryWrapper);
}
@Override
public List<JSONObject> dynamicFieldConfigList() {
String currentDataSourceId = dbsApi.getCurrentDataSourceId();
String tableName = AnnotationUtil.getAnnotationValue(BizOrg.class, TableName.class);
return devDfcApi.getTableFieldList(currentDataSourceId, tableName);
}
/* ====以下为各种递归方法==== */
@Override
public List<BizOrg> getParentAndChildListById(List<BizOrg> originDataList, String id, boolean includeSelf) {
List<BizOrg> parentListById = this.getParentListById(originDataList, id, false);
List<BizOrg> childListById = this.getChildListById(originDataList, id, true);
parentListById.addAll(childListById);
return parentListById;
}
@Override
public List<BizOrg> getChildListById(List<BizOrg> originDataList, String id, boolean includeSelf) {
List<BizOrg> resultList = CollectionUtil.newArrayList();
execRecursionFindChild(originDataList, id, resultList);
if(includeSelf) {
BizOrg self = this.getById(originDataList, id);
if(ObjectUtil.isNotEmpty(self)) {
resultList.add(self);
}
}
return resultList;
}
@Override
public List<BizOrg> getParentListById(List<BizOrg> originDataList, String id, boolean includeSelf) {
List<BizOrg> resultList = CollectionUtil.newArrayList();
execRecursionFindParent(originDataList, id, resultList);
if(includeSelf) {
BizOrg self = this.getById(originDataList, id);
if(ObjectUtil.isNotEmpty(self)) {
resultList.add(self);
}
}
return resultList;
}
public void execRecursionFindChild(List<BizOrg> originDataList, String id, List<BizOrg> resultList) {
originDataList.forEach(item -> {
if(item.getParentId().equals(id)) {
resultList.add(item);
execRecursionFindChild(originDataList, item.getId(), resultList);
}
});
}
public void execRecursionFindParent(List<BizOrg> originDataList, String id, List<BizOrg> resultList) {
originDataList.forEach(item -> {
if(item.getId().equals(id)) {
BizOrg parent = this.getById(originDataList, item.getParentId());
if(ObjectUtil.isNotEmpty(parent)) {
resultList.add(parent);
}
execRecursionFindParent(originDataList, item.getParentId(), resultList);
}
});
}
@Override
public BizOrg getById(List<BizOrg> originDataList, String id) {
int index = CollStreamUtil.toList(originDataList, BizOrg::getId).indexOf(id);
return index == -1?null:originDataList.get(index);
}
@Override
public BizOrg getParentById(List<BizOrg> originDataList, String id) {
BizOrg self = this.getById(originDataList, id);
return ObjectUtil.isNotEmpty(self)?self:this.getById(originDataList, self.getParentId());
}
@Override
public BizOrg getChildById(List<BizOrg> originDataList, String id) {
int index = CollStreamUtil.toList(originDataList, BizOrg::getParentId).indexOf(id);
return index == -1?null:originDataList.get(index);
}
}

View File

@@ -0,0 +1,164 @@
package mjkf.xinke.biz.modular.position.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import mjkf.xinke.biz.modular.position.entity.BizPosition;
import mjkf.xinke.biz.modular.position.param.*;
import mjkf.xinke.biz.modular.position.service.BizPositionService;
import mjkf.xinke.common.annotation.CommonLog;
import mjkf.xinke.common.pojo.CommonResult;
import mjkf.xinke.common.pojo.CommonValidList;
import javax.annotation.Resource;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.util.List;
/**
* 岗位控制器
*
*
* @date 2022/4/25 20:40
*/
@Api(tags = "岗位控制器")
@ApiSupport(author = "SNOWY_TEAM", order = 2)
@RestController
@Validated
public class BizPositionController {
@Resource
private BizPositionService bizPositionService;
/**
* 获取岗位分页
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 1)
@ApiOperation("获取岗位分页")
@SaCheckPermission("/biz/position/page")
@GetMapping("/biz/position/page")
public CommonResult<Page<BizPosition>> page(BizPositionPageParam bizPositionPageParam) {
return CommonResult.data(bizPositionService.page(bizPositionPageParam));
}
/**
* 添加岗位
*
*
* @date 2022/4/24 20:47
*/
@ApiOperationSupport(order = 2)
@ApiOperation("添加岗位")
@CommonLog("添加岗位")
@SaCheckPermission("/biz/position/add")
@PostMapping("/biz/position/add")
public CommonResult<String> add(@RequestBody @Valid BizPositionAddParam bizPositionAddParam) {
bizPositionService.add(bizPositionAddParam);
return CommonResult.ok();
}
/**
* 编辑岗位
*
*
* @date 2022/4/24 20:47
*/
@ApiOperationSupport(order = 3)
@ApiOperation("编辑岗位")
@CommonLog("编辑岗位")
@SaCheckPermission("/biz/position/edit")
@PostMapping("/biz/position/edit")
public CommonResult<String> edit(@RequestBody @Valid BizPositionEditParam bizPositionEditParam) {
bizPositionService.edit(bizPositionEditParam);
return CommonResult.ok();
}
/**
* 删除岗位
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 4)
@ApiOperation("删除岗位")
@CommonLog("删除岗位")
@SaCheckPermission("/biz/position/delete")
@PostMapping("/biz/position/delete")
public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
CommonValidList<BizPositionIdParam> bizPositionIdParamList) {
bizPositionService.delete(bizPositionIdParamList);
return CommonResult.ok();
}
/**
* 获取岗位详情
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 5)
@ApiOperation("获取岗位详情")
@SaCheckPermission("/biz/position/detail")
@GetMapping("/biz/position/detail")
public CommonResult<BizPosition> detail(@Valid BizPositionIdParam bizPositionIdParam) {
return CommonResult.data(bizPositionService.detail(bizPositionIdParam));
}
/* ====岗位部分所需要用到的选择器==== */
/**
* 获取组织树选择器
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 6)
@ApiOperation("获取组织树选择器")
@SaCheckPermission("/biz/position/orgTreeSelector")
@GetMapping("/biz/position/orgTreeSelector")
public CommonResult<List<Tree<String>>> orgTreeSelector() {
return CommonResult.data(bizPositionService.orgTreeSelector());
}
/**
* 获取岗位选择器
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 7)
@ApiOperation("获取岗位选择器")
@SaCheckPermission("/biz/position/positionSelector")
@GetMapping("/biz/position/positionSelector")
public CommonResult<Page<BizPosition>> positionSelector(BizPositionSelectorPositionParam bizPositionSelectorPositionParam) {
return CommonResult.data(bizPositionService.positionSelector(bizPositionSelectorPositionParam));
}
/**
* 获取岗位动态字段的配置
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 8)
@ApiOperation("获取岗位动态字段的配置")
@SaCheckPermission("/biz/position/dynamicFieldConfigList")
@GetMapping("/biz/position/dynamicFieldConfigList")
public CommonResult<List<JSONObject>> dynamicFieldConfigList() {
return CommonResult.data(bizPositionService.dynamicFieldConfigList());
}
}

View File

@@ -0,0 +1,56 @@
package mjkf.xinke.biz.modular.position.entity;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fhs.core.trans.vo.TransPojo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import mjkf.xinke.common.pojo.CommonEntity;
/**
* 岗位实体
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
@TableName("SYS_POSITION")
public class BizPosition extends CommonEntity implements TransPojo {
/** id */
@ApiModelProperty(value = "id", position = 1)
private String id;
/** 租户id */
@ApiModelProperty(value = "租户id", position = 2)
private String tenantId;
/** 机构id */
@ApiModelProperty(value = "机构id", position = 3)
private String orgId;
/** 名称 */
@ApiModelProperty(value = "名称", position = 4)
private String name;
/** 编码 */
@ApiModelProperty(value = "编码", position = 5)
private String code;
/** 分类 */
@ApiModelProperty(value = "分类", position = 6)
private String category;
/** 排序码 */
@ApiModelProperty(value = "排序码", position = 7)
private Integer sortCode;
/** 扩展信息 */
@ApiModelProperty(value = "扩展信息", position = 8)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String extJson;
}

View File

@@ -0,0 +1,37 @@
package mjkf.xinke.biz.modular.position.enums;
import lombok.Getter;
import mjkf.xinke.common.exception.CommonException;
/**
* 岗位分类枚举
*
*
* @date 2022/4/21 19:56
**/
@Getter
public enum BizPositionCategoryEnum {
/** 高层 */
HIGH("HIGH"),
/** 中层 */
MIDDLE("MIDDLE"),
/** 基层 */
LOW("LOW");
private final String value;
BizPositionCategoryEnum(String value) {
this.value = value;
}
public static void validate(String value) {
boolean flag = HIGH.getValue().equals(value) || MIDDLE.getValue().equals(value) || LOW.getValue().equals(value);
if(!flag) {
throw new CommonException("不支持的岗位分类:{}", value);
}
}
}

View File

@@ -0,0 +1,14 @@
package mjkf.xinke.biz.modular.position.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import mjkf.xinke.biz.modular.position.entity.BizPosition;
/**
* 岗位Mapper接口
*
*
* @date 2022/4/21 18:37
**/
public interface BizPositionMapper extends BaseMapper<BizPosition> {
}

View File

@@ -0,0 +1,6 @@
<?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="mjkf.xinke.biz.modular.position.mapper.BizPositionMapper">
</mapper>

View File

@@ -0,0 +1,44 @@
package mjkf.xinke.biz.modular.position.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 岗位添加参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizPositionAddParam {
/** 机构id */
@ApiModelProperty(value = "机构id", required = true, position = 1)
@NotBlank(message = "orgId不能为空")
private String orgId;
/** 名称 */
@ApiModelProperty(value = "名称", required = true, position = 2)
@NotBlank(message = "name不能为空")
private String name;
/** 分类 */
@ApiModelProperty(value = "分类", required = true, position = 3)
@NotBlank(message = "category不能为空")
private String category;
/** 排序码 */
@ApiModelProperty(value = "排序码", required = true, position = 4)
@NotNull(message = "sortCode不能为空")
private Integer sortCode;
/** 扩展JSON */
@ApiModelProperty(value = "扩展JSON", position = 5)
private String extJson;
}

View File

@@ -0,0 +1,49 @@
package mjkf.xinke.biz.modular.position.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 岗位编辑参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizPositionEditParam {
/** id */
@ApiModelProperty(value = "id", required = true, position = 1)
@NotBlank(message = "id不能为空")
private String id;
/** 机构id */
@ApiModelProperty(value = "机构id", required = true, position = 2)
@NotBlank(message = "orgId不能为空")
private String orgId;
/** 名称 */
@ApiModelProperty(value = "名称", required = true, position = 3)
@NotBlank(message = "name不能为空")
private String name;
/** 分类 */
@ApiModelProperty(value = "分类", required = true, position = 4)
@NotBlank(message = "category不能为空")
private String category;
/** 排序码 */
@ApiModelProperty(value = "排序码", required = true, position = 5)
@NotNull(message = "sortCode不能为空")
private Integer sortCode;
/** 扩展JSON */
@ApiModelProperty(value = "扩展JSON", position = 6)
private String extJson;
}

View File

@@ -0,0 +1,24 @@
package mjkf.xinke.biz.modular.position.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* 岗位Id参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizPositionIdParam {
/** id */
@ApiModelProperty(value = "id", required = true)
@NotBlank(message = "id不能为空")
private String id;
}

View File

@@ -0,0 +1,45 @@
package mjkf.xinke.biz.modular.position.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 岗位查询参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizPositionPageParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 排序字段 */
@ApiModelProperty(value = "排序字段字段驼峰名称userName")
private String sortField;
/** 排序方式 */
@ApiModelProperty(value = "排序方式升序ASCEND降序DESCEND")
private String sortOrder;
/** 机构id */
@ApiModelProperty(value = "机构id")
private String orgId;
/** 岗位分类 */
@ApiModelProperty(value = "岗位分类")
private String category;
/** 名称关键词 */
@ApiModelProperty(value = "名称关键词")
private String searchKey;
}

View File

@@ -0,0 +1,33 @@
package mjkf.xinke.biz.modular.position.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 岗位选择器参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizPositionSelectorPositionParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 机构id */
@ApiModelProperty(value = "机构id")
private String orgId;
/** 名称关键词 */
@ApiModelProperty(value = "名称关键词")
private String searchKey;
}

View File

@@ -0,0 +1,102 @@
package mjkf.xinke.biz.modular.position.service;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import mjkf.xinke.biz.modular.position.entity.BizPosition;
import mjkf.xinke.biz.modular.position.param.*;
import java.util.List;
/**
* 岗位Service接口
*
*
* @date 2022/4/21 18:35
**/
public interface BizPositionService extends IService<BizPosition> {
/**
* 获取岗位分页
*
*
* @date 2022/4/24 20:08
*/
Page<BizPosition> page(BizPositionPageParam bizPositionPageParam);
/**
* 添加岗位
*
*
* @date 2022/4/24 20:48
*/
void add(BizPositionAddParam bizPositionAddParam);
/**
* 编辑岗位
*
*
* @date 2022/4/24 21:13
*/
void edit(BizPositionEditParam bizPositionEditParam);
/**
* 删除岗位
*
*
* @date 2022/4/24 21:18
*/
void delete(List<BizPositionIdParam> bizPositionIdParamList);
/**
* 获取岗位详情
*
*
* @date 2022/4/24 21:18
*/
BizPosition detail(BizPositionIdParam bizPositionIdParam);
/**
* 获取岗位详情
*
*
* @date 2022/7/25 19:42
**/
BizPosition queryEntity(String id);
/**
* 根据机构id和岗位名称获取岗位id有则返回无则创建
*
*
* @date 2022/8/15 14:55
**/
String getPositionIdByPositionNameWithCreate(String orgId, String positionName);
/* ====岗位部分所需要用到的选择器==== */
/**
* 获取机构树选择器
*
*
* @date 2022/4/24 20:08
*/
List<Tree<String>> orgTreeSelector();
/**
* 获取岗位选择器
*
*
* @date 2022/4/24 20:08
*/
Page<BizPosition> positionSelector(BizPositionSelectorPositionParam bizPositionSelectorPositionParam);
/**
* 获取动态字段配置
*
*
* @date 2022/8/5 323
*/
List<JSONObject> dynamicFieldConfigList();
}

View File

@@ -0,0 +1,266 @@
package mjkf.xinke.biz.modular.position.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import mjkf.xinke.auth.core.util.StpLoginUserUtil;
import mjkf.xinke.biz.core.enums.BizDataTypeEnum;
import mjkf.xinke.biz.modular.org.entity.BizOrg;
import mjkf.xinke.biz.modular.org.service.BizOrgService;
import mjkf.xinke.biz.modular.position.entity.BizPosition;
import mjkf.xinke.biz.modular.position.enums.BizPositionCategoryEnum;
import mjkf.xinke.biz.modular.position.mapper.BizPositionMapper;
import mjkf.xinke.biz.modular.position.param.*;
import mjkf.xinke.biz.modular.position.service.BizPositionService;
import mjkf.xinke.biz.modular.user.entity.BizUser;
import mjkf.xinke.biz.modular.user.service.BizUserService;
import mjkf.xinke.common.enums.CommonSortOrderEnum;
import mjkf.xinke.common.exception.CommonException;
import mjkf.xinke.common.listener.CommonDataChangeEventCenter;
import mjkf.xinke.common.page.CommonPageRequest;
import mjkf.xinke.dbs.api.DbsApi;
import mjkf.xinke.dev.api.DevDfcApi;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 岗位Service接口实现类
*
*
* @date 2022/2/23 18:43
**/
@Service
public class BizPositionServiceImpl extends ServiceImpl<BizPositionMapper, BizPosition> implements BizPositionService {
@Resource
private BizOrgService bizOrgService;
@Resource
private BizUserService bizUserService;
@Resource
private DbsApi dbsApi;
@Resource
private DevDfcApi devDfcApi;
@Override
public Page<BizPosition> page(BizPositionPageParam bizPositionPageParam) {
QueryWrapper<BizPosition> queryWrapper = new QueryWrapper<>();
// 查询部分字段
queryWrapper.lambda().select(BizPosition::getId, BizPosition::getOrgId, BizPosition::getName,
BizPosition::getCategory, BizPosition::getSortCode, BizPosition::getExtJson);
if(ObjectUtil.isNotEmpty(bizPositionPageParam.getOrgId())) {
queryWrapper.lambda().eq(BizPosition::getOrgId, bizPositionPageParam.getOrgId());
}
if(ObjectUtil.isNotEmpty(bizPositionPageParam.getCategory())) {
queryWrapper.lambda().eq(BizPosition::getCategory, bizPositionPageParam.getCategory());
}
if(ObjectUtil.isAllNotEmpty(bizPositionPageParam.getSortField(), bizPositionPageParam.getSortOrder())) {
CommonSortOrderEnum.validate(bizPositionPageParam.getSortOrder());
queryWrapper.orderBy(true, bizPositionPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
StrUtil.toUnderlineCase(bizPositionPageParam.getSortField()));
} else {
queryWrapper.lambda().orderByAsc(BizPosition::getSortCode);
}
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
queryWrapper.lambda().in(BizPosition::getOrgId, loginUserDataScope);
} else {
return new Page<>();
}
return this.page(CommonPageRequest.defaultPage(), queryWrapper);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void add(BizPositionAddParam bizPositionAddParam) {
BizPositionCategoryEnum.validate(bizPositionAddParam.getCategory());
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.contains(bizPositionAddParam.getOrgId())) {
throw new CommonException("您没有权限在该机构下增加岗位机构id{}", bizPositionAddParam.getOrgId());
}
} else {
throw new CommonException("您没有权限在该机构下增加岗位机构id{}", bizPositionAddParam.getOrgId());
}
BizPosition bizPosition = BeanUtil.toBean(bizPositionAddParam, BizPosition.class);
boolean repeatName = this.count(new LambdaQueryWrapper<BizPosition>().eq(BizPosition::getOrgId, bizPosition.getOrgId())
.eq(BizPosition::getName, bizPosition.getName())) > 0;
if(repeatName) {
throw new CommonException("同机构下存在重复的岗位,名称为:{}", bizPosition.getName());
}
bizPosition.setCode(RandomUtil.randomString(10));
this.save(bizPosition);
// 发布增加事件
CommonDataChangeEventCenter.doAddWithData(BizDataTypeEnum.POSITION.getValue(), JSONUtil.createArray().put(bizPosition));
}
@Transactional(rollbackFor = Exception.class)
@Override
public void edit(BizPositionEditParam bizPositionEditParam) {
BizPositionCategoryEnum.validate(bizPositionEditParam.getCategory());
BizPosition bizPosition = this.queryEntity(bizPositionEditParam.getId());
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.contains(bizPositionEditParam.getOrgId())) {
throw new CommonException("您没有权限编辑该机构下的岗位机构id{}", bizPositionEditParam.getOrgId());
}
} else {
if(!bizPositionEditParam.getId().equals(StpUtil.getLoginIdAsString())) {
throw new CommonException("您没有权限编辑该机构下的岗位机构id{}", bizPositionEditParam.getOrgId());
}
}
BeanUtil.copyProperties(bizPositionEditParam, bizPosition);
boolean repeatName = this.count(new LambdaQueryWrapper<BizPosition>().eq(BizPosition::getOrgId, bizPosition.getOrgId())
.eq(BizPosition::getName, bizPosition.getName()).ne(BizPosition::getId, bizPosition.getId())) > 0;
if(repeatName) {
throw new CommonException("同机构下存在重复的岗位,名称为:{}", bizPosition.getName());
}
this.updateById(bizPosition);
// 发布更新事件
CommonDataChangeEventCenter.doUpdateWithData(BizDataTypeEnum.POSITION.getValue(), JSONUtil.createArray().put(bizPosition));
}
@Transactional(rollbackFor = Exception.class)
@Override
public void delete(List<BizPositionIdParam> bizPositionIdParamList) {
List<String> positionIdList = CollStreamUtil.toList(bizPositionIdParamList, BizPositionIdParam::getId);
if(ObjectUtil.isNotEmpty(positionIdList)) {
// 获取这些岗位的的机构id集合
Set<String> positionOrgIdList = this.listByIds(positionIdList).stream().map(BizPosition::getOrgId).collect(Collectors.toSet());
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.containsAll(positionOrgIdList)) {
throw new CommonException("您没有权限删除这些机构下的岗位机构id{}", positionOrgIdList);
}
} else {
throw new CommonException("您没有权限删除这些机构下的岗位机构id{}", positionOrgIdList);
}
// 岗位下有人不能删除(直属岗位)
boolean hasOrgUser = bizUserService.count(new LambdaQueryWrapper<BizUser>().in(BizUser::getPositionId, positionIdList)) > 0;
if(hasOrgUser) {
throw new CommonException("请先删除岗位下的用户");
}
// 岗位下有人不能删除(兼任岗位)
List<String> positionJsonList = bizUserService.list(new LambdaQueryWrapper<BizUser>()
.isNotNull(BizUser::getPositionJson)).stream().map(BizUser::getPositionJson).collect(Collectors.toList());
if(ObjectUtil.isNotEmpty(positionJsonList)) {
List<String> extPositionIdList = CollectionUtil.newArrayList();
positionJsonList.forEach(positionJson -> JSONUtil.toList(JSONUtil.parseArray(positionJson), JSONObject.class)
.forEach(jsonObject -> extPositionIdList.add(jsonObject.getStr("positionId"))));
boolean hasPositionUser = CollectionUtil.intersectionDistinct(positionIdList, CollectionUtil.removeNull(extPositionIdList)).size() > 0;
if(hasPositionUser) {
throw new CommonException("请先删除岗位下的用户");
}
}
// 执行删除
this.removeByIds(positionIdList);
// 发布删除事件
CommonDataChangeEventCenter.doDeleteWithDataId(BizDataTypeEnum.POSITION.getValue(), positionIdList);
}
}
@Override
public BizPosition detail(BizPositionIdParam bizPositionIdParam) {
return this.queryEntity(bizPositionIdParam.getId());
}
@Override
public BizPosition queryEntity(String id) {
BizPosition bizPosition = this.getById(id);
if(ObjectUtil.isEmpty(bizPosition)) {
throw new CommonException("岗位不存在id值为{}", id);
}
return bizPosition;
}
@Override
public String getPositionIdByPositionNameWithCreate(String orgId, String positionName) {
return null;
}
/* ====岗位部分所需要用到的选择器==== */
@Override
public List<Tree<String>> orgTreeSelector() {
LambdaQueryWrapper<BizOrg> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
// 定义机构集合
Set<BizOrg> bizOrgSet = CollectionUtil.newHashSet();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
// 获取所有机构
List<BizOrg> allOrgList = bizOrgService.list();
loginUserDataScope.forEach(orgId -> bizOrgSet.addAll(bizOrgService.getParentListById(allOrgList, orgId, true)));
List<String> loginUserDataScopeFullList = bizOrgSet.stream().map(BizOrg::getId).collect(Collectors.toList());
lambdaQueryWrapper.in(BizOrg::getId, loginUserDataScopeFullList);
} else {
return CollectionUtil.newArrayList();
}
lambdaQueryWrapper.orderByAsc(BizOrg::getSortCode);
List<BizOrg> bizOrgList = bizOrgService.list(lambdaQueryWrapper);
List<TreeNode<String>> treeNodeList = bizOrgList.stream().map(bizOrg ->
new TreeNode<>(bizOrg.getId(), bizOrg.getParentId(), bizOrg.getName(), bizOrg.getSortCode()))
.collect(Collectors.toList());
return TreeUtil.build(treeNodeList, "0");
}
@Override
public Page<BizPosition> positionSelector(BizPositionSelectorPositionParam bizPositionSelectorPositionParam) {
LambdaQueryWrapper<BizPosition> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
lambdaQueryWrapper.in(BizPosition::getOrgId, loginUserDataScope);
} else {
return new Page<>();
}
// 查询部分字段
lambdaQueryWrapper.select(BizPosition::getId, BizPosition::getOrgId, BizPosition::getName,
BizPosition::getCategory, BizPosition::getSortCode);
if(ObjectUtil.isNotEmpty(bizPositionSelectorPositionParam.getOrgId())) {
lambdaQueryWrapper.eq(BizPosition::getOrgId, bizPositionSelectorPositionParam.getOrgId());
}
if(ObjectUtil.isNotEmpty(bizPositionSelectorPositionParam.getSearchKey())) {
lambdaQueryWrapper.like(BizPosition::getName, bizPositionSelectorPositionParam.getSearchKey());
}
lambdaQueryWrapper.orderByAsc(BizPosition::getSortCode);
return this.page(CommonPageRequest.defaultPage(), lambdaQueryWrapper);
}
@Override
public List<JSONObject> dynamicFieldConfigList() {
String currentDataSourceId = dbsApi.getCurrentDataSourceId();
String tableName = AnnotationUtil.getAnnotationValue(BizPosition.class, TableName.class);
return devDfcApi.getTableFieldList(currentDataSourceId, tableName);
}
}

View File

@@ -0,0 +1,320 @@
package mjkf.xinke.biz.modular.user.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import mjkf.xinke.biz.modular.org.entity.BizOrg;
import mjkf.xinke.biz.modular.position.entity.BizPosition;
import mjkf.xinke.biz.modular.user.entity.BizUser;
import mjkf.xinke.biz.modular.user.param.*;
import mjkf.xinke.biz.modular.user.result.BizUserRoleResult;
import mjkf.xinke.biz.modular.user.service.BizUserService;
import mjkf.xinke.common.annotation.CommonLog;
import mjkf.xinke.common.pojo.CommonResult;
import mjkf.xinke.common.pojo.CommonValidList;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.io.IOException;
import java.util.List;
/**
* 人员控制器
*
*
* @date 2022/4/22 9:34
**/
@Api(tags = "人员控制器")
@ApiSupport(author = "SNOWY_TEAM", order = 9)
@RestController
@Validated
public class BizUserController {
@Resource
private BizUserService bizUserService;
/**
* 获取人员分页
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 1)
@ApiOperation("获取人员分页")
@SaCheckPermission("/biz/user/page")
@GetMapping("/biz/user/page")
public CommonResult<Page<BizUser>> page(BizUserPageParam bizUserPageParam) {
return CommonResult.data(bizUserService.page(bizUserPageParam));
}
/**
* 添加人员
*
*
* @date 2022/4/24 20:47
*/
@ApiOperationSupport(order = 2)
@ApiOperation("添加人员")
@CommonLog("添加人员")
@SaCheckPermission("/biz/user/add")
@PostMapping("/biz/user/add")
public CommonResult<String> add(@RequestBody @Valid BizUserAddParam bizUserAddParam) {
bizUserService.add(bizUserAddParam);
return CommonResult.ok();
}
/**
* 编辑人员
*
*
* @date 2022/4/24 20:47
*/
@ApiOperationSupport(order = 3)
@ApiOperation("编辑人员")
@CommonLog("编辑人员")
@SaCheckPermission("/biz/user/edit")
@PostMapping("/biz/user/edit")
public CommonResult<String> edit(@RequestBody @Valid BizUserEditParam bizUserEditParam) {
bizUserService.edit(bizUserEditParam);
return CommonResult.ok();
}
/**
* 删除人员
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 4)
@ApiOperation("删除人员")
@CommonLog("删除人员")
@SaCheckPermission("/biz/user/delete")
@PostMapping("/biz/user/delete")
public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
CommonValidList<BizUserIdParam> bizUserIdParamList) {
bizUserService.delete(bizUserIdParamList);
return CommonResult.ok();
}
/**
* 获取人员详情
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 5)
@ApiOperation("获取人员详情")
@SaCheckPermission("/biz/user/detail")
@GetMapping("/biz/user/detail")
public CommonResult<BizUser> detail(@Valid BizUserIdParam bizUserIdParam) {
return CommonResult.data(bizUserService.detail(bizUserIdParam));
}
/**
* 禁用人员
*
*
* @date 2021/10/13 14:01
**/
@ApiOperationSupport(order = 6)
@ApiOperation("禁用人员")
@CommonLog("禁用人员")
@SaCheckPermission("/biz/user/disableUser")
@PostMapping("/biz/user/disableUser")
public CommonResult<String> disableUser(@RequestBody BizUserIdParam bizUserIdParam) {
bizUserService.disableUser(bizUserIdParam);
return CommonResult.ok();
}
/**
* 启用人员
*
*
* @date 2021/10/13 14:01
**/
@ApiOperationSupport(order = 7)
@ApiOperation("启用人员")
@CommonLog("启用人员")
@SaCheckPermission("/biz/user/enableUser")
@PostMapping("/biz/user/enableUser")
public CommonResult<String> enableUser(@RequestBody @Valid BizUserIdParam bizUserIdParam) {
bizUserService.enableUser(bizUserIdParam);
return CommonResult.ok();
}
/**
* 重置人员密码
*
*
* @date 2021/10/13 14:01
**/
@ApiOperationSupport(order = 8)
@ApiOperation("重置人员密码")
@CommonLog("重置人员密码")
@SaCheckPermission("/biz/user/resetPassword")
@PostMapping("/biz/user/resetPassword")
public CommonResult<String> resetPassword(@RequestBody @Valid BizUserIdParam bizUserIdParam) {
bizUserService.resetPassword(bizUserIdParam);
return CommonResult.ok();
}
/**
* 人员拥有角色
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 9)
@ApiOperation("获取人员拥有角色")
@SaCheckPermission("/biz/user/ownRole")
@GetMapping("/biz/user/ownRole")
public CommonResult<List<String>> ownRole(@Valid BizUserIdParam bizUserIdParam) {
return CommonResult.data(bizUserService.ownRole(bizUserIdParam));
}
/**
* 给人员授权角色
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 10)
@ApiOperation("给人员授权角色")
@CommonLog("给人员授权角色")
@SaCheckPermission("/biz/user/grantRole")
@PostMapping("/biz/user/grantRole")
public CommonResult<String> grantRole(@RequestBody @Valid BizUserGrantRoleParam bizUserGrantRoleParam) {
bizUserService.grantRole(bizUserGrantRoleParam);
return CommonResult.ok();
}
/**
* 人员导出
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 11)
@ApiOperation("人员导出")
@CommonLog("人员导出")
@SaCheckPermission("/biz/user/export")
@GetMapping(value = "/biz/user/export", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void exportUser(BizUserExportParam bizUserExportParam, HttpServletResponse response) throws IOException {
bizUserService.exportUser(bizUserExportParam, response);
}
/**
* 按模板导出人员个人信息
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 12)
@ApiOperation("导出人员个人信息")
@CommonLog("导出人员个人信息")
@SaCheckPermission("/biz/user/exportUserInfo")
@GetMapping(value = "/biz/user/exportUserInfo", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void exportUserInfo(BizUserIdParam bizUserIdParam, HttpServletResponse response) throws IOException {
bizUserService.exportUserInfo(bizUserIdParam, response);
}
/* ====人员部分所需要用到的选择器==== */
/**
* 获取机构树选择器
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 13)
@ApiOperation("获取机构树选择器")
@SaCheckPermission("/biz/user/orgTreeSelector")
@GetMapping("/biz/user/orgTreeSelector")
public CommonResult<List<Tree<String>>> orgTreeSelector() {
return CommonResult.data(bizUserService.orgTreeSelector());
}
/**
* 获取机构列表选择器
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 14)
@ApiOperation("获取机构列表选择器")
@SaCheckPermission("/biz/user/orgListSelector")
@GetMapping("/biz/user/orgListSelector")
public CommonResult<Page<BizOrg>> orgListSelector(BizUserSelectorOrgListParam bizUserSelectorOrgListParam) {
return CommonResult.data(bizUserService.orgListSelector(bizUserSelectorOrgListParam));
}
/**
* 获取岗位选择器
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 15)
@ApiOperation("获取岗位选择器")
@SaCheckPermission("/biz/user/positionSelector")
@GetMapping("/biz/user/positionSelector")
public CommonResult<Page<BizPosition>> positionSelector(BizUserSelectorPositionParam bizUserSelectorPositionParam) {
return CommonResult.data(bizUserService.positionSelector(bizUserSelectorPositionParam));
}
/**
* 获取角色选择器
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 16)
@ApiOperation("获取角色选择器")
@SaCheckPermission("/biz/user/roleSelector")
@GetMapping("/biz/user/roleSelector")
public CommonResult<Page<BizUserRoleResult>> roleSelector(BizUserSelectorRoleParam bizUserSelectorRoleParam) {
return CommonResult.data(bizUserService.roleSelector(bizUserSelectorRoleParam));
}
/**
* 获取人员选择器
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 17)
@ApiOperation("获取人员选择器")
@SaCheckPermission("/biz/user/userSelector")
@GetMapping("/biz/user/userSelector")
public CommonResult<Page<BizUser>> userSelector(BizUserSelectorUserParam bizUserSelectorUserParam) {
return CommonResult.data(bizUserService.userSelector(bizUserSelectorUserParam));
}
/**
* 获取人员动态字段的配置
*
*
* @date 2022/4/24 20:00
*/
@ApiOperationSupport(order = 18)
@ApiOperation("获取人员动态字段的配置")
@SaCheckPermission("/biz/user/dynamicFieldConfigList")
@GetMapping("/biz/user/dynamicFieldConfigList")
public CommonResult<List<JSONObject>> dynamicFieldConfigList() {
return CommonResult.data(bizUserService.dynamicFieldConfigList());
}
}

View File

@@ -0,0 +1,271 @@
package mjkf.xinke.biz.modular.user.entity;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fhs.core.trans.anno.Trans;
import com.fhs.core.trans.constant.TransType;
import com.fhs.core.trans.vo.TransPojo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import mjkf.xinke.biz.modular.org.entity.BizOrg;
import mjkf.xinke.biz.modular.position.entity.BizPosition;
import mjkf.xinke.common.handler.CommonSm4CbcTypeHandler;
import mjkf.xinke.common.pojo.CommonEntity;
import java.util.Date;
/**
* 人员实体
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
@TableName(value = "SYS_USER", autoResultMap = true)
public class BizUser extends CommonEntity implements TransPojo {
/** id */
@TableId
@ApiModelProperty(value = "id", position = 1)
private String id;
/** 租户id */
@ApiModelProperty(value = "租户id", position = 2)
private String tenantId;
/** 头像 */
@ApiModelProperty(value = "头像", position = 3)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String avatar;
/** 签名 */
@ApiModelProperty(value = "签名", position = 4)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String signature;
/** 账号 */
@ApiModelProperty(value = "账号", position = 5)
private String account;
/** 密码 */
@ApiModelProperty(value = "密码", position = 6)
private String password;
/** 姓名 */
@ApiModelProperty(value = "姓名", position = 7)
private String name;
/** 昵称 */
@ApiModelProperty(value = "昵称", position = 8)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String nickname;
/** 性别 */
@ApiModelProperty(value = "性别", position = 9)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
@Trans(type = TransType.DICTIONARY, key = "GENDER")
private String gender;
/** 年龄 */
@ApiModelProperty(value = "年龄", position = 10)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String age;
/** 出生日期 */
@ApiModelProperty(value = "出生日期", position = 11)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String birthday;
/** 民族 */
@ApiModelProperty(value = "民族", position = 12)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String nation;
/** 籍贯 */
@ApiModelProperty(value = "籍贯", position = 13)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String nativePlace;
/** 家庭住址 */
@ApiModelProperty(value = "家庭住址", position = 14)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String homeAddress;
/** 通信地址 */
@ApiModelProperty(value = "通信地址", position = 15)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String mailingAddress;
/** 证件类型 */
@ApiModelProperty(value = "证件类型", position = 16)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String idCardType;
/** 证件号码 */
@ApiModelProperty(value = "证件号码", position = 17)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED, typeHandler = CommonSm4CbcTypeHandler.class)
private String idCardNumber;
/** 文化程度 */
@ApiModelProperty(value = "文化程度", position = 18)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String cultureLevel;
/** 政治面貌 */
@ApiModelProperty(value = "政治面貌", position = 19)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String politicalOutlook;
/** 毕业院校 */
@ApiModelProperty(value = "毕业院校", position = 20)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String college;
/** 学历 */
@ApiModelProperty(value = "学历", position = 21)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String education;
/** 学制 */
@ApiModelProperty(value = "学制", position = 22)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String eduLength;
/** 学位 */
@ApiModelProperty(value = "学位", position = 23)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String degree;
/** 手机 */
@ApiModelProperty(value = "手机", position = 24)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED, typeHandler = CommonSm4CbcTypeHandler.class)
private String phone;
/** 邮箱 */
@ApiModelProperty(value = "邮箱", position = 25)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String email;
/** 家庭电话 */
@ApiModelProperty(value = "家庭电话", position = 26)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String homeTel;
/** 办公电话 */
@ApiModelProperty(value = "办公电话", position = 27)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String officeTel;
/** 紧急联系人 */
@ApiModelProperty(value = "紧急联系人", position = 28)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String emergencyContact;
/** 紧急联系人电话 */
@ApiModelProperty(value = "紧急联系人电话", position = 29)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED, typeHandler = CommonSm4CbcTypeHandler.class)
private String emergencyPhone;
/** 紧急联系人地址 */
@ApiModelProperty(value = "紧急联系人地址", position = 30)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String emergencyAddress;
/** 员工编号 */
@ApiModelProperty(value = "员工编号", position = 31)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String empNo;
/** 入职日期 */
@ApiModelProperty(value = "入职日期", position = 32)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String entryDate;
/** 机构id */
@ApiModelProperty(value = "机构id", position = 33)
@Trans(type = TransType.SIMPLE, target = BizOrg.class, fields = "name", alias = "org", ref = "orgName")
private String orgId;
/** 岗位id */
@ApiModelProperty(value = "岗位id", position = 34)
@Trans(type = TransType.SIMPLE, target = BizPosition.class, fields = "name", alias = "position", ref = "positionName")
private String positionId;
/** 职级 */
@ApiModelProperty(value = "职级", position = 35)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String positionLevel;
/** 主管id */
@ApiModelProperty(value = "主管id", position = 36)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
@Trans(type = TransType.SIMPLE, target = BizUser.class, fields = "name", alias = "director", ref = "directorName")
private String directorId;
/** 兼任信息 */
@ApiModelProperty(value = "兼任信息", position = 37)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String positionJson;
/** 上次登录ip */
@ApiModelProperty(value = "上次登录ip", position = 38)
private String lastLoginIp;
/** 上次登录地点 */
@ApiModelProperty(value = "上次登录地点", position = 39)
private String lastLoginAddress;
/** 上次登录时间 */
@ApiModelProperty(value = "上次登录时间", position = 40)
private Date lastLoginTime;
/** 上次登录设备 */
@ApiModelProperty(value = "上次登录设备", position = 41)
private String lastLoginDevice;
/** 最新登录ip */
@ApiModelProperty(value = "最新登录ip", position = 42)
private String latestLoginIp;
/** 最新登录地点 */
@ApiModelProperty(value = "最新登录地点", position = 43)
private String latestLoginAddress;
/** 最新登录时间 */
@ApiModelProperty(value = "最新登录时间", position = 44)
private Date latestLoginTime;
/** 最新登录设备 */
@ApiModelProperty(value = "最新登录设备", position = 45)
private String latestLoginDevice;
/** 用户状态 */
@ApiModelProperty(value = "用户状态", position = 46)
private String userStatus;
/** 排序码 */
@ApiModelProperty(value = "排序码", position = 47)
private Integer sortCode;
/** 扩展信息 */
@ApiModelProperty(value = "扩展信息", position = 48)
@TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
private String extJson;
@ApiModelProperty(value = "机构名称", position = 49)
@TableField(exist = false)
private String orgName;
@ApiModelProperty(value = "岗位名称", position = 50)
@TableField(exist = false)
private String positionName;
@ApiModelProperty(value = "主管名称", position = 51)
@TableField(exist = false)
private String directorName;
}

View File

@@ -0,0 +1,34 @@
package mjkf.xinke.biz.modular.user.enums;
import lombok.Getter;
import mjkf.xinke.common.exception.CommonException;
/**
* 角色分类枚举
*
*
* @date 2022/4/21 19:56
**/
@Getter
public enum BizRoleCategoryEnum {
/** 全局 */
GLOBAL("GLOBAL"),
/** 组织 */
ORG("ORG");
private final String value;
BizRoleCategoryEnum(String value) {
this.value = value;
}
public static void validate(String value) {
boolean flag = GLOBAL.getValue().equals(value) || ORG.getValue().equals(value);
if(!flag) {
throw new CommonException("不支持的角色分类:{}", value);
}
}
}

View File

@@ -0,0 +1,38 @@
package mjkf.xinke.biz.modular.user.enums;
import lombok.Getter;
import mjkf.xinke.common.exception.CommonException;
/**
* 人员状态枚举
*
*
* @date 2022/4/27 21:47
*/
@Getter
public enum BizUserStatusEnum {
/**
* 正常
*/
ENABLE("ENABLE"),
/**
* 停用
*/
DISABLED("DISABLED");
private final String value;
BizUserStatusEnum(String value) {
this.value = value;
}
public static void validate(String value) {
boolean flag = ENABLE.getValue().equals(value) || DISABLED.getValue().equals(value);
if(!flag) {
throw new CommonException("不支持的人员状态:{}", value);
}
}
}

View File

@@ -0,0 +1,15 @@
package mjkf.xinke.biz.modular.user.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import mjkf.xinke.biz.modular.user.entity.BizUser;
/**
* 人员Mapper接口
*
*
* @date 2022/4/21 18:37
**/
public interface BizUserMapper extends BaseMapper<BizUser> {
}

View File

@@ -0,0 +1,5 @@
<?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="mjkf.xinke.biz.modular.user.mapper.BizUserMapper">
</mapper>

View File

@@ -0,0 +1,167 @@
package mjkf.xinke.biz.modular.user.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* 人员添加参数
*
*
* @date 2022/7/26 15:36
**/
@Getter
@Setter
public class BizUserAddParam {
/** 账号 */
@ApiModelProperty(value = "账号", required = true, position = 1)
@NotBlank(message = "account不能为空")
private String account;
/** 姓名 */
@ApiModelProperty(value = "姓名", required = true, position = 2)
@NotBlank(message = "name不能为空")
private String name;
/** 机构id */
@ApiModelProperty(value = "机构id", required = true, position = 3)
@NotBlank(message = "orgId不能为空")
private String orgId;
/** 岗位id */
@ApiModelProperty(value = "岗位id", required = true, position = 4)
@NotBlank(message = "positionId不能为空")
private String positionId;
/** 岗级 */
@ApiModelProperty(value = "岗级", position = 5)
private String positionLevel;
/** 主管id */
@ApiModelProperty(value = "主管id", position = 6)
private String directorId;
/** 头像 */
@ApiModelProperty(value = "头像图片base64", position = 7)
private String avatar;
/** 签名 */
@ApiModelProperty(value = "签名图片base64", position = 8)
private String signature;
/** 昵称 */
@ApiModelProperty(value = "昵称", position = 9)
private String nickname;
/** 性别 */
@ApiModelProperty(value = "性别", position = 10)
private String gender;
/** 年龄 */
@ApiModelProperty(value = "年龄", position = 11)
private String age;
/** 出生日期 */
@ApiModelProperty(value = "出生日期", position = 12)
private String birthday;
/** 民族 */
@ApiModelProperty(value = "民族", position = 13)
private String nation;
/** 籍贯 */
@ApiModelProperty(value = "籍贯", position = 14)
private String nativePlace;
/** 家庭住址 */
@ApiModelProperty(value = "家庭住址", position = 15)
private String homeAddress;
/** 通信地址 */
@ApiModelProperty(value = "通信地址", position = 16)
private String mailingAddress;
/** 证件类型 */
@ApiModelProperty(value = "证件类型", position = 17)
private String idCardType;
/** 证件号码 */
@ApiModelProperty(value = "证件号码", position = 18)
private String idCardNumber;
/** 文化程度 */
@ApiModelProperty(value = "文化程度", position = 19)
private String cultureLevel;
/** 政治面貌 */
@ApiModelProperty(value = "政治面貌", position = 20)
private String politicalOutlook;
/** 毕业院校 */
@ApiModelProperty(value = "毕业院校", position = 21)
private String college;
/** 学历 */
@ApiModelProperty(value = "学历", position = 22)
private String education;
/** 学制 */
@ApiModelProperty(value = "学制", position = 23)
private String eduLength;
/** 学位 */
@ApiModelProperty(value = "学位", position = 24)
private String degree;
/** 手机 */
@ApiModelProperty(value = "手机", position = 25)
private String phone;
/** 邮箱 */
@ApiModelProperty(value = "邮箱", position = 26)
private String email;
/** 家庭电话 */
@ApiModelProperty(value = "家庭电话", position = 27)
private String homeTel;
/** 办公电话 */
@ApiModelProperty(value = "办公电话", position = 28)
private String officeTel;
/** 紧急联系人 */
@ApiModelProperty(value = "紧急联系人", position = 29)
private String emergencyContact;
/** 紧急联系人电话 */
@ApiModelProperty(value = "紧急联系人电话", position = 30)
private String emergencyPhone;
/** 紧急联系人地址 */
@ApiModelProperty(value = "紧急联系人地址", position = 31)
private String emergencyAddress;
/** 员工编号 */
@ApiModelProperty(value = "员工编号", position = 32)
private String empNo;
/** 入职日期 */
@ApiModelProperty(value = "员工编号", position = 33)
private String entryDate;
/** 兼任信息 */
@ApiModelProperty(value = "兼任信息", position = 34)
private String positionJson;
/** 排序码 */
@ApiModelProperty(value = "排序码", position = 35)
private Integer sortCode;
/** 扩展信息 */
@ApiModelProperty(value = "扩展信息", position = 36)
private String extJson;
}

View File

@@ -0,0 +1,172 @@
package mjkf.xinke.biz.modular.user.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* 人员编辑参数
*
*
* @date 2022/7/26 15:43
**/
@Getter
@Setter
public class BizUserEditParam {
/** id */
@ApiModelProperty(value = "id", required = true, position = 1)
@NotBlank(message = "id不能为空")
private String id;
/** 账号 */
@ApiModelProperty(value = "账号", required = true, position = 2)
@NotBlank(message = "account不能为空")
private String account;
/** 姓名 */
@ApiModelProperty(value = "姓名", required = true, position = 3)
@NotBlank(message = "name不能为空")
private String name;
/** 机构id */
@ApiModelProperty(value = "机构id", required = true, position = 4)
@NotBlank(message = "orgId不能为空")
private String orgId;
/** 岗位id */
@ApiModelProperty(value = "岗位id", required = true, position = 5)
@NotBlank(message = "positionId不能为空")
private String positionId;
/** 岗级 */
@ApiModelProperty(value = "岗级", position = 6)
private String positionLevel;
/** 主管id */
@ApiModelProperty(value = "主管id", position = 7)
private String directorId;
/** 头像 */
@ApiModelProperty(value = "头像图片base64", position = 8)
private String avatar;
/** 签名 */
@ApiModelProperty(value = "签名图片base64", position = 9)
private String signature;
/** 昵称 */
@ApiModelProperty(value = "昵称", position = 10)
private String nickname;
/** 性别 */
@ApiModelProperty(value = "性别", position = 11)
private String gender;
/** 年龄 */
@ApiModelProperty(value = "年龄", position = 12)
private String age;
/** 出生日期 */
@ApiModelProperty(value = "出生日期", position = 13)
private String birthday;
/** 民族 */
@ApiModelProperty(value = "民族", position = 14)
private String nation;
/** 籍贯 */
@ApiModelProperty(value = "籍贯", position = 15)
private String nativePlace;
/** 家庭住址 */
@ApiModelProperty(value = "家庭住址", position = 16)
private String homeAddress;
/** 通信地址 */
@ApiModelProperty(value = "通信地址", position = 17)
private String mailingAddress;
/** 证件类型 */
@ApiModelProperty(value = "证件类型", position = 18)
private String idCardType;
/** 证件号码 */
@ApiModelProperty(value = "证件号码", position = 19)
private String idCardNumber;
/** 文化程度 */
@ApiModelProperty(value = "文化程度", position = 20)
private String cultureLevel;
/** 政治面貌 */
@ApiModelProperty(value = "政治面貌", position = 21)
private String politicalOutlook;
/** 毕业院校 */
@ApiModelProperty(value = "毕业院校", position = 22)
private String college;
/** 学历 */
@ApiModelProperty(value = "学历", position = 23)
private String education;
/** 学制 */
@ApiModelProperty(value = "学制", position = 24)
private String eduLength;
/** 学位 */
@ApiModelProperty(value = "学位", position = 25)
private String degree;
/** 手机 */
@ApiModelProperty(value = "手机", position = 26)
private String phone;
/** 邮箱 */
@ApiModelProperty(value = "邮箱", position = 27)
private String email;
/** 家庭电话 */
@ApiModelProperty(value = "家庭电话", position = 28)
private String homeTel;
/** 办公电话 */
@ApiModelProperty(value = "办公电话", position = 29)
private String officeTel;
/** 紧急联系人 */
@ApiModelProperty(value = "紧急联系人", position = 30)
private String emergencyContact;
/** 紧急联系人电话 */
@ApiModelProperty(value = "紧急联系人电话", position = 31)
private String emergencyPhone;
/** 紧急联系人地址 */
@ApiModelProperty(value = "紧急联系人地址", position = 32)
private String emergencyAddress;
/** 员工编号 */
@ApiModelProperty(value = "员工编号", position = 33)
private String empNo;
/** 入职日期 */
@ApiModelProperty(value = "员工编号", position = 34)
private String entryDate;
/** 兼任信息 */
@ApiModelProperty(value = "兼任信息", position = 35)
private String positionJson;
/** 排序码 */
@ApiModelProperty(value = "排序码", position = 36)
private Integer sortCode;
/** 扩展信息 */
@ApiModelProperty(value = "扩展信息", position = 37)
private String extJson;
}

View File

@@ -0,0 +1,29 @@
package mjkf.xinke.biz.modular.user.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 人员导出参数
*
*
* @date 2022/7/26 16:00
**/
@Getter
@Setter
public class BizUserExportParam {
/** 人员状态 */
@ApiModelProperty(value = "人员状态")
private String userStatus;
/** 账号、姓名、手机号关键词 */
@ApiModelProperty(value = "账号、姓名、手机号关键词")
private String searchKey;
/** 人员id集合 */
@ApiModelProperty(value = "人员id集合")
private String userIds;
}

View File

@@ -0,0 +1,31 @@
package mjkf.xinke.biz.modular.user.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 人员授权角色参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizUserGrantRoleParam {
/** id */
@ApiModelProperty(value = "id", required = true, position = 1)
@NotBlank(message = "id不能为空")
private String id;
/** 角色id集合 */
@ApiModelProperty(value = "角色id集合", required = true, position = 2)
@NotNull(message = "roleIdList不能为空")
private List<String> roleIdList;
}

View File

@@ -0,0 +1,24 @@
package mjkf.xinke.biz.modular.user.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* 人员Id参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizUserIdParam {
/** id */
@ApiModelProperty(value = "id", required = true)
@NotBlank(message = "id不能为空")
private String id;
}

View File

@@ -0,0 +1,109 @@
package mjkf.xinke.biz.modular.user.param;
import lombok.Getter;
import lombok.Setter;
/**
* 人员导入参数
*
*
* @date 2022/7/8 13:22
**/
@Getter
@Setter
public class BizUserImportParam {
/** 账号 */
private String account;
/** 姓名 */
private String name;
/** 组织名称 */
private String orgName;
/** 职位名称 */
private String positionName;
/** 手机 */
private String phone;
/** 邮箱 */
private String email;
/** 主管名称 */
private String directorName;
/** 员工编号 */
private String empNo;
/** 入职日期 */
private String entryDate;
/** 职级 */
private String positionLevel;
/** 昵称 */
private String nickname;
/** 性别 */
private String gender;
/** 年龄 */
private String age;
/** 出生日期 */
private String birthday;
/** 民族 */
private String nation;
/** 籍贯 */
private String nativePlace;
/** 家庭住址 */
private String homeAddress;
/** 通信地址 */
private String mailingAddress;
/** 证件类型 */
private String idCardType;
/** 证件号码 */
private String idCardNumber;
/** 文化程度 */
private String cultureLevel;
/** 政治面貌 */
private String politicalOutlook;
/** 毕业院校 */
private String college;
/** 学历 */
private String education;
/** 学制 */
private String eduLength;
/** 学位 */
private String degree;
/** 家庭电话 */
private String homeTel;
/** 办公电话 */
private String officeTel;
/** 紧急联系人 */
private String emergencyContact;
/** 紧急联系人电话 */
private String emergencyPhone;
/** 紧急联系人地址 */
private String emergencyAddress;
}

View File

@@ -0,0 +1,45 @@
package mjkf.xinke.biz.modular.user.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 人员查询参数
*
*
* @date 2022/7/26 16:00
**/
@Getter
@Setter
public class BizUserPageParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 排序字段 */
@ApiModelProperty(value = "排序字段字段驼峰名称userName")
private String sortField;
/** 排序方式 */
@ApiModelProperty(value = "排序方式升序ASCEND降序DESCEND")
private String sortOrder;
/** 姓名关键词 */
@ApiModelProperty(value = "账号、姓名、手机号关键词")
private String searchKey;
/** 人员状态 */
@ApiModelProperty(value = "人员状态")
private String userStatus;
/** 所属机构 */
@ApiModelProperty(value = "所属机构")
private String orgId;
}

View File

@@ -0,0 +1,33 @@
package mjkf.xinke.biz.modular.user.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 组织列表选择器参数
*
*
* @date 2022/4/21 16:13
**/
@Getter
@Setter
public class BizUserSelectorOrgListParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 父id */
@ApiModelProperty(value = "父id")
private String parentId;
/** 名称关键词 */
@ApiModelProperty(value = "名称关键词")
private String searchKey;
}

View File

@@ -0,0 +1,33 @@
package mjkf.xinke.biz.modular.user.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 岗位选择器参数
*
*
* @date 2022/7/26 15:58
**/
@Getter
@Setter
public class BizUserSelectorPositionParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 机构id */
@ApiModelProperty(value = "机构id")
private String orgId;
/** 名称关键词 */
@ApiModelProperty(value = "名称关键词")
private String searchKey;
}

View File

@@ -0,0 +1,37 @@
package mjkf.xinke.biz.modular.user.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 角色选择器参数
*
*
* @date 2022/7/26 16:02
**/
@Getter
@Setter
public class BizUserSelectorRoleParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 组织id */
@ApiModelProperty(value = "组织id")
private String orgId;
/** 角色分类 */
@ApiModelProperty(value = "角色分类")
private String category;
/** 名称关键词 */
@ApiModelProperty(value = "名称关键词")
private String searchKey;
}

View File

@@ -0,0 +1,33 @@
package mjkf.xinke.biz.modular.user.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 用户选择器参数
*
*
* @date 2022/7/26 16:03
**/
@Getter
@Setter
public class BizUserSelectorUserParam {
/** 当前页 */
@ApiModelProperty(value = "当前页码")
private Integer current;
/** 每页条数 */
@ApiModelProperty(value = "每页条数")
private Integer size;
/** 组织id */
@ApiModelProperty(value = "组织id")
private String orgId;
/** 姓名关键词 */
@ApiModelProperty(value = "姓名关键词")
private String searchKey;
}

View File

@@ -0,0 +1,236 @@
package mjkf.xinke.biz.modular.user.result;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.HeadStyle;
import com.alibaba.excel.enums.poi.FillPatternTypeEnum;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
/**
* 人员导出结果集
*
*
* @date 2022/7/8 13:22
**/
@Getter
@Setter
public class BizUserExportResult {
/** 机构分组 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40)
@ExcelProperty({"人员信息", "机构分组"})
private String groupName;
/** 头像 */
@ColumnWidth(8)
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "头像"})
private byte[] avatar;
/** 账号 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "账号"})
private String account;
/** 姓名 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "姓名"})
private String name;
/** 昵称 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "昵称"})
private String nickname;
/** 性别 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "性别"})
private String gender;
/** 年龄 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "年龄"})
private String age;
/** 出生日期 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "出生日期"})
private String birthday;
/** 民族 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "民族"})
private String nation;
/** 籍贯 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "籍贯"})
private String nativePlace;
/** 家庭住址 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "家庭住址"})
private String homeAddress;
/** 通信地址 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "通信地址"})
private String mailingAddress;
/** 证件类型 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "证件类型"})
private String idCardType;
/** 证件号码 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "证件号码"})
private String idCardNumber;
/** 文化程度 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "文化程度"})
private String cultureLevel;
/** 政治面貌 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "政治面貌"})
private String politicalOutlook;
/** 毕业院校 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "毕业院校"})
private String college;
/** 学历 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "学历"})
private String education;
/** 学制 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "学制"})
private String eduLength;
/** 学位 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "学位"})
private String degree;
/** 手机 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "手机"})
private String phone;
/** 邮箱 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "邮箱"})
private String email;
/** 家庭电话 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "家庭电话"})
private String homeTel;
/** 办公电话 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "办公电话"})
private String officeTel;
/** 紧急联系人 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "紧急联系人"})
private String emergencyContact;
/** 紧急联系人电话 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "紧急联系人电话"})
private String emergencyPhone;
/** 紧急联系人地址 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 29)
@ExcelProperty({"人员信息", "基本信息", "紧急联系人地址"})
private String emergencyAddress;
/** 员工编号 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 34)
@ExcelProperty({"人员信息", "员工信息", "员工编号"})
private String empNo;
/** 入职日期 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 34)
@ExcelProperty({"人员信息", "员工信息", "入职日期"})
private String entryDate;
/** 组织名称 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 34)
@ExcelProperty({"人员信息", "员工信息", "组织名称"})
private String orgName;
/** 职位名称 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 34)
@ExcelProperty({"人员信息", "员工信息", "职位名称"})
private String positionName;
/** 主管名称 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 34)
@ExcelProperty({"人员信息", "员工信息", "主管名称"})
private String directorName;
/** 职级 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 34)
@ExcelProperty({"人员信息", "员工信息", "职级"})
private String positionLevel;
/** 上次登录ip */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 42)
@ExcelProperty({"人员信息", "系统信息", "上次登录ip"})
private String lastLoginIp;
/** 上次登录地点 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 42)
@ExcelProperty({"人员信息", "系统信息", "上次登录地点"})
private String lastLoginAddress;
/** 上次登录时间 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 42)
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
@ExcelProperty({"人员信息", "系统信息", "上次登录时间"})
private Date lastLoginTime;
/** 上次登录设备 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 42)
@ExcelProperty({"人员信息", "系统信息", "上次登录设备"})
private String lastLoginDevice;
/** 最新登录ip */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 42)
@ExcelProperty({"人员信息", "系统信息", "最新登录ip"})
private String latestLoginIp;
/** 最新登录地点 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 42)
@ExcelProperty({"人员信息", "系统信息", "最新登录地点"})
private String latestLoginAddress;
/** 最新登录时间 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 42)
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
@ExcelProperty({"人员信息", "系统信息", "最新登录时间"})
private Date latestLoginTime;
/** 最新登录设备 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 42)
@ExcelProperty({"人员信息", "系统信息", "最新登录设备"})
private String latestLoginDevice;
/** 人员状态 */
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 42)
@ExcelProperty({"人员信息", "系统信息", "人员状态"})
private String userStatus;
}

View File

@@ -0,0 +1,37 @@
package mjkf.xinke.biz.modular.user.result;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* 角色选择器结果
*
*
* @date 2022/7/22 14:29
**/
@Getter
@Setter
public class BizUserRoleResult {
/** id */
@ApiModelProperty(value = "id", position = 1)
private String id;
/** 组织id */
@ApiModelProperty(value = "机构id", position = 2)
private String orgId;
/** 名称 */
@ApiModelProperty(value = "名称", position = 3)
private String name;
/** 分类 */
@ApiModelProperty(value = "分类", position = 4)
private String category;
/** 排序码 */
@ApiModelProperty(value = "排序码", position = 5)
private Integer sortCode;
}

View File

@@ -0,0 +1,179 @@
package mjkf.xinke.biz.modular.user.service;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import mjkf.xinke.biz.modular.org.entity.BizOrg;
import mjkf.xinke.biz.modular.position.entity.BizPosition;
import mjkf.xinke.biz.modular.user.entity.BizUser;
import mjkf.xinke.biz.modular.user.param.*;
import mjkf.xinke.biz.modular.user.result.BizUserRoleResult;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* 人员Service接口
*
*
* @date 2022/4/21 18:35
**/
public interface BizUserService extends IService<BizUser> {
/**
* 获取人员分页
*
*
* @date 2022/4/24 20:08
*/
Page<BizUser> page(BizUserPageParam bizUserPageParam);
/**
* 添加人员
*
*
* @date 2022/4/24 20:48
*/
void add(BizUserAddParam bizUserAddParam);
/**
* 编辑人员
*
*
* @date 2022/4/24 21:13
*/
void edit(BizUserEditParam bizUserEditParam);
/**
* 删除人员
*
*
* @date 2022/4/24 21:18
*/
void delete(List<BizUserIdParam> bizUserIdParamList);
/**
* 获取人员详情
*
*
* @date 2022/4/24 21:18
*/
BizUser detail(BizUserIdParam bizUserIdPara);
/**
* 获取人员详情
*
*
* @date 2022/7/26 17:21
**/
BizUser queryEntity(String id);
/**
* 禁用人员
*
*
* @date 2022/7/5 18:20
**/
void disableUser(BizUserIdParam bizUserIdParam);
/**
* 启用人员
*
*
* @date 2022/7/5 18:21
**/
void enableUser(BizUserIdParam bizUserIdParam);
/**
* 重置人员密码
*
*
* @date 2022/7/5 18:22
**/
void resetPassword(BizUserIdParam bizUserIdParam);
/**
* 获取人员拥有角色
*
*
* @date 2022/5/13 21:00
*/
List<String> ownRole(BizUserIdParam bizUserIdParam);
/**
* 给人员授权角色
*
*
* @date 2022/4/29 11:13
**/
void grantRole(BizUserGrantRoleParam bizUserGrantRoleParam);
/**
* 用户导出
*
*
* @date 2022/8/8 13:16
**/
void exportUser(BizUserExportParam bizUserExportParam, HttpServletResponse response) throws IOException;
/**
* 导出用户个人信息
*
*
* @date 2022/8/8 13:16
**/
void exportUserInfo(BizUserIdParam bizUserIdParam, HttpServletResponse response) throws IOException;
/* ====人员部分所需要用到的选择器==== */
/**
* 获取机构树选择器
*
*
* @date 2022/5/13 21:00
*/
List<Tree<String>> orgTreeSelector();
/**
* 获取机构列表选择器
*
*
* @date 2022/7/22 13:34
**/
Page<BizOrg> orgListSelector(BizUserSelectorOrgListParam bizUserSelectorOrgListParam);
/**
* 获取岗位选择器
*
*
* @date 2022/5/13 21:00
*/
Page<BizPosition> positionSelector(BizUserSelectorPositionParam bizUserSelectorPositionParam);
/**
* 获取角色选择器
*
*
* @date 2022/5/13 21:00
*/
Page<BizUserRoleResult> roleSelector(BizUserSelectorRoleParam bizUserSelectorRoleParam);
/**
* 获取人员选择器
*
*
* @date 2022/4/24 20:08
*/
Page<BizUser> userSelector(BizUserSelectorUserParam bizUserSelectorUserParam);
/**
* 获取动态字段配置
*
*
* @date 2022/8/5 323
*/
List<JSONObject> dynamicFieldConfigList();
}

View File

@@ -0,0 +1,743 @@
package mjkf.xinke.biz.modular.user.service.impl;
import cn.afterturn.easypoi.cache.manager.POICacheManager;
import cn.afterturn.easypoi.entity.ImageEntity;
import cn.afterturn.easypoi.word.WordExportUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fhs.trans.service.impl.TransService;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import mjkf.xinke.auth.core.util.StpLoginUserUtil;
import mjkf.xinke.biz.core.enums.BizBuildInEnum;
import mjkf.xinke.biz.core.enums.BizDataTypeEnum;
import mjkf.xinke.biz.modular.org.entity.BizOrg;
import mjkf.xinke.biz.modular.org.service.BizOrgService;
import mjkf.xinke.biz.modular.position.entity.BizPosition;
import mjkf.xinke.biz.modular.position.service.BizPositionService;
import mjkf.xinke.biz.modular.user.entity.BizUser;
import mjkf.xinke.biz.modular.user.enums.BizRoleCategoryEnum;
import mjkf.xinke.biz.modular.user.enums.BizUserStatusEnum;
import mjkf.xinke.biz.modular.user.mapper.BizUserMapper;
import mjkf.xinke.biz.modular.user.param.*;
import mjkf.xinke.biz.modular.user.result.BizUserRoleResult;
import mjkf.xinke.biz.modular.user.service.BizUserService;
import mjkf.xinke.common.enums.CommonSortOrderEnum;
import mjkf.xinke.common.excel.CommonExcelCustomMergeStrategy;
import mjkf.xinke.common.exception.CommonException;
import mjkf.xinke.common.listener.CommonDataChangeEventCenter;
import mjkf.xinke.common.page.CommonPageRequest;
import mjkf.xinke.common.util.*;
import mjkf.xinke.dbs.api.DbsApi;
import mjkf.xinke.dev.api.DevConfigApi;
import mjkf.xinke.dev.api.DevDfcApi;
import mjkf.xinke.sys.api.SysRoleApi;
import mjkf.xinke.sys.api.SysUserApi;
import mjkf.xinke.biz.modular.user.result.BizUserExportResult;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 人员Service接口实现类
*
*
* @date 2022/2/23 18:43
**/
@Service
public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> implements BizUserService {
private static final String SNOWY_SYS_DEFAULT_PASSWORD_KEY = "SNOWY_SYS_DEFAULT_PASSWORD";
@Resource
private TransService transService;
@Resource
private DevConfigApi devConfigApi;
@Resource
private SysUserApi sysUserApi;
@Resource
private SysRoleApi sysRoleApi;
@Resource
private BizOrgService bizOrgService;
@Resource
private BizPositionService bizPositionService;
@Resource
private DbsApi dbsApi;
@Resource
private DevDfcApi devDfcApi;
@Override
public Page<BizUser> page(BizUserPageParam bizUserPageParam) {
QueryWrapper<BizUser> queryWrapper = new QueryWrapper<>();
if (ObjectUtil.isNotEmpty(bizUserPageParam.getSearchKey())) {
queryWrapper.lambda().like(BizUser::getAccount, bizUserPageParam.getSearchKey()).or()
.like(BizUser::getName, bizUserPageParam.getSearchKey());
}
if (ObjectUtil.isNotEmpty(bizUserPageParam.getOrgId())) {
queryWrapper.lambda().eq(BizUser::getOrgId, bizUserPageParam.getOrgId());
}
if (ObjectUtil.isNotEmpty(bizUserPageParam.getUserStatus())) {
queryWrapper.lambda().eq(BizUser::getUserStatus, bizUserPageParam.getUserStatus());
}
if (ObjectUtil.isAllNotEmpty(bizUserPageParam.getSortField(), bizUserPageParam.getSortOrder())) {
CommonSortOrderEnum.validate(bizUserPageParam.getSortOrder());
queryWrapper.orderBy(true, bizUserPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
StrUtil.toUnderlineCase(bizUserPageParam.getSortField()));
} else {
queryWrapper.lambda().orderByAsc(BizUser::getSortCode);
}
// 排除超管
queryWrapper.lambda().ne(BizUser::getAccount, BizBuildInEnum.BUILD_IN_USER_ACCOUNT.getValue());
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
queryWrapper.lambda().in(BizUser::getOrgId, loginUserDataScope);
} else {
queryWrapper.lambda().eq(BizUser::getId, StpUtil.getLoginIdAsString());
}
return this.page(CommonPageRequest.defaultPage(), queryWrapper);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void add(BizUserAddParam bizUserAddParam) {
checkParam(bizUserAddParam);
BizUser bizUser = BeanUtil.toBean(bizUserAddParam, BizUser.class);
if(ObjectUtil.isEmpty(bizUser.getAvatar())) {
// 设置默认头像
bizUser.setAvatar(CommonAvatarUtil.generateImg(bizUser.getName()));
}
// 设置密码
bizUser.setPassword(CommonCryptogramUtil.doHashValue(devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_PASSWORD_KEY)));
// 设置状态
bizUser.setUserStatus(BizUserStatusEnum.ENABLE.getValue());
this.save(bizUser);
// 发布增加事件
CommonDataChangeEventCenter.doAddWithData(BizDataTypeEnum.USER.getValue(), JSONUtil.createArray().put(bizUser));
}
private void checkParam(BizUserAddParam bizUserAddParam) {
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.contains(bizUserAddParam.getOrgId())) {
throw new CommonException("您没有权限在该机构下增加人员机构id{}", bizUserAddParam.getOrgId());
}
} else {
throw new CommonException("您没有权限在该机构下增加人员机构id{}", bizUserAddParam.getOrgId());
}
if (this.count(new LambdaQueryWrapper<BizUser>()
.eq(BizUser::getAccount, bizUserAddParam.getAccount())) > 0) {
throw new CommonException("存在重复的账号,账号为:{}", bizUserAddParam.getAccount());
}
if(ObjectUtil.isNotEmpty(bizUserAddParam.getPhone())) {
if(!PhoneUtil.isMobile(bizUserAddParam.getPhone())) {
throw new CommonException("手机号码:{}格式错误", bizUserAddParam.getPhone());
}
if (this.count(new LambdaQueryWrapper<BizUser>()
.eq(BizUser::getPhone, CommonCryptogramUtil.doSm4CbcEncrypt(bizUserAddParam.getPhone()))) > 0) {
throw new CommonException("存在重复的手机号,手机号为:{}", bizUserAddParam.getPhone());
}
}
if(ObjectUtil.isNotEmpty(bizUserAddParam.getEmail())) {
if(!CommonEmailUtil.isEmail(bizUserAddParam.getEmail())) {
throw new CommonException("邮箱:{}格式错误", bizUserAddParam.getEmail());
}
if (this.count(new LambdaQueryWrapper<BizUser>()
.eq(BizUser::getEmail, bizUserAddParam.getEmail())) > 0) {
throw new CommonException("存在重复的邮箱,邮箱为:{}", bizUserAddParam.getEmail());
}
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void edit(BizUserEditParam bizUserEditParam) {
BizUser bizUser = this.queryEntity(bizUserEditParam.getId());
checkParam(bizUserEditParam);
boolean updateSuperAdminAccount = bizUser.getAccount().equals(BizBuildInEnum.BUILD_IN_USER_ACCOUNT.getValue()) &&
!bizUserEditParam.getAccount().equals(BizBuildInEnum.BUILD_IN_USER_ACCOUNT.getValue());
if(updateSuperAdminAccount) {
throw new CommonException("不可修改系统内置超管人员账号");
}
BeanUtil.copyProperties(bizUserEditParam, bizUser);
this.updateById(bizUser);
// 发布更新事件
CommonDataChangeEventCenter.doUpdateWithData(BizDataTypeEnum.USER.getValue(), JSONUtil.createArray().put(bizUser));
}
private void checkParam(BizUserEditParam bizUserEditParam) {
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.contains(bizUserEditParam.getOrgId())) {
throw new CommonException("您没有权限编辑该机构下的人员机构id{}", bizUserEditParam.getOrgId());
}
} else {
if(!bizUserEditParam.getId().equals(StpUtil.getLoginIdAsString())) {
throw new CommonException("您没有权限编辑该机构下的人员机构id{}", bizUserEditParam.getOrgId());
}
}
if (this.count(new LambdaQueryWrapper<BizUser>()
.eq(BizUser::getAccount, bizUserEditParam.getAccount())
.ne(BizUser::getId, bizUserEditParam.getId())) > 0) {
throw new CommonException("存在重复的账号,账号为:{}", bizUserEditParam.getAccount());
}
if(ObjectUtil.isNotEmpty(bizUserEditParam.getPhone())) {
if(!PhoneUtil.isMobile(bizUserEditParam.getPhone())) {
throw new CommonException("手机号码:{}格式错误", bizUserEditParam.getPhone());
}
if (this.count(new LambdaQueryWrapper<BizUser>()
.eq(BizUser::getPhone, CommonCryptogramUtil.doSm4CbcEncrypt(bizUserEditParam.getPhone()))
.ne(BizUser::getId, bizUserEditParam.getId())) > 0) {
throw new CommonException("存在重复的手机号,手机号为:{}", bizUserEditParam.getPhone());
}
}
if(ObjectUtil.isNotEmpty(bizUserEditParam.getEmail())) {
if(!CommonEmailUtil.isEmail(bizUserEditParam.getEmail())) {
throw new CommonException("邮箱:{}格式错误", bizUserEditParam.getEmail());
}
if (this.count(new LambdaQueryWrapper<BizUser>()
.eq(BizUser::getEmail, bizUserEditParam.getEmail())
.ne(BizUser::getId, bizUserEditParam.getId())) > 0) {
throw new CommonException("存在重复的邮箱,邮箱为:{}", bizUserEditParam.getEmail());
}
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void delete(List<BizUserIdParam> bizUserIdParamList) {
List<String> bizUserIdList = CollStreamUtil.toList(bizUserIdParamList, BizUserIdParam::getId);
if(ObjectUtil.isNotEmpty(bizUserIdList)) {
boolean containsSuperAdminAccount = this.listByIds(bizUserIdList).stream().map(BizUser::getAccount)
.collect(Collectors.toSet()).contains(BizBuildInEnum.BUILD_IN_USER_ACCOUNT.getValue());
if(containsSuperAdminAccount) {
throw new CommonException("不可删除系统内置超管人员");
}
// 获取这些人员的的机构id集合
Set<String> userOrgIdList = this.listByIds(bizUserIdList).stream().map(BizUser::getOrgId).collect(Collectors.toSet());
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.containsAll(userOrgIdList)) {
throw new CommonException("您没有权限删除这些机构下的人员机构id{}",
CollectionUtil.subtract(userOrgIdList, loginUserDataScope));
}
} else {
if(bizUserIdList.size() != 1 || !bizUserIdList.get(0).equals(StpUtil.getLoginIdAsString())) {
throw new CommonException("您没有权限删除这些机构下的人员机构id{}", userOrgIdList);
}
}
// 清除【将这些人员作为主管】的信息
this.update(new LambdaUpdateWrapper<BizUser>().in(BizUser::getDirectorId, bizUserIdList).set(BizUser::getDirectorId, null));
// 清除【将这些人员作为兼任岗位的主管】的信息
this.list(new LambdaQueryWrapper<BizUser>() .isNotNull(BizUser::getPositionJson)).forEach(bizUser -> {
List<JSONObject> handledJsonObjectList = JSONUtil.toList(JSONUtil.parseArray(bizUser.getPositionJson()),
JSONObject.class).stream().peek(jsonObject -> {
String directorId = jsonObject.getStr("directorId");
if (ObjectUtil.isNotEmpty(directorId) && bizUserIdList.contains(directorId)) {
jsonObject.remove("directorId");
}
}).collect(Collectors.toList());
this.update(new LambdaUpdateWrapper<BizUser>().eq(BizUser::getId, bizUser.getId())
.set(BizUser::getPositionJson, JSONUtil.toJsonStr(handledJsonObjectList)));
});
// 清除【将这些人员作为主管】的机构的主管信息
bizOrgService.update(new LambdaUpdateWrapper<BizOrg>().in(BizOrg::getDirectorId, bizUserIdList).set(BizOrg::getDirectorId, null));
// 执行删除
this.removeByIds(bizUserIdList);
// 发布删除事件
CommonDataChangeEventCenter.doDeleteWithDataId(BizDataTypeEnum.USER.getValue(), bizUserIdList);
}
}
@Override
public BizUser detail(BizUserIdParam bizUserIdParam) {
return this.queryEntity(bizUserIdParam.getId());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void disableUser(BizUserIdParam bizUserIdParam) {
BizUser bizUser = this.detail(bizUserIdParam);
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
throw new CommonException("您没有权限禁用该机构下的人员:{}机构id{}", bizUser.getName(), bizUser.getOrgId());
}
} else {
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
throw new CommonException("您没有权限禁用该机构下的人员:{}机构id{}", bizUser.getName(), bizUser.getOrgId());
}
}
this.update(new LambdaUpdateWrapper<BizUser>().eq(BizUser::getId,
bizUserIdParam.getId()).set(BizUser::getUserStatus, BizUserStatusEnum.DISABLED.getValue()));
}
@Transactional(rollbackFor = Exception.class)
@Override
public void enableUser(BizUserIdParam bizUserIdParam) {
BizUser bizUser = this.detail(bizUserIdParam);
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
throw new CommonException("您没有权限启用该机构下的人员:{}机构id{}", bizUser.getName(), bizUser.getOrgId());
}
} else {
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
throw new CommonException("您没有权限启用该机构下的人员:{}机构id{}", bizUser.getName(), bizUser.getOrgId());
}
}
this.update(new LambdaUpdateWrapper<BizUser>().eq(BizUser::getId,
bizUserIdParam.getId()).set(BizUser::getUserStatus, BizUserStatusEnum.ENABLE.getValue()));
}
@Transactional(rollbackFor = Exception.class)
@Override
public void resetPassword(BizUserIdParam bizUserIdParam) {
BizUser bizUser = this.detail(bizUserIdParam);
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
throw new CommonException("您没有权限为该机构下的人员:{}重置密码机构id{}", bizUser.getName(), bizUser.getOrgId());
}
} else {
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
throw new CommonException("您没有权限为该机构下的人员:{}重置密码机构id{}", bizUser.getName(), bizUser.getOrgId());
}
}
this.update(new LambdaUpdateWrapper<BizUser>().eq(BizUser::getId,
bizUserIdParam.getId()).set(BizUser::getPassword,
CommonCryptogramUtil.doHashValue(devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_PASSWORD_KEY))));
}
@Override
public List<String> ownRole(BizUserIdParam bizUserIdParam) {
return sysUserApi.ownRole(bizUserIdParam.getId());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void grantRole(BizUserGrantRoleParam bizUserGrantRoleParam) {
BizUser bizUser = this.queryEntity(bizUserGrantRoleParam.getId());
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
throw new CommonException("您没有权限为该机构下的人员:{}授权角色机构id{}", bizUser.getName(), bizUser.getOrgId());
}
} else {
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
throw new CommonException("您没有权限为该机构下的人员:{}授权角色机构id{}", bizUser.getName(), bizUser.getOrgId());
}
}
sysUserApi.grantRole(bizUserGrantRoleParam.getId(), bizUserGrantRoleParam.getRoleIdList());
}
@Override
public void exportUser(BizUserExportParam bizUserExportParam, HttpServletResponse response) throws IOException {
File tempFile = null;
try {
QueryWrapper<BizUser> queryWrapper = new QueryWrapper<>();
// 排除超管
queryWrapper.lambda().ne(BizUser::getAccount, BizBuildInEnum.BUILD_IN_USER_ACCOUNT.getValue());
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
queryWrapper.lambda().in(BizUser::getOrgId, loginUserDataScope);
} else {
queryWrapper.lambda().eq(BizUser::getId, StpUtil.getLoginIdAsString());
}
if(ObjectUtil.isNotEmpty(bizUserExportParam.getUserIds())) {
queryWrapper.lambda().in(BizUser::getId, StrUtil.split(bizUserExportParam.getUserIds(), StrUtil.COMMA));
} else {
if (ObjectUtil.isNotEmpty(bizUserExportParam.getSearchKey())) {
queryWrapper.lambda().like(BizUser::getAccount, bizUserExportParam.getSearchKey())
.or().like(BizUser::getName, bizUserExportParam.getSearchKey())
.or().like(BizUser::getPhone, bizUserExportParam.getSearchKey());
}
if (ObjectUtil.isNotEmpty(bizUserExportParam.getUserStatus())) {
queryWrapper.lambda().eq(BizUser::getUserStatus, bizUserExportParam.getUserStatus());
}
}
String fileName = "SNOWY2.0系统B端人员信息清单.xlsx";
List<BizUser> bizUserList = this.list(queryWrapper);
if(ObjectUtil.isEmpty(bizUserList)) {
throw new CommonException("无数据可导出");
}
transService.transBatch(bizUserList);
bizUserList = CollectionUtil.sort(bizUserList, Comparator.comparing(BizUser::getOrgId));
List<BizUserExportResult> bizUserExportResultList = bizUserList.stream()
.map(bizUser -> {
BizUserExportResult bizUserExportResult = new BizUserExportResult();
BeanUtil.copyProperties(bizUser, bizUserExportResult);
bizUserExportResult.setGroupName(ObjectUtil.isNotEmpty(bizUserExportResult.getOrgName())?
bizUserExportResult.getOrgName():"无组织");
// 状态枚举转为文字
bizUserExportResult.setUserStatus(bizUserExportResult.getUserStatus()
.equalsIgnoreCase(BizUserStatusEnum.ENABLE.getValue())?"正常":"停用");
// 将base64转为byte数组
if (ObjectUtil.isNotEmpty(bizUser.getAvatar())) {
bizUserExportResult.setAvatar(ImgUtil.toBytes(ImgUtil.toImage(StrUtil
.split(bizUser.getAvatar(), StrUtil.COMMA).get(1)), ImgUtil.IMAGE_TYPE_PNG));
}
return bizUserExportResult;
}).collect(Collectors.toList());
// 创建临时文件
tempFile = FileUtil.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
// 头的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short) 14);
headWriteCellStyle.setWriteFont(headWriteFont);
// 水平垂直居中
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
// 内容背景白色
contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
WriteFont contentWriteFont = new WriteFont();
// 内容字体大小
contentWriteFont.setFontHeightInPoints((short) 12);
contentWriteCellStyle.setWriteFont(contentWriteFont);
//设置边框样式,细实线
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
// 水平垂直居中
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle,
contentWriteCellStyle);
// 写excel
EasyExcel.write(tempFile.getPath(), BizUserExportResult.class)
// 自定义样式
.registerWriteHandler(horizontalCellStyleStrategy)
// 自动列宽
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
// 机构分组合并单元格
.registerWriteHandler(new CommonExcelCustomMergeStrategy(bizUserExportResultList.stream().map(BizUserExportResult::getGroupName)
.collect(Collectors.toList()), 0))
// 设置第一行字体
.registerWriteHandler(new CellWriteHandler() {
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
WriteCellData<?> cellData = context.getFirstCellData();
WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();
Integer rowIndex = context.getRowIndex();
if(rowIndex == 0) {
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontName("宋体");
headWriteFont.setBold(true);
headWriteFont.setFontHeightInPoints((short) 16);
writeCellStyle.setWriteFont(headWriteFont);
}
}
})
// 设置表头行高
.registerWriteHandler(new AbstractRowHeightStyleStrategy() {
@Override
protected void setHeadColumnHeight(Row row, int relativeRowIndex) {
if(relativeRowIndex == 0) {
// 表头第一行
row.setHeightInPoints(34);
} else {
// 表头其他行
row.setHeightInPoints(30);
}
}
@Override
protected void setContentColumnHeight(Row row, int relativeRowIndex) {
// 内容行
row.setHeightInPoints(20);
}
})
.sheet("人员信息")
.doWrite(bizUserExportResultList);
CommonDownloadUtil.download(tempFile, response);
} catch (Exception e) {
log.error(">>> 人员导出异常:", e);
CommonResponseUtil.renderError(response, "导出失败");
} finally {
FileUtil.del(tempFile);
}
}
@Override
public void exportUserInfo(BizUserIdParam bizUserIdParam, HttpServletResponse response) throws IOException {
File destTemplateFile = null;
File resultFile = null;
try {
BizUser bizUser = this.queryEntity(bizUserIdParam.getId());
transService.transOne(bizUser);
// 读取模板流
InputStream inputStream = POICacheManager.getFile("userExportTemplate.docx");
// 创建一个临时模板
destTemplateFile = FileUtil.writeFromStream(inputStream, FileUtil.file(FileUtil.getTmpDir() +
File.separator + "userExportTemplate.docx"));
// 构造填充的参数
Map<String, Object> map = BeanUtil.beanToMap(bizUser);
String avatarBase64;
if(ObjectUtil.isNotEmpty(bizUser.getAvatar())) {
avatarBase64 = bizUser.getAvatar();
} else {
avatarBase64 = CommonAvatarUtil.generateImg(bizUser.getName());
}
// 头像
ImageEntity imageEntity = new ImageEntity(ImgUtil.toBytes(ImgUtil.toImage(StrUtil
.split(avatarBase64, StrUtil.COMMA).get(1)), ImgUtil.IMAGE_TYPE_PNG), 120, 160);
map.put("avatar", imageEntity);
if(ObjectUtil.isNotEmpty(bizUser.getBirthday())) {
try {
// 年龄
long age = cn.hutool.core.date.DateUtil.betweenYear(cn.hutool.core.date.DateUtil.parseDate(bizUser.getBirthday()), DateTime.now(), true);
if(age != 0) {
map.put("age", age + "");
}
} catch (Exception ignored) {
}
}
// 导出时间
map.put("exportDateTime", DateUtil.format(DateTime.now(), DatePattern.CHINESE_DATE_PATTERN));
// 生成doc
XWPFDocument doc = WordExportUtil.exportWord07(destTemplateFile.getAbsolutePath(), map);
// 生成临时导出文件
resultFile = FileUtil.file(FileUtil.getTmpDir() + File.separator + "SNOWY2.0系统B端人员信息_" + bizUser.getName() + ".docx");
// 写入
BufferedOutputStream outputStream = FileUtil.getOutputStream(resultFile);
doc.write(outputStream);
outputStream.close();
// 下载
CommonDownloadUtil.download(resultFile, response);
} catch (Exception e) {
log.error(">>> 导出人员个人信息异常:", e);
CommonResponseUtil.renderError(response, "导出失败");
} finally {
// 删除临时文件
if(ObjectUtil.isNotEmpty(destTemplateFile)) {
FileUtil.del(destTemplateFile);
}
if(ObjectUtil.isNotEmpty(resultFile)) {
FileUtil.del(resultFile);
}
}
}
@Override
public BizUser queryEntity(String id) {
BizUser bizUser = this.getById(id);
if(ObjectUtil.isEmpty(bizUser)) {
throw new CommonException("人员不存在id值为{}", id);
}
return bizUser;
}
/* ====人员部分所需要用到的选择器==== */
@Override
public List<Tree<String>> orgTreeSelector() {
LambdaQueryWrapper<BizOrg> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
// 定义机构集合
Set<BizOrg> bizOrgSet = CollectionUtil.newHashSet();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
// 获取所有机构
List<BizOrg> allOrgList = bizOrgService.list();
loginUserDataScope.forEach(orgId -> bizOrgSet.addAll(bizOrgService.getParentListById(allOrgList, orgId, true)));
List<String> loginUserDataScopeFullList = bizOrgSet.stream().map(BizOrg::getId).collect(Collectors.toList());
lambdaQueryWrapper.in(BizOrg::getId, loginUserDataScopeFullList);
} else {
return CollectionUtil.newArrayList();
}
lambdaQueryWrapper.orderByAsc(BizOrg::getSortCode);
List<BizOrg> bizOrgList = bizOrgService.list(lambdaQueryWrapper);
List<TreeNode<String>> treeNodeList = bizOrgList.stream().map(bizOrg ->
new TreeNode<>(bizOrg.getId(), bizOrg.getParentId(), bizOrg.getName(), bizOrg.getSortCode()))
.collect(Collectors.toList());
return TreeUtil.build(treeNodeList, "0");
}
@Override
public Page<BizOrg> orgListSelector(BizUserSelectorOrgListParam bizUserSelectorOrgListParam) {
LambdaQueryWrapper<BizOrg> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
lambdaQueryWrapper.in(BizOrg::getId, loginUserDataScope);
} else {
return new Page<>();
}
// 查询部分字段
lambdaQueryWrapper.select(BizOrg::getId, BizOrg::getParentId, BizOrg::getName,
BizOrg::getCategory, BizOrg::getSortCode);
if(ObjectUtil.isNotEmpty(bizUserSelectorOrgListParam.getParentId())) {
lambdaQueryWrapper.eq(BizOrg::getParentId, bizUserSelectorOrgListParam.getParentId());
}
if(ObjectUtil.isNotEmpty(bizUserSelectorOrgListParam.getSearchKey())) {
lambdaQueryWrapper.like(BizOrg::getName, bizUserSelectorOrgListParam.getSearchKey());
}
lambdaQueryWrapper.orderByAsc(BizOrg::getSortCode);
return bizOrgService.page(CommonPageRequest.defaultPage(), lambdaQueryWrapper);
}
@Override
public Page<BizPosition> positionSelector(BizUserSelectorPositionParam bizUserSelectorPositionParam) {
LambdaQueryWrapper<BizPosition> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
lambdaQueryWrapper.in(BizPosition::getOrgId, loginUserDataScope);
} else {
return new Page<>();
}
// 查询部分字段
lambdaQueryWrapper.select(BizPosition::getId, BizPosition::getOrgId, BizPosition::getName,
BizPosition::getCategory, BizPosition::getSortCode);
if(ObjectUtil.isNotEmpty(bizUserSelectorPositionParam.getOrgId())) {
lambdaQueryWrapper.eq(BizPosition::getOrgId, bizUserSelectorPositionParam.getOrgId());
}
if(ObjectUtil.isNotEmpty(bizUserSelectorPositionParam.getSearchKey())) {
lambdaQueryWrapper.like(BizPosition::getName, bizUserSelectorPositionParam.getSearchKey());
}
lambdaQueryWrapper.orderByAsc(BizPosition::getSortCode);
return bizPositionService.page(CommonPageRequest.defaultPage(), lambdaQueryWrapper);
}
@SuppressWarnings("ALL")
@Override
public Page<BizUserRoleResult> roleSelector(BizUserSelectorRoleParam bizUserSelectorRoleParam) {
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
if(ObjectUtil.isNotEmpty(bizUserSelectorRoleParam.getOrgId())) {
if(loginUserDataScope.contains(bizUserSelectorRoleParam.getOrgId())) {
return BeanUtil.toBean(sysRoleApi.roleSelector(bizUserSelectorRoleParam.getOrgId(), bizUserSelectorRoleParam.getCategory(),
bizUserSelectorRoleParam.getSearchKey(), loginUserDataScope, true), Page.class);
} else {
return new Page<>();
}
} else {
if (ObjectUtil.isNotEmpty(bizUserSelectorRoleParam.getCategory()) & BizRoleCategoryEnum.GLOBAL.getValue().equals(bizUserSelectorRoleParam.getCategory())) {
// 查询系统级别的
return BeanUtil.toBean(sysRoleApi.roleSelector(null, bizUserSelectorRoleParam.getCategory(),
bizUserSelectorRoleParam.getSearchKey(), null, true), Page.class);
} else {
return BeanUtil.toBean(sysRoleApi.roleSelector(null, bizUserSelectorRoleParam.getCategory(),
bizUserSelectorRoleParam.getSearchKey(), loginUserDataScope, true), Page.class);
}
}
} else {
return new Page<>();
}
}
@Override
public Page<BizUser> userSelector(BizUserSelectorUserParam bizUserSelectorUserParam) {
LambdaQueryWrapper<BizUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 校验数据范围
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
lambdaQueryWrapper.in(BizUser::getOrgId, loginUserDataScope);
} else {
return new Page<>();
}
// 只查询部分字段
lambdaQueryWrapper.select(BizUser::getId, BizUser::getAvatar, BizUser::getOrgId, BizUser::getPositionId, BizUser::getAccount,
BizUser::getName, BizUser::getSortCode, BizUser::getGender, BizUser::getEntryDate);
if (ObjectUtil.isNotEmpty(bizUserSelectorUserParam.getOrgId())) {
// 如果机构id不为空则查询该机构及其子机构下的所有人
List<String> childOrgIdList = CollStreamUtil.toList(bizOrgService.getChildListById(bizOrgService
.getAllOrgList(), bizUserSelectorUserParam.getOrgId(), true), BizOrg::getId);
if (ObjectUtil.isNotEmpty(childOrgIdList)) {
lambdaQueryWrapper.in(BizUser::getOrgId, childOrgIdList);
} else {
return new Page<>();
}
}
if(ObjectUtil.isNotEmpty(bizUserSelectorUserParam.getSearchKey())) {
lambdaQueryWrapper.like(BizUser::getName, bizUserSelectorUserParam.getSearchKey());
}
lambdaQueryWrapper.orderByAsc(BizUser::getSortCode);
return this.page(CommonPageRequest.defaultPage(), lambdaQueryWrapper);
}
@Override
public List<JSONObject> dynamicFieldConfigList() {
String currentDataSourceId = dbsApi.getCurrentDataSourceId();
String tableName = AnnotationUtil.getAnnotationValue(BizUser.class, TableName.class);
return devDfcApi.getTableFieldList(currentDataSourceId, tableName);
}
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>mjkf-xinke-plugin-client</artifactId>
<packaging>jar</packaging>
<description>C端功能插件</description>
<dependencies>
<!-- 每个插件都要引入自己的对外接口 -->
<dependency>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin-client-api</artifactId>
</dependency>
<!-- 引入登录鉴权接口用于实现其C端登录所需逻辑 -->
<dependency>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin-auth-api</artifactId>
</dependency>
<!-- 引入开发工具接口,用于配置信息 -->
<dependency>
<groupId>mjkf.xinke</groupId>
<artifactId>mjkf-xinke-plugin-dev-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,57 @@
package mjkf.xinke.client.core.config;
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import mjkf.xinke.common.pojo.CommonResult;
import javax.annotation.Resource;
/**
* C端相关配置
*
*
* @date 2022/7/7 16:18
**/
@Configuration
public class ClientConfigure {
@Resource
private OpenApiExtensionResolver openApiExtensionResolver;
/**
* API文档分组配置
*
*
* @date 2022/7/7 16:18
**/
@Bean(value = "clientDocApi")
public Docket clientDocApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
.title("C端功能CLIENT")
.description("C端功能CLIENT")
.termsOfServiceUrl("https://www.xiaonuo.vip")
.contact(new Contact("SNOWY_TEAM","https://www.xiaonuo.vip", "xuyuxiang29@foxmail.com"))
.version("2.0.0")
.build())
.useDefaultResponseMessages(false)
.globalResponseMessage(RequestMethod.GET, CommonResult.responseList())
.globalResponseMessage(RequestMethod.POST, CommonResult.responseList())
.groupName("C端功能CLIENT")
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.apis(RequestHandlerSelectors.basePackage("mjkf.xinke.client"))
.paths(PathSelectors.any())
.build().extensions(openApiExtensionResolver.buildExtensions("C端功能CLIENT"));
}
}

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