前端地址: https://gitee.com/yrki/colud-office-front
云上办公系统是一套自动办公系统,系统主要包含:管理端和员工端
管理端包含:权限管理、审批管理、公众号菜单管理
员工端采用微信公众号操作,包含:办公审批、微信授权登录、消息推送等功能
项目服务器端架构:SpringBoot + MyBatisPlus + SpringSecurity + Redis + Activiti+ MySQL
前端架构:vue-admin-template + Node.js + Npm + Vue + ElementUI + Axios
核心技术 基础框架:SpringBoot 数据缓存:Redis 数据库:MySQL 权限控制:SpringSecurity 工作流引擎:Activiti 前端技术:vue-admin-template + Node.js + Npm + Vue + ElementUI + Axios 微信公众号:公众号菜单 + 微信授权登录 + 消息推送
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
</parent>
<groupId>com.oumuanode</groupId>
<artifactId>open-oa</artifactId>
<packaging>pom</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>open-oa</name>
<description>open-oa</description>
<modules>
<module>common</module>
<module>model</module>
<module>service-oa</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<mybatis-plus.version>3.4.1</mybatis-plus.version>
<mysql.version>8.0.30</mysql.version>
<knife4j.version>3.0.3</knife4j.version>
<jwt.version>0.9.1</jwt.version>
<fastjson.version>2.0.21</fastjson.version>
</properties>
<!--配置dependencyManagement锁定依赖的版本-->
<dependencyManagement>
<dependencies>
<!--mybatis-plus 持久层-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!--jjwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>open-oa</artifactId>
<groupId>com.oumuanode</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common</artifactId>
<packaging>pom</packaging>
<modules>
<module>common-util</module>
<module>service-util</module>
<module>spring-security</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>common</artifactId>
<groupId>com.oumuanode</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-util</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--<scope>provided </scope>-->
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
</project>
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>common</artifactId>
<groupId>com.oumuanode</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service-util</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.oumuanode</groupId>
<artifactId>common-util</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>common</artifactId>
<groupId>com.oumuanode</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-security</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.oumuanode</groupId>
<artifactId>common-util</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.oumuanode</groupId>
<artifactId>model</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring Security依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided </scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
</project>
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>open-oa</artifactId>
<groupId>com.oumuanode</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>model</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--lombok用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<scope>provided </scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<scope>provided </scope>
</dependency>
</dependencies>
</project>
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>open-oa</artifactId>
<groupId>com.oumuanode</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>service-oa</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.oumuanode</groupId>
<artifactId>model</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.oumuanode</groupId>
<artifactId>service-util</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>com.oumuanode</groupId>
<artifactId>spring-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--引入activiti的springboot启动器 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
<exclusions>
<exclusion>
<artifactId>mybatis</artifactId>
<groupId>org.mybatis</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-modeler</artifactId>
<version>5.22.0</version>
</dependency>
<!-- Activiti生成流程图 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-image-generator</artifactId>
<version>5.22.0</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/*.png</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
spring:
application:
name: service-oa
profiles:
active: dev
server:
port: 8800
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看日志
mapper-locations: classpath:com/oumuanode/serviceoa/mapper/xml/*.xml
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/guigu-oa?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&nullCatalogMeansCurrent=true
# url: jdbc:mysql://localhost:3306/guigu-oa?characterEncoding=UTF-8&nullCatalogMeansCurrent=true&serverTimezone=GMT&useSSL=false
username: root
password: 123456
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
redis:
host: localhost
port: 6379
database: 0
timeout: 1800000
password: 123456
jedis:
pool:
max-active: 20 #最大连接数
max-wait: -1 #最大阻塞等待时间(负数表示没限制)
max-idle: 5 #最大空闲
min-idle: 0 #最小空闲
activiti:
# false:默认,数据库表不变,但是如果版本不对或者缺失表会抛出异常(生产使用)
# true:表不存在,自动创建(开发使用)
# create_drop: 启动时创建,关闭时删除表(测试使用)
# drop_create: 启动时删除表,在创建表 (不需要手动关闭引擎)
database-schema-update: true
#监测历史表是否存在,activities7默认不开启历史表
db-history-used: true
async-executor-activate: false
#none:不保存任何历史数据,流程中这是最高效的
#activity:只保存流程实例和流程行为
#audit:除了activity,还保存全部的流程任务以及其属性,audit为history默认值
#full:除了audit、还保存其他全部流程相关的细节数据,包括一些流程参数
history-level: full
#校验流程文件,默认校验resources下的process 文件夹的流程文件
check-process-definitions: true
wechat:
mpAppId: wx504a4f9a2ec50a53
mpAppSecret: 55264b831a7bbbfbed9086de199da78b
# 授权回调获取用户信息接口地址
userInfoUrl: http://ggkt2.vipgz1.91tunnel.com/admin/wechat/userInfo
package com.oumuanode.serviceoa;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan("com.oumuanode")
public class ServiceOaApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceOaApplication.class, args);
}
}
controller
@Api(tags = "后台登录管理")
@RestController
@RequestMapping("/admin/system/index")
public class IndexController {
@Autowired
private SysUserService sysUserService;
@Autowired
private SysMenuService sysMenuService;
@ApiOperation("登录")
@PostMapping("/login")
public Result login(@RequestBody LoginVo loginVo) {
// 1、获取用户名和密码
// 2、根据用户名查询数据库
String username = loginVo.getUsername();
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getName, username);
SysUser sysUser = sysUserService.getOne(queryWrapper);
// 3、用户信息是否存在
if (StringUtils.isEmpty(sysUser)) {
throw new OpenException(503, "用户不存在");
}
// 4、判断密码
// 取出数据库中的密文密码(MD5)
String password = sysUser.getPassword();
String encrypt = MD5.encrypt(loginVo.getPassword());
if (!encrypt.equals(password)) {
throw new OpenException(502, "密码错误,请重新输入");
}
// 5、判断用户是否被禁用 1 可用 0 禁用
if (sysUser.getStatus().intValue() == 0) {
throw new OpenException(501, "用户被禁用,您无权登录");
}
// 6、使用jwt根据用户id和用户名称生成token的字符串
String token = JwtHelper.createToken(sysUser.getId(), sysUser.getPassword());
// 7、返回
HashMap<String, Object> map = new HashMap<>();
map.put("token", token);
return Result.ok(map);
}
@GetMapping("/info")
public Result getInfo(HttpServletRequest request){
//1 从请求头获取用户信息(获取请求头token字符串)
String token = request.getHeader("token");
//2 从token字符串获取用户id 或者 用户名称
Long userId = JwtHelper.getUserId(token);
//3 根据用户id查询数据库,把用户信息获取出来
SysUser sysUser = sysUserService.getById(userId);
//4 根据用户id获取用户可以操作菜单列表
//查询数据库动态构建路由结构,进行显示
List<RouterVo> routerList = sysMenuService.findUserMenuListByUserId(userId);
//5 根据用户id获取用户可以操作按钮列表
List<String> permsList = sysMenuService.findUserPermsByUserId(userId);
//6 返回相应的数据
Map<String, Object> map = new HashMap<>();
map.put("roles","[admin]");
map.put("name",sysUser.getName());
map.put("avatar","https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
//返回用户可以操作菜单
map.put("routers",routerList);
//返回用户可以操作按钮
map.put("buttons",permsList);
return Result.ok(map);
}
@PostMapping("logout")
public Result logout(){
return Result.ok();
}
}
service
/**
* <p>
* 用户表 服务类
* </p>
*
*/
public interface SysUserService extends IService<SysUser> {
//更新状态
void updateStatus(Long id, Integer status);
//根据用户名进行查询
SysUser getUserByUserName(String username);
Map<String, Object> getCurrentUser();
}
/**
* <p>
* 菜单表 服务类
* </p>
*
*/
public interface SysMenuService extends IService<SysMenu> {
//菜单列表接口
List<SysMenu> findNodes();
//删除菜单
void removeMenuById(Long id);
//查询所有菜单和角色分配的菜单
List<SysMenu> findMenuByRoleId(Long roleId);
//角色分配菜单
void doAssign(AssginMenuVo assginMenuVo);
//4 根据用户id获取用户可以操作菜单列表
List<RouterVo> findUserMenuListByUserId(Long userId);
//5 根据用户id获取用户可以操作按钮列表
List<String> findUserPermsByUserId(Long userId);
}
vo
/**
* 登录对象
*/
public class LoginVo {
/**
* 手机号
*/
private String username;
/**
* 密码
*/
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
全局异常
@ControllerAdvice
public class GlobalExceptionHandler {
//全局异常处理,执行的方法
@ExceptionHandler(Exception.class)
@ResponseBody
public Result error(Exception e) {
e.printStackTrace();
return Result.fail().message("执行全局异常处理...");
}
//特定异常处理
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public Result error(ArithmeticException e) {
e.printStackTrace();
return Result.fail().message("执行特定异常处理...");
}
//自定义异常处理
@ExceptionHandler(OpenException.class)
@ResponseBody
public Result error(OpenException e) {
e.printStackTrace();
return Result.fail().code(e.getCode()).message(e.getMsg());
}
/**
* spring security异常
* @param e
* @return
*/
@ExceptionHandler(AccessDeniedException.class)
@ResponseBody
public Result error(AccessDeniedException e) throws AccessDeniedException {
return Result.fail().code(205).message("没有操作权限");
}
}
自定义异常
@Data
public class OpenException extends RuntimeException{ private Integer code;//状态码
private String msg;//描述信息
public OpenException(Integer code,String msg) {
super(msg);
this.code = code;
this.msg = msg;
}
/**
* 接收枚举类型对象
* @param resultCodeEnum
*/
public OpenException(ResultCodeEnum resultCodeEnum) {
super(resultCodeEnum.getMessage());
this.code = resultCodeEnum.getCode();
this.msg = resultCodeEnum.getMessage();
}
@Override
public String toString() {
return "OpenException{" +
"code=" + code +
", message=" + this.getMessage() +
'}';
}
}
knife4j
/**
* knife4j配置信息
*/
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfig {
@Bean
public Docket adminApiConfig() {
List<Parameter> pars = new ArrayList<>();
ParameterBuilder tokenPar = new ParameterBuilder();
tokenPar.name("token")
.description("用户token")
.defaultValue("")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build();
pars.add(tokenPar.build());
//添加head参数end
Docket adminApi = new Docket(DocumentationType.SWAGGER_2)
.groupName("adminApi")
.apiInfo(adminApiInfo())
.select()
//只显示admin路径下的页面
.apis(RequestHandlerSelectors.basePackage("com.oumuanode"))
.paths(PathSelectors.regex("/admin/.*"))
.build()
.globalOperationParameters(pars);
return adminApi;
}
private ApiInfo adminApiInfo() {
return new ApiInfoBuilder()
.title("后台管理系统-API文档")
.description("本文档描述了后台管理系统微服务接口定义")
.version("1.0")
.contact(new Contact("oumuanode", "1.12.69.119", "[email protected]"))
.build();
}
}
MP
@Configuration
@MapperScan("com.oumuanode.serviceoa.mapper")
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
springsecurity
config
@Configuration
@EnableWebSecurity //@EnableWebSecurity是开启SpringSecurity的默认行为
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private CustomMd5PasswordEncoder customMd5PasswordEncoder;
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 这是配置的关键,决定哪些接口开启防护,哪些接口绕过防护
http
//关闭csrf跨站请求伪造
.csrf().disable()
// 开启跨域以便前端调用接口
.cors().and()
.authorizeRequests()
// 指定某些接口不需要通过验证即可访问。登陆接口肯定是不需要认证的
//.antMatchers("/admin/system/index/login").permitAll()
// 这里意思是其它所有接口需要认证才能访问
.anyRequest().authenticated()
.and()
//TokenAuthenticationFilter放到UsernamePasswordAuthenticationFilter的前面,这样做就是为了除了登录的时候去查询数据库外,其他时候都用token进行认证。
.addFilterBefore(new TokenAuthenticationFilter(redisTemplate),
UsernamePasswordAuthenticationFilter.class)
.addFilter(new TokenLoginFilter(authenticationManager(),redisTemplate));
//禁用session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 指定UserDetailService和加密器
auth.userDetailsService(userDetailsService).passwordEncoder(customMd5PasswordEncoder);
}
/**
* 配置哪些请求不拦截
* 排除swagger相关请求
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/admin/modeler/**","/diagram-viewer/**","/editor-app/**","/*.html",
"/admin/processImage/**",
"/admin/wechat/authorize","/admin/wechat/userInfo","/admin/wechat/bindPhone",
"/favicon.ico","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/doc.html");
}
}
自定义处理类
@Component
public class CustomMd5PasswordEncoder implements PasswordEncoder {
public String encode(CharSequence rawPassword) {
return MD5.encrypt(rawPassword.toString());
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(MD5.encrypt(rawPassword.toString()));
}
}
public class CustomUser extends User {
/**
* 我们自己的用户实体对象,要调取用户信息时直接获取这个实体对象。(这里我就不写get/set方法了)
*/
private SysUser sysUser;
public CustomUser(SysUser sysUser, Collection<? extends GrantedAuthority> authorities) {
super(sysUser.getUsername(), sysUser.getPassword(), authorities);
this.sysUser = sysUser;
}
public SysUser getSysUser() {
return sysUser;
}
public void setSysUser(SysUser sysUser) {
this.sysUser = sysUser;
}
}
public class LoginUserInfoHelper {
private static ThreadLocal<Long> userId = new ThreadLocal<Long>();
private static ThreadLocal<String> username = new ThreadLocal<String>();
public static void setUserId(Long _userId) {
userId.set(_userId);
}
public static Long getUserId() {
return userId.get();
}
public static void removeUserId() {
userId.remove();
}
public static void setUsername(String _username) {
username.set(_username);
}
public static String getUsername() {
return username.get();
}
public static void removeUsername() {
username.remove();
}
}
@Component
public interface UserDetailsService extends org.springframework.security.core.userdetails.UserDetailsService {
/**
* 根据用户名获取用户对象(获取不到直接抛异常)
*/
@Override
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
过滤器
public class TokenAuthenticationFilter extends OncePerRequestFilter {
private RedisTemplate redisTemplate;
public TokenAuthenticationFilter(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
//如果是登录接口,直接放行
if("/admin/system/index/login".equals(request.getRequestURI())) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
if(null != authentication) {
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
} else {
ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_ERROR));
}
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
//请求头是否有token
String token = request.getHeader("token");
if(!StringUtils.isEmpty(token)) {
String username = JwtHelper.getUsername(token);
if(!StringUtils.isEmpty(username)) {
//当前用户信息放到ThreadLocal里面
LoginUserInfoHelper.setUserId(JwtHelper.getUserId(token));
LoginUserInfoHelper.setUsername(username);
//通过username从redis获取权限数据
String authString = (String)redisTemplate.opsForValue().get(username);
//把redis获取字符串权限数据转换要求集合类型 List<SimpleGrantedAuthority>
if(!StringUtils.isEmpty(authString)) {
List<Map> maplist = JSON.parseArray(authString, Map.class);
System.out.println(maplist);
List<SimpleGrantedAuthority> authList = new ArrayList<>();
for (Map map:maplist) {
String authority = (String)map.get("authority");
authList.add(new SimpleGrantedAuthority(authority));
}
return new UsernamePasswordAuthenticationToken(username,null, authList);
} else {
return new UsernamePasswordAuthenticationToken(username,null, new ArrayList<>());
}
}
}
return null;
}
}
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
private RedisTemplate redisTemplate;
//构造方法
public TokenLoginFilter(AuthenticationManager authenticationManager,
RedisTemplate redisTemplate) {
this.setAuthenticationManager(authenticationManager);
this.setPostOnly(false);
//指定登录接口及提交方式,可以指定任意路径
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login","POST"));
this.redisTemplate = redisTemplate;
}
//登录认证
//获取输入的用户名和密码,调用方法认证
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response)
throws AuthenticationException {
try {
//获取用户信息
LoginVo loginVo = new ObjectMapper().readValue(request.getInputStream(), LoginVo.class);
//封装对象
Authentication authenticationToken =
new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());
//调用方法
return this.getAuthenticationManager().authenticate(authenticationToken);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//认证成功调用方法
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication auth)
throws IOException, ServletException {
//获取当前用户
CustomUser customUser = (CustomUser)auth.getPrincipal();
//生成token
String token = JwtHelper.createToken(customUser.getSysUser().getId(),
customUser.getSysUser().getUsername());
//获取当前用户权限数据,放到Redis里面 key:username value:权限数据
redisTemplate.opsForValue().set(customUser.getUsername(),
JSON.toJSONString(customUser.getAuthorities()));
//返回
Map<String,Object> map = new HashMap<>();
map.put("token",token);
ResponseUtil.out(response, Result.ok(map));
}
//认证失败调用方法
protected void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException failed)
throws IOException, ServletException {
ResponseUtil.out(response,Result.build(null, ResultCodeEnum.LOGIN_ERROR));
}
}
@Api(tags = "系统角色管理")
@RestController
@RequestMapping("/admin/system/sysRole")
public class SysRoleController {
@Autowired
private SysRoleService sysRoleService;
//1 查询所有角色 和 当前用户所属角色
@ApiOperation("获取角色")
@GetMapping("/toAssign/{userId}")
public Result toAssign(@PathVariable Long userId){
Map<String,Object> map = sysRoleService.findRoleDataByUserId(userId);
return Result.ok(map);
}
//2 为用户分配角色
@ApiOperation("为用户分配角色")
@PostMapping("/doAssign")
public Result doAssign(@RequestBody AssginRoleVo assginRoleVo) {
sysRoleService.doAssign(assginRoleVo);
return Result.ok();
}
@ApiOperation("查询所有角色")
@GetMapping("/findAll")
public Result findAll() {
//调用service的方法
List<SysRole> list = sysRoleService.list();
//模拟异常效果
try {
int i = 10/0;
} catch (Exception e) {
//抛出自定义异常
throw new OpenException(20001,"执行了自定义异常处理..");
}
return Result.ok(list);
}
//条件分页查询
//page 当前页 limit 每页显示记录数
//SysRoleQueryVo 条件对象
@PreAuthorize("hasAuthority('bnt.sysRole.list')")
@ApiOperation("条件分页查询")
@GetMapping("{page}/{limit}")
public Result pageQueryRole(@PathVariable Long page,
@PathVariable Long limit,
SysRoleQueryVo sysRoleQueryVo) {
//调用service的方法实现
//1 创建Page对象,传递分页相关参数
//page 当前页 limit 每页显示记录数
Page<SysRole> pageParam = new Page<>(page, limit);
//2 封装条件,判断条件是否为空,不为空进行封装
LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>();
String roleName = sysRoleQueryVo.getRoleName();
if (!StringUtils.isEmpty(roleName)) {
queryWrapper.like(SysRole::getRoleName,roleName);
}
//3 调用方法实现
IPage<SysRole> pageModel = sysRoleService.page(pageParam,
queryWrapper);
return Result.ok(pageModel);
}
//添加角色
@PreAuthorize("hasAuthority('bnt.sysRole.add')")
@ApiOperation("添加角色")
@PostMapping ("save")
public Result save(@RequestBody SysRole role) {
//调用service的方法
boolean is_success = sysRoleService.save(role);
if(is_success) {
return Result.ok();
} else {
return Result.fail();
}
}
//修改角色-根据id查询
@PreAuthorize("hasAuthority('bnt.sysRole.list')")
@ApiOperation("根据id查询")
@GetMapping("get/{id}")
public Result get(@PathVariable Long id) {
SysRole sysRole = sysRoleService.getById(id);
return Result.ok(sysRole);
}
//修改角色-最终修改
@PreAuthorize("hasAuthority('bnt.sysRole.update')")
@ApiOperation("修改角色")
@PutMapping ("update")
public Result update(@RequestBody SysRole role) {
//调用service的方法
boolean is_success = sysRoleService.updateById(role);
if(is_success) {
return Result.ok();
} else {
return Result.fail();
}
}
//根据id删除
@PreAuthorize("hasAuthority('bnt.sysRole.remove')")
@ApiOperation("根据id删除")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
boolean is_success = sysRoleService.removeById(id);
if(is_success) {
return Result.ok();
} else {
return Result.fail();
}
}
//批量删除
// 前端数组 [1,2,3]
@PreAuthorize("hasAuthority('bnt.sysRole.remove')")
@ApiOperation("批量删除")
@DeleteMapping("batchRemove")
public Result batchRemove(@RequestBody List<Long> idList) {
boolean is_success = sysRoleService.removeByIds(idList);
if(is_success) {
return Result.ok();
} else {
return Result.fail();
}
}
}
interface
public interface SysRoleService extends IService<SysRole> {
//1 查询所有角色 和 当前用户所属角色
Map<String, Object> findRoleDataByUserId(Long userId);
//2 为用户分配角色
void doAssign(AssginRoleVo assginRoleVo);
}
service
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {
@Autowired
private SysUserRoleService sysUserRoleService;
//1 查询所有角色 和 当前用户所属角色
@Override
public Map<String, Object> findRoleDataByUserId(Long userId) {
//1 查询所有角色,返回list集合,返回
List<SysRole> allRoleList = baseMapper.selectList(null);
LambdaQueryWrapper<SysUserRole> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUserRole::getUserId,userId);
List<SysUserRole> existUserRoleList = sysUserRoleService.list(queryWrapper);
List<Long> collect = existUserRoleList.stream().map(c -> c.getRoleId()).collect(Collectors.toList());
//3 根据查询所有角色id,找到对应角色信息
//根据角色id到所有的角色的list集合进行比较
List<SysRole> assignRoleList = new ArrayList<>();
for(SysRole sysRole : allRoleList) {
//比较
if(existUserRoleList.contains(sysRole.getId())) {
assignRoleList.add(sysRole);
}
}
//4 把得到两部分数据封装map集合,返回
Map<String, Object> roleMap = new HashMap<>();
roleMap.put("assginRoleList", assignRoleList);
roleMap.put("allRolesList", allRoleList);
return roleMap;
}
//2 为用户分配角色
@Override
public void doAssign(AssginRoleVo assginRoleVo) {
//把用户之前分配角色数据删除,用户角色关系表里面,根据userid删除
LambdaQueryWrapper<SysUserRole> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUserRole::getUserId,assginRoleVo.getUserId());
sysUserRoleService.remove(wrapper);
//重新进行分配
List<Long> roleIdList = assginRoleVo.getRoleIdList();
for (Long roleId : roleIdList) {
if (StringUtils.isEmpty(roleId)){
continue;
}
SysUserRole sysUserRole = new SysUserRole();
sysUserRole.setUserId(assginRoleVo.getUserId());
sysUserRole.setRoleId(roleId);
sysUserRoleService.save(sysUserRole);
}
}
}
vo
@ApiModel(description = "分配菜单")
@Data
public class AssginRoleVo {
@ApiModelProperty(value = "用户id")
private Long userId;
@ApiModelProperty(value = "角色id列表")
private List<Long> roleIdList;
}
controller
@Api(tags = "菜单管理接口")
@RestController
@RequestMapping("/admin/system/sysMenu")
public class SysMenuController {
@Autowired
private SysMenuService sysMenuService;
//查询所有菜单和角色分配的菜单
@ApiOperation("查询所有菜单和角色分配的菜单")
@GetMapping("toAssign/{roleId}")
public Result toAssign(@PathVariable Long roleId) {
List<SysMenu> list = sysMenuService.findMenuByRoleId(roleId);
return Result.ok(list);
}
@ApiOperation("角色分配菜单")
@PostMapping("/doAssign")
public Result doAssign(@RequestBody AssginMenuVo assginMenuVo) {
sysMenuService.doAssign(assginMenuVo);
return Result.ok();
}
//菜单列表接口
@ApiOperation("菜单列表")
@GetMapping("findNodes")
public Result findNodes() {
List<SysMenu> list = sysMenuService.findNodes();
return Result.ok(list);
}
@ApiOperation(value = "新增菜单")
@PostMapping("save")
public Result save(@RequestBody SysMenu sysMenu) {
sysMenuService.save(sysMenu);
return Result.ok();
}
@ApiOperation(value = "修改菜单")
@PutMapping("update")
public Result updateById(@RequestBody SysMenu sysMenu) {
sysMenuService.updateById(sysMenu);
return Result.ok();
}
@ApiOperation(value = "删除菜单")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
sysMenuService.removeMenuById(id);
return Result.ok();
}
}
interface
/**
* <p>
* 菜单表 服务类
* </p>
*
*/
public interface SysMenuService extends IService<SysMenu> {
//菜单列表接口
List<SysMenu> findNodes();
//删除菜单
void removeMenuById(Long id);
//查询所有菜单和角色分配的菜单
List<SysMenu> findMenuByRoleId(Long roleId);
//角色分配菜单
void doAssign(AssginMenuVo assginMenuVo);
//4 根据用户id获取用户可以操作菜单列表
List<RouterVo> findUserMenuListByUserId(Long userId);
//5 根据用户id获取用户可以操作按钮列表
List<String> findUserPermsByUserId(Long userId);
}
service
/**
* <p>
* 菜单表 服务实现类
* </p>
*/
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
@Autowired
private SysRoleMenuService sysRoleMenuService;
@Override
public List<SysMenu> findNodes() {
//1 查询所有菜单数据
List<SysMenu> sysMenuList = baseMapper.selectList(null);
List<SysMenu> sysMenus = MenuHelper.buildTree(sysMenuList);
return sysMenus;
}
@Override
public void removeMenuById(Long id) {
//判断当前菜单是否有下一层菜单
LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysMenu::getParentId,id);
Integer count = baseMapper.selectCount(wrapper);
if (count > 0){
throw new OpenException(201,"菜单不能删除");
}
baseMapper.deleteById(id);
}
//查询所有菜单和角色分配的菜单
@Override
public List<SysMenu> findMenuByRoleId(Long roleId) {
//1 查询所有菜单- 添加条件 status=1
LambdaQueryWrapper<SysMenu> wrapperSysMenu = new LambdaQueryWrapper<>();
wrapperSysMenu.eq(SysMenu::getStatus,1);
List<SysMenu> allSysMenuList = baseMapper.selectList(wrapperSysMenu);
//2 根据角色id roleId查询 角色菜单关系表里面 角色id对应所有的菜单id
LambdaQueryWrapper<SysRoleMenu> wrapperSysRoleMenu = new LambdaQueryWrapper<>();
wrapperSysRoleMenu.eq(SysRoleMenu::getRoleId,roleId);
List<SysRoleMenu> sysRoleMenuList = sysRoleMenuService.list(wrapperSysRoleMenu);
//3 根据获取菜单id,获取对应菜单对象
List<Long> menuIdList = sysRoleMenuList.stream().map(c -> c.getMenuId()).collect(Collectors.toList());
//3.1 拿着菜单id 和所有菜单集合里面id进行比较,如果相同封装
allSysMenuList.stream().forEach(item -> {
if(menuIdList.contains(item.getId())) {
item.setSelect(true);
} else {
item.setSelect(false);
}
});
//4 返回规定树形显示格式菜单列表
List<SysMenu> sysMenuList = MenuHelper.buildTree(allSysMenuList);
return sysMenuList;
}
@Override
public void doAssign(AssginMenuVo assginMenuVo) {
//1 根据角色id 删除菜单角色表 分配数据
LambdaQueryWrapper<SysRoleMenu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysRoleMenu::getRoleId,assginMenuVo.getRoleId());
sysRoleMenuService.remove(wrapper);
//2 从参数里面获取角色新分配菜单id列表,
// 进行遍历,把每个id数据添加菜单角色表
List<Long> menuIdList = assginMenuVo.getMenuIdList();
for(Long menuId:menuIdList) {
if(StringUtils.isEmpty(menuId)) {
continue;
}
SysRoleMenu sysRoleMenu = new SysRoleMenu();
sysRoleMenu.setMenuId(menuId);
sysRoleMenu.setRoleId(assginMenuVo.getRoleId());
sysRoleMenuService.save(sysRoleMenu);
}
}
//4 根据用户id获取用户可以操作菜单列表
@Override
public List<RouterVo> findUserMenuListByUserId(Long userId) {
List<SysMenu> sysMenuList = null;
//1 判断当前用户是否是管理员 userId=1是管理员
//1.1 如果是管理员,查询所有菜单列表
if(userId.longValue() == 1) {
//查询所有菜单列表
LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysMenu::getStatus,1);
wrapper.orderByAsc(SysMenu::getSortValue);
sysMenuList = baseMapper.selectList(wrapper);
} else {
//1.2 如果不是管理员,根据userId查询可以操作菜单列表
//多表关联查询:用户角色关系表 、 角色菜单关系表、 菜单表
sysMenuList = baseMapper.findMenuListByUserId(userId);
}
//2 把查询出来数据列表-构建成框架要求的路由结构
//使用菜单操作工具类构建树形结构
List<SysMenu> sysMenuTreeList = MenuHelper.buildTree(sysMenuList);
//构建成框架要求的路由结构
List<RouterVo> routerList = this.buildRouter(sysMenuTreeList);
return routerList;
}
//构建成框架要求的路由结构
private List<RouterVo> buildRouter(List<SysMenu> menus) {
//创建list集合,存储最终数据
List<RouterVo> routers = new ArrayList<>();
//menus遍历
for(SysMenu menu : menus) {
RouterVo router = new RouterVo();
router.setHidden(false);
router.setAlwaysShow(false);
router.setPath(getRouterPath(menu));
router.setComponent(menu.getComponent());
router.setMeta(new MetaVo(menu.getName(), menu.getIcon()));
//下一层数据部分
List<SysMenu> children = menu.getChildren();
if(menu.getType().intValue() == 1) {
//加载出来下面隐藏路由
List<SysMenu> hiddenMenuList = children.stream()
.filter(item -> !StringUtils.isEmpty(item.getComponent()))
.collect(Collectors.toList());
for(SysMenu hiddenMenu : hiddenMenuList) {
RouterVo hiddenRouter = new RouterVo();
//true 隐藏路由
hiddenRouter.setHidden(true);
hiddenRouter.setAlwaysShow(false);
hiddenRouter.setPath(getRouterPath(hiddenMenu));
hiddenRouter.setComponent(hiddenMenu.getComponent());
hiddenRouter.setMeta(new MetaVo(hiddenMenu.getName(), hiddenMenu.getIcon()));
routers.add(hiddenRouter);
}
} else {
if(!CollectionUtils.isEmpty(children)) {
if(children.size() > 0) {
router.setAlwaysShow(true);
}
//递归
router.setChildren(buildRouter(children));
}
}
routers.add(router);
}
return routers;
}
/**
* 获取路由地址
*
* @param menu 菜单信息
* @return 路由地址
*/
public String getRouterPath(SysMenu menu) {
String routerPath = "/" + menu.getPath();
if(menu.getParentId().intValue() != 0) {
routerPath = menu.getPath();
}
return routerPath;
}
//5 根据用户id获取用户可以操作按钮列表
@Override
public List<String> findUserPermsByUserId(Long userId) {
//1 判断是否是管理员,如果是管理员,查询所有按钮列表
List<SysMenu> sysMenuList = null;
if(userId.longValue() == 1) {
//查询所有菜单列表
LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysMenu::getStatus,1);
sysMenuList = baseMapper.selectList(wrapper);
} else {
//2 如果不是管理员,根据userId查询可以操作按钮列表
//多表关联查询:用户角色关系表 、 角色菜单关系表、 菜单表
sysMenuList = baseMapper.findMenuListByUserId(userId);
}
//3 从查询出来的数据里面,获取可以操作按钮值的list集合,返回
List<String> permsList = sysMenuList.stream()
.filter(item -> item.getType() == 2)
.map(item -> item.getPerms())
.collect(Collectors.toList());
return permsList;
}
}
public class MenuHelper{
public static List<SysMenu> buildTree(List<SysMenu> sysMenuList) {
ArrayList<SysMenu> trees = new ArrayList<>();
for (SysMenu sysMenu : sysMenuList) {
if (sysMenu.getParentId().longValue() == 0) {
trees.add(getChildren(sysMenu,sysMenuList));
}
}
return trees;
}
public static SysMenu getChildren(SysMenu sysMenu,
List<SysMenu> sysMenuList) {
sysMenu.setChildren(new ArrayList<SysMenu>());
//遍历所有菜单数据,判断 id 和 parentId对应关系
for(SysMenu it: sysMenuList) {
if(sysMenu.getId().longValue() == it.getParentId().longValue()) {
if (sysMenu.getChildren() == null) {
sysMenu.setChildren(new ArrayList<>());
}
sysMenu.getChildren().add(getChildren(it,sysMenuList));
}
}
return sysMenu;
}
}
controller
@Api(tags = "用户管理接口")
@RestController
@RequestMapping("/admin/system/sysUser")
public class SysUserController {
@Autowired
private SysUserService sysUserService;
@ApiOperation(value = "更新状态")
@GetMapping("updateStatus/{id}/{status}")
public Result updateStatus(@PathVariable Long id, @PathVariable Integer status){
sysUserService.updateStatus(id, status);
return Result.ok();
}
//用户条件分页查询
@ApiOperation("用户条件分页查询")
@GetMapping("{page}/{limit}")
public Result index(@PathVariable Long page,
@PathVariable Long limit,
SysUserQueryVo sysUserQueryVo) {
Page<SysUser> pageParam = new Page(page, limit);
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
//获取条件值
String username = sysUserQueryVo.getKeyword();
String createTimeBegin = sysUserQueryVo.getCreateTimeBegin();
String createTimeEnd = sysUserQueryVo.getCreateTimeEnd();
//判断条件值不为空
//like 模糊查询
if (!StringUtils.isEmpty(username)) {
queryWrapper.like(SysUser::getUsername, username);
}
//ge 大于等于
if (!StringUtils.isEmpty(createTimeBegin)) {
queryWrapper.ge(SysUser::getCreateTime, createTimeBegin);
}
//le 小于等于
if (!StringUtils.isEmpty(createTimeEnd)) {
queryWrapper.le(SysUser::getCreateTime, createTimeEnd);
}
Page<SysUser> page1 = sysUserService.page(pageParam, queryWrapper);
return Result.ok(page1);
}
@ApiOperation(value = "获取用户")
@GetMapping("get/{id}")
public Result get(@PathVariable Long id) {
SysUser user = sysUserService.getById(id);
return Result.ok(user);
}
@ApiOperation(value = "保存用户")
@PostMapping("save")
public Result save(@RequestBody SysUser user) {
//密码进行加密,使用MD5
String passwordMD5 = MD5.encrypt(user.getPassword());
user.setPassword(passwordMD5);
sysUserService.save(user);
return Result.ok();
}
@ApiOperation(value = "更新用户")
@PutMapping("update")
public Result updateById(@RequestBody SysUser user) {
sysUserService.updateById(user);
return Result.ok();
}
@ApiOperation(value = "删除用户")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
sysUserService.removeById(id);
return Result.ok();
}
}
interface
/**
* <p>
* 用户表 服务类
* </p>
*
*/
public interface SysUserService extends IService<SysUser> {
//更新状态
void updateStatus(Long id, Integer status);
//根据用户名进行查询
SysUser getUserByUserName(String username);
Map<String, Object> getCurrentUser();
}
service
/**
* <p>
* 用户表 服务实现类
* </p>
*
*/
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
@Override
public void updateStatus(Long id, Integer status) {
SysUser sysUser = baseMapper.selectById(id);
sysUser.setStatus(status);
baseMapper.updateById(sysUser);
}
@Override
public SysUser getUserByUserName(String username) {
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getUsername,username);
SysUser sysUser = baseMapper.selectOne(wrapper);
return sysUser;
}
@Override
public Map<String, Object> getCurrentUser() {
SysUser sysUser = baseMapper.selectById(LoginUserInfoHelper.getUserId());
Map<String, Object> map = new HashMap<>();
map.put("name", sysUser.getName());
map.put("phone", sysUser.getPhone());
return map;
}
}
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private SysUserService sysUserService;
@Autowired
private SysMenuService sysMenuService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserService.getUserByUserName(username);
if(null == sysUser) {
throw new UsernameNotFoundException("用户名不存在!");
}
if(sysUser.getStatus().intValue() == 0) {
throw new RuntimeException("账号已停用");
}
//根据userid查询用户操作权限数据
List<String> userPermsList = sysMenuService.findUserPermsByUserId(sysUser.getId());
//创建list集合,封装最终权限数据
List<SimpleGrantedAuthority> authList = new ArrayList<>();
//查询list集合遍历
for (String perm : userPermsList) {
authList.add(new SimpleGrantedAuthority(perm.trim()));
}
return new CustomUser(sysUser, authList);
}
}
/**
* <p>
* 审批类型 前端控制器
* </p>
*/
@RestController
@RequestMapping(value = "/admin/process")
public class OaProcessController {
@Autowired
private OaProcessService processService;
//审批管理列表
@ApiOperation(value = "获取分页列表")
@GetMapping("{page}/{limit}")
public Result index(@PathVariable Long page,
@PathVariable Long limit,
ProcessQueryVo processQueryVo) {
Page<ProcessVo> pageParam = new Page<>(page,limit);
IPage<ProcessVo> pageModel =
processService.selectPage(pageParam,processQueryVo);
return Result.ok(pageModel);
}
}
/**
* <p>
* 审批模板 前端控制器
* </p>
*/
@RestController
@RequestMapping(value = "/admin/process/processTemplate")
public class OaProcessTemplateController {
@Autowired
private OaProcessTemplateService oaProcessTemplateService;
@ApiOperation("获取分页审批模版数据")
@GetMapping("{page}/{limit}")
public Result index(@PathVariable Long page, @PathVariable int limit){
Page<ProcessTemplate> pageParam = new Page<>(page,limit);
IPage<ProcessTemplate> pageModel = oaProcessTemplateService.selectPageProcessTempate(pageParam);
return Result.ok(pageModel);
}
//@PreAuthorize("hasAuthority('bnt.processTemplate.list')")
@ApiOperation(value = "获取")
@GetMapping("get/{id}")
public Result get(@PathVariable Long id) {
ProcessTemplate processTemplate = oaProcessTemplateService.getById(id);
return Result.ok(processTemplate);
}
//@PreAuthorize("hasAuthority('bnt.processTemplate.templateSet')")
@ApiOperation(value = "新增")
@PostMapping("save")
public Result save(@RequestBody ProcessTemplate processTemplate) {
oaProcessTemplateService.save(processTemplate);
return Result.ok();
}
//@PreAuthorize("hasAuthority('bnt.processTemplate.templateSet')")
@ApiOperation(value = "修改")
@PutMapping("update")
public Result updateById(@RequestBody ProcessTemplate processTemplate) {
oaProcessTemplateService.updateById(processTemplate);
return Result.ok();
}
//@PreAuthorize("hasAuthority('bnt.processTemplate.remove')")
@ApiOperation(value = "删除")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
oaProcessTemplateService.removeById(id);
return Result.ok();
}
@ApiOperation(value = "上传流程定义")
@PostMapping("/uploadProcessDefinition")
public Result uploadProcessDefinition(MultipartFile file) throws FileNotFoundException {
//获取classes目录位置
String path = new File(ResourceUtils.getURL("classpath:")
.getPath()).getAbsolutePath();
//设置上传文件夹
File tempFile = new File(path + "/processes/");
if(!tempFile.exists()) {
tempFile.mkdirs();
}
//创建空文件,实现文件写入
String filename = file.getOriginalFilename();
File zipFile = new File(path + "/processes/" + filename);
//保存文件
try {
file.transferTo(zipFile);
} catch (IOException e) {
return Result.fail();
}
Map<String, Object> map = new HashMap<>();
//根据上传地址后续部署流程定义,文件名称为流程定义的默认key
map.put("processDefinitionPath", "processes/" + filename);
map.put("processDefinitionKey", filename.substring(0, filename.lastIndexOf(".")));
return Result.ok(map);
}
//部署流程定义(发布)
@ApiOperation(value = "发布")
@GetMapping("/publish/{id}")
public Result publish(@PathVariable Long id) {
//修改模板发布状态 1 已经发布
//流程定义部署
oaProcessTemplateService.publish(id);
return Result.ok();
}
public static void main(String[] args) {
try {
String path = new File(ResourceUtils.getURL("classpath:")
.getPath()).getAbsolutePath();
System.out.println(path);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
}
/**
* <p>
* 审批类型 前端控制器
* </p>
*/
@RestController
@RequestMapping(value = "/admin/process/processType")
public class OaProcessTypeController {
@Autowired
private OaProcessTypeService processTypeService;
//查询所有审批分类
@GetMapping("findAll")
public Result findAll(){
List<ProcessType> list = processTypeService.list();
return Result.ok(list);
}
@ApiOperation(value = "获取分页列表")
@GetMapping("{page}/{limit}")
public Result index(@PathVariable Long page, @PathVariable Long limit){
Page<ProcessType> processTypePage = new Page<>(page,limit);
IPage<ProcessType> pageModel = processTypeService.page(processTypePage);
return Result.ok(pageModel);
}
@ApiOperation(value = "获取")
@GetMapping("get/{id}")
public Result get(@PathVariable Long id) {
ProcessType processType = processTypeService.getById(id);
return Result.ok(processType);
}
@ApiOperation(value = "新增")
@PostMapping("save")
public Result save(@RequestBody ProcessType processType) {
processTypeService.save(processType);
return Result.ok();
}
@ApiOperation(value = "修改")
@PutMapping("update")
public Result updateById(@RequestBody ProcessType processType) {
processTypeService.updateById(processType);
return Result.ok();
}
@ApiOperation(value = "删除")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id){
processTypeService.removeById(id);
return Result.ok();
}
}
interface
/**
* <p>
* 审批模板 服务类
* </p>
*/
public interface OaProcessTemplateService extends IService<ProcessTemplate> {
IPage<ProcessTemplate> selectPageProcessTempate(Page<ProcessTemplate> pageParam);
void publish(Long id);
}
service
/**
* <p>
* 审批模板 服务实现类
* </p>
*/
@Service
public class OaProcessTemplateServiceImpl extends ServiceImpl<OaProcessTemplateMapper, ProcessTemplate> implements OaProcessTemplateService {
@Autowired
private OaProcessTypeService processTypeService;
@Autowired
private OaProcessService processService;
//分页查询审批模板,把审批类型对应名称查询
@Override
public IPage<ProcessTemplate> selectPageProcessTempate(Page<ProcessTemplate> pageParam) {
//1 调用mapper的方法实现分页查询
Page<ProcessTemplate> processTemplatePage = baseMapper.selectPage(pageParam, null);
//2 第一步分页查询返回分页数据,从分页数据获取列表list集合
List<ProcessTemplate> processTemplateList = processTemplatePage.getRecords();
//3 遍历list集合,得到每个对象的审批类型id
for(ProcessTemplate processTemplate : processTemplateList) {
//得到每个对象的审批类型id
Long processTypeId = processTemplate.getProcessTypeId();
//4 根据审批类型id,查询获取对应名称
LambdaQueryWrapper<ProcessType> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ProcessType::getId,processTypeId);
ProcessType processType = processTypeService.getOne(wrapper);
if(processType == null) {
continue;
}
//5 完成最终封装processTypeName
processTemplate.setProcessTypeName(processType.getName());
}
return processTemplatePage;
}
//修改模板发布状态 1 已经发布
//流程定义部署
@Override
public void publish(Long id) {
ProcessTemplate processTemplate = baseMapper.selectById(id);
processTemplate.setStatus(1);
baseMapper.updateById(processTemplate);
if (!StringUtils.isEmpty(processTemplate.getProcessDefinitionPath())){
processService.deployByZip(processTemplate.getProcessDefinitionPath());
}
}
}
/**
* <p>
* 审批类型 服务类
* </p>
*
*/
public interface OaProcessTypeService extends IService<ProcessType> {
//查询所有审批分类和每个分类所有审批模板
List<ProcessType> findProcessType();
}
/**
* <p>
* 审批类型 服务实现类
* </p>
*/
@Service
public class OaProcessTypeServiceImpl extends ServiceImpl<OaProcessTypeMapper, ProcessType> implements OaProcessTypeService {
@Autowired
private OaProcessTemplateService processTemplateService;
//查询所有审批分类和每个分类所有审批模板
@Override
public List<ProcessType> findProcessType() {
//1 查询所有审批分类,返回list集合
List<ProcessType> processTypeList = baseMapper.selectList(null);
//2 遍历返回所有审批分类list集合
for (ProcessType processType:processTypeList) {
//3 得到每个审批分类,根据审批分类id查询对应审批模板
//审批分类id
Long typeId = processType.getId();
//根据审批分类id查询对应审批模板
LambdaQueryWrapper<ProcessTemplate> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ProcessTemplate::getProcessTypeId,typeId);
List<ProcessTemplate> processTemplateList = processTemplateService.list(wrapper);
//4 根据审批分类id查询对应审批模板数据(List)封装到每个审批分类对象里面
processType.setProcessTemplateList(processTemplateList);
}
return processTypeList;
}
}
controller
@Api(tags = "审批流管理")
@RestController
@RequestMapping(value="/admin/process")
@CrossOrigin //跨域
public class ProcessController {
@Autowired
private OaProcessTypeService processTypeService;
@Autowired
private OaProcessTemplateService processTemplateService;
@Autowired
private OaProcessService processService;
@Autowired
private SysUserService sysUserService;
@ApiOperation(value = "待处理")
@GetMapping("/findPending/{page}/{limit}")
public Result findPending(
@ApiParam(name = "page", value = "当前页码", required = true)
@PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true)
@PathVariable Long limit) {
Page<Process> pageParam = new Page<>(page,limit);
IPage<ProcessVo> pageModel = processService.findPending(pageParam);
return Result.ok(pageModel);
}
@ApiOperation(value = "启动流程")
@PostMapping("/startUp")
public Result startUp(@RequestBody ProcessFormVo processFormVo){
processService.startUp(processFormVo);
return Result.ok();
}
//获取审批模板数据
@GetMapping("getProcessTemplate/{processTemplateId}")
public Result getProcessTemplate(@PathVariable Long processTemplateId){
ProcessTemplate byId = processTemplateService.getById(processTemplateId);
return Result.ok(byId);
}
//查询所有审批分类和每个分类所有审批模板
@GetMapping("findProcessType")
public Result findProcessType(){
List<ProcessType> list = processTypeService.findProcessType();
return Result.ok(list);
}
//查看审批详情信息
@GetMapping("show/{id}")
public Result show(@PathVariable Long id) {
Map<String,Object> map = processService.show(id);
return Result.ok(map);
}
//审批
@ApiOperation(value = "审批")
@PostMapping("approve")
public Result approve(@RequestBody ApprovalVo approvalVo) {
processService.approve(approvalVo);
return Result.ok();
}
@ApiOperation(value = "已处理")
@GetMapping("/findProcessed/{page}/{limit}")
public Result findProcessed(
@ApiParam(name = "page", value = "当前页码", required = true)
@PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true)
@PathVariable Long limit) {
Page<Process> pageParam = new Page<>(page,limit);
IPage<ProcessVo> pageModel = processService.findProcessed(pageParam);
return Result.ok(pageModel);
}
@ApiOperation(value = "已发起")
@GetMapping("/findStarted/{page}/{limit}")
public Result findStarted(
@ApiParam(name = "page", value = "当前页码", required = true)
@PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true)
@PathVariable Long limit) {
Page<ProcessVo> pageParam = new Page<>(page, limit);
IPage<ProcessVo> pageModel = processService.findStarted(pageParam);
return Result.ok(pageModel);
}
@GetMapping("getCurrentUser")
public Result getCurrentUser() {
Map<String,Object> map = sysUserService.getCurrentUser();
return Result.ok(map);
}
}
interface
/**
* <p>
* 审批类型 服务类
* </p>
*/
public interface OaProcessService extends IService<Process> {
//审批管理列表
IPage<ProcessVo> selectPage(Page<ProcessVo> pageParam, ProcessQueryVo processQueryVo);
//部署流程定义
void deployByZip(String deployPath);
//启动流程
void startUp(ProcessFormVo processFormVo);
//查询待处理任务列表
IPage<ProcessVo> findPending(Page<Process> pageParam);
//查看审批详情信息
Map<String, Object> show(Long id);
//审批
void approve(ApprovalVo approvalVo);
//已处理
IPage<ProcessVo> findProcessed(Page<Process> pageParam);
//已发起
IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam);
}
service
/**
* <p>
* 审批类型 服务实现类
* </p>
*/
@Service
public class OaProcessServiceImpl extends ServiceImpl<OaProcessMapper, Process> implements OaProcessService {
@Autowired
private RepositoryService repositoryService;
@Autowired
private SysUserService sysUserService;
@Autowired
private OaProcessTemplateService processTemplateService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private OaProcessRecordService processRecordService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
@Autowired
private MessageService messageService;
//审批管理列表
@Override
public IPage<ProcessVo> selectPage(Page<ProcessVo> pageParam, ProcessQueryVo processQueryVo) {
IPage<ProcessVo> pageModel = baseMapper.selectPage(pageParam, processQueryVo);
return pageModel;
}
//部署流程定义
@Override
public void deployByZip(String deployPath) {
InputStream inputStream =
this.getClass().getClassLoader().getResourceAsStream(deployPath);
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
//部署
Deployment deployment = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.name("请假申请流程")
.deploy();
System.out.println(deployment.getId());
System.out.println(deployment.getName());
}
//启动流程
@Override
public void startUp(ProcessFormVo processFormVo) {
//1 根据当前用户id获取用户信息
SysUser sysUser = sysUserService.getById(LoginUserInfoHelper.getUserId());
//2 根据审批模板id把模板信息查询
ProcessTemplate processTemplate = processTemplateService.getById(processFormVo.getProcessTemplateId());
//3 保存提交审批信息到业务表,oa_process
Process process = new Process();
//processFormVo复制到process对象里面
BeanUtils.copyProperties(processFormVo,process);
//其他值
process.setStatus(1); //审批中
String workNo = System.currentTimeMillis() + "";
process.setProcessCode(workNo);
process.setUserId(LoginUserInfoHelper.getUserId());
process.setFormValues(processFormVo.getFormValues());
process.setTitle(sysUser.getName() + "发起" + processTemplate.getName() + "申请");
baseMapper.insert(process);
//4 启动流程实例 - RuntimeService
//4.1 流程定义key
String processDefinitionKey = processTemplate.getProcessDefinitionKey();
//4.2 业务key processId
String businessKey = String.valueOf(process.getId());
//4.3 流程参数 form表单json数据,转换map集合
String formValues = processFormVo.getFormValues();
//formData
JSONObject jsonObject = JSON.parseObject(formValues);
JSONObject formData = jsonObject.getJSONObject("formData");
//遍历formData得到内容,封装map集合
Map<String,Object> map = new HashMap<>();
for(Map.Entry<String,Object> entry:formData.entrySet()) {
map.put(entry.getKey(),entry.getValue());
}
Map<String,Object> variables = new HashMap<>();
variables.put("data",map);
//启动流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey,
businessKey, variables);
//5 查询下一个审批人
//审批人可能多个
List<Task> taskList = this.getCurrentTaskList(processInstance.getId());
List<String> nameList = new ArrayList<>();
for(Task task : taskList) {
String assigneeName = task.getAssignee();
SysUser user = sysUserService.getUserByUserName(assigneeName);
String name = user.getName();
nameList.add(name);
//推送消息
messageService.pushPendingMessage(process.getId(),user.getId(),task.getId());
}
process.setProcessInstanceId(processInstance.getId());
process.setDescription("等待"+ StringUtils.join(nameList.toArray(), ",")+"审批");
//7 业务和流程关联 更新oa_process数据
baseMapper.updateById(process);
//记录操作审批信息记录
processRecordService.record(process.getId(),1,"发起申请");
}
@Override
public IPage<ProcessVo> findPending(Page<Process> pageParam) {
//1 封装查询条件,根据当前登录的用户名称
TaskQuery query = taskService.createTaskQuery()
.taskAssignee(LoginUserInfoHelper.getUsername())
.orderByTaskCreateTime()
.desc();
//2 调用方法分页条件查询,返回list集合,待办任务集合
//listPage方法有两个参数
//第一个参数:开始位置 第二个参数:每页显示记录数
int begin = (int)((pageParam.getCurrent()-1)*pageParam.getSize());
int size = (int)pageParam.getSize();
List<Task> taskList = query.listPage(begin, size);
long totalCount = query.count();
//3 封装返回list集合数据 到 List<ProcessVo>里面
//List<Task> -- List<ProcessVo>
List<ProcessVo> processVoList = new ArrayList<>();
for(Task task : taskList) {
//从task获取流程实例id
String processInstanceId = task.getProcessInstanceId();
//根据流程实例id获取实例对象
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
//从流程实例对象获取业务key---processId
String businessKey = processInstance.getBusinessKey();
if(businessKey == null) {
continue;
}
//根据业务key获取Process对象
long processId = Long.parseLong(businessKey);
Process process = baseMapper.selectById(processId);
//Process对象 复制 ProcessVo对象
ProcessVo processVo = new ProcessVo();
BeanUtils.copyProperties(process,processVo);
processVo.setTaskId(task.getId());
//放到最终list集合processVoList
processVoList.add(processVo);
}
//4 封装返回IPage对象
IPage<ProcessVo> page = new Page<ProcessVo>(pageParam.getCurrent(),
pageParam.getSize(),totalCount);
page.setRecords(processVoList);
return page;
}
@Override
public Map<String, Object> show(Long id) {
//1 根据流程id获取流程信息Process
Process process = baseMapper.selectById(id);
//2 根据流程id获取流程记录信息
LambdaQueryWrapper<ProcessRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ProcessRecord::getProcessId,id);
List<ProcessRecord> processRecordList = processRecordService.list(wrapper);
//3 根据模板id查询模板信息
ProcessTemplate processTemplate = processTemplateService.getById(process.getProcessTemplateId());
//4 判断当前用户是否可以审批
//可以看到信息不一定能审批,不能重复审批
boolean isApprove = false;
List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
for(Task task : taskList) {
//判断任务审批人是否是当前用户
String username = LoginUserInfoHelper.getUsername();
if(task.getAssignee().equals(username)) {
isApprove = true;
}
}
//5 查询数据封装到map集合,返回
Map<String,Object> map = new HashMap<>();
map.put("process", process);
map.put("processRecordList", processRecordList);
map.put("processTemplate", processTemplate);
map.put("isApprove", isApprove);
return map;
}
@Override
public void approve(ApprovalVo approvalVo) {
//1 从approvalVo获取任务id,根据任务id获取流程变量
String taskId = approvalVo.getTaskId();
Map<String, Object> variables = taskService.getVariables(taskId);
for(Map.Entry<String,Object> entry:variables.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
//2 TODO 判断审批状态值 2?!1 应该是2 待解决
if(approvalVo.getStatus() == 1) {
//2.1 状态值 =1 审批通过
Map<String, Object> variable = new HashMap<>();
taskService.complete(taskId,variable);
} else {
//2.2 状态值 = -1 驳回,流程直接结束
this.endTask(taskId);
}
//3 记录审批相关过程信息 oa_process_record
String description = approvalVo.getStatus().intValue() ==1 ? "已通过" : "驳回";
processRecordService.record(approvalVo.getProcessId(),
approvalVo.getStatus(),description);
//4 查询下一个审批人,更新流程表记录 process表记录
Process process = baseMapper.selectById(approvalVo.getProcessId());
//查询任务
List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
if(!CollectionUtils.isEmpty(taskList)) {
List<String> assignList = new ArrayList<>();
for(Task task : taskList) {
String assignee = task.getAssignee();
SysUser sysUser = sysUserService.getUserByUserName(assignee);
assignList.add(sysUser.getName());
//TODO 公众号消息推送
}
//更新process流程信息
process.setDescription("等待" + StringUtils.join(assignList.toArray(), ",") + "审批");
process.setStatus(1);
} else {
if(approvalVo.getStatus().intValue() == 1) {
process.setDescription("审批完成(通过)");
process.setStatus(2);
} else {
process.setDescription("审批完成(驳回)");
process.setStatus(-1);
}
}
baseMapper.updateById(process);
}
@Override
public IPage<ProcessVo> findProcessed(Page<Process> pageParam) {
//封装查询条件
HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
.taskAssignee(LoginUserInfoHelper.getUsername())
.finished().orderByTaskCreateTime().desc();
//调用方法条件分页查询,返回list集合
// 开始位置 和 每页显示记录数
int begin = (int)((pageParam.getCurrent()-1)*pageParam.getSize());
int size = (int)pageParam.getSize();
List<HistoricTaskInstance> list = query.listPage(begin, size);
long totalCount = query.count();
//遍历返回list集合,封装List<ProcessVo>
List<ProcessVo> processVoList = new ArrayList<>();
for(HistoricTaskInstance item : list) {
//流程实例id
String processInstanceId = item.getProcessInstanceId();
//根据流程实例id查询获取process信息
LambdaQueryWrapper<Process> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Process::getProcessInstanceId,processInstanceId);
Process process = baseMapper.selectOne(wrapper);
if(process != null) {
// process -- processVo
ProcessVo processVo = new ProcessVo();
BeanUtils.copyProperties(process,processVo);
processVo.setTaskId("0");
//放到list
processVoList.add(processVo);
}
}
//IPage封装分页查询所有数据,返回
IPage<ProcessVo> pageModel =
new Page<ProcessVo>(pageParam.getCurrent(),pageParam.getSize(),
totalCount);
pageModel.setRecords(processVoList);
return pageModel;
}
//结束流程
private void endTask(String taskId) {
//1 根据任务id获取任务对象 Task
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
//2 获取流程定义模型 BpmnModel
BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
//3 获取结束流向节点
List<EndEvent> endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
if(CollectionUtils.isEmpty(endEventList)) {
return;
}
FlowNode endFlowNode = (FlowNode)endEventList.get(0);
//4 当前流向节点
FlowNode currentFlowNode = (FlowNode)bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
// 临时保存当前活动的原始方向
List originalSequenceFlowList = new ArrayList<>();
originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
//5 清理当前流动方向
currentFlowNode.getOutgoingFlows().clear();
//6 创建新流向
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlow");
newSequenceFlow.setSourceFlowElement(currentFlowNode);
newSequenceFlow.setTargetFlowElement(endFlowNode);
//7 当前节点指向新方向
List newSequenceFlowList = new ArrayList();
newSequenceFlowList.add(newSequenceFlow);
currentFlowNode.setOutgoingFlows(newSequenceFlowList);
//8 完成当前任务
taskService.complete(task.getId());
}
@Override
public IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam) {
ProcessQueryVo processQueryVo = new ProcessQueryVo();
processQueryVo.setUserId(LoginUserInfoHelper.getUserId());
IPage<ProcessVo> pageModel = baseMapper.selectPage(pageParam, processQueryVo);
for (ProcessVo item : pageModel.getRecords()) {
item.setTaskId("0");
}
return pageModel;
}
//当前任务列表
private List<Task> getCurrentTaskList(String id) {
List<Task> taskList = taskService.createTaskQuery().processInstanceId(id).list();
return taskList;
}
}
/**
* <p>
* 审批记录 服务实现类
* </p>
*/
@Service
public class OaProcessRecordServiceImpl extends ServiceImpl<OaProcessRecordMapper, ProcessRecord> implements OaProcessRecordService {
@Autowired
private SysUserService sysUserService;
@Override
public void record(Long processId, Integer status, String description) {
Long userId = LoginUserInfoHelper.getUserId();
SysUser sysUser = sysUserService.getById(userId);
ProcessRecord processRecord = new ProcessRecord();
processRecord.setProcessId(processId);
processRecord.setStatus(status);
processRecord.setDescription(description);
processRecord.setOperateUser(sysUser.getName());
processRecord.setOperateUserId(userId);
baseMapper.insert(processRecord);
}
}
config
@Data
@Component
@ConfigurationProperties(prefix = "wechat")
public class WechatAccountConfig {
private String mpAppId;
private String mpAppSecret;
}
@Component
public class WeChatMpConfig {
@Autowired
private WechatAccountConfig wechatAccountConfig;
@Bean
public WxMpService wxMpService(){
WxMpService wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
return wxMpService;
}
@Bean
public WxMpConfigStorage wxMpConfigStorage(){
WxMpDefaultConfigImpl wxMpConfigStorage = new WxMpDefaultConfigImpl();
wxMpConfigStorage.setAppId(wechatAccountConfig.getMpAppId());
wxMpConfigStorage.setSecret(wechatAccountConfig.getMpAppSecret());
return wxMpConfigStorage;
}
}
controller
@Controller
@CrossOrigin
@RequestMapping("/admin/wechat")
public class WechatController {
@Autowired
private SysUserService sysUserService;
@Autowired
private WxMpService wxMpService;
@Value("${wechat.userInfoUrl}")
private String userInfoUrl;
@GetMapping("/authorize")
public String authorize(@RequestParam("returnUrl") String returnUrl,
HttpServletRequest request) {
//buildAuthorizationUrl三个参数
//第一个参数:授权路径,在哪个路径获取微信信息
//第二个参数:固定值,授权类型 WxConsts.OAuth2Scope.SNSAPI_USERINFO
//第三个参数:授权成功之后,跳转路径 'guiguoa' 转换成 '#'
String redirectUrl = null;
try {
redirectUrl = wxMpService.getOAuth2Service()
.buildAuthorizationUrl(userInfoUrl,
WxConsts.OAuth2Scope.SNSAPI_USERINFO,
URLEncoder.encode(returnUrl.replace("guiguoa", "#"),"utf-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return "redirect:" + redirectUrl;
}
@GetMapping("/userInfo")
public String userInfo(@RequestParam("code") String code,
@RequestParam("state") String returnUrl) throws Exception {
//获取accessToken
WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code);
//使用accessToken获取openId
String openId = accessToken.getOpenId();
System.out.println("openId: "+openId);
//获取微信用户信息
WxOAuth2UserInfo wxMpUser = wxMpService.getOAuth2Service().getUserInfo(accessToken, null);
System.out.println("微信用户信息: "+ JSON.toJSONString(wxMpUser));
//根据openid查询用户表
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getOpenId,openId);
SysUser sysUser = sysUserService.getOne(wrapper);
String token = "";
//判断openid是否存在
if(sysUser != null) {
token = JwtHelper.createToken(sysUser.getId(),sysUser.getUsername());
}
if(returnUrl.indexOf("?") == -1) {
return "redirect:" + returnUrl + "?token=" + token + "&openId=" + openId;
} else {
return "redirect:" + returnUrl + "&token=" + token + "&openId=" + openId;
}
}
@PostMapping("/bindPhone")
@ResponseBody
public Result bindPhone(@RequestBody BindPhoneVo bindPhoneVo) {
//1 根据手机号查询数据库
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getPhone,bindPhoneVo.getPhone());
SysUser sysUser = sysUserService.getOne(wrapper);
//2 如果存在,更新记录 openid
if(sysUser != null) {
sysUser.setOpenId(bindPhoneVo.getOpenId());
sysUserService.updateById(sysUser);
String token = JwtHelper.createToken(sysUser.getId(),sysUser.getUsername());
return Result.ok(token);
} else {
return Result.fail("手机号不存在,请联系管理员修改");
}
}
}
/**
* <p>
* 菜单 前端控制器
* </p>
*/
@RestController
@RequestMapping("/admin/wechat/menu")
public class MenuController {
@Autowired
private MenuService menuService;
@ApiOperation(value = "删除菜单")
@DeleteMapping("removeMenu")
public Result removeMenu() {
menuService.removeMenu();
return Result.ok();
}
@ApiOperation(value = "同步菜单")
@GetMapping("syncMenu")
public Result createMenu() {
menuService.syncMenu();
return Result.ok();
}
@ApiOperation(value = "获取全部菜单")
@GetMapping("findMenuInfo")
public Result findMenuInfo() {
List<MenuVo> menuList = menuService.findMenuInfo();
return Result.ok(menuList);
}
}
interface
/**
* <p>
* 菜单 服务类
* </p>
*/
public interface MenuService extends IService<Menu> {
//获取全部菜单
List<MenuVo> findMenuInfo();
//同步菜单
void syncMenu();
//删除菜单
void removeMenu();
}
public interface MessageService {
/**
* 推送待审批人员
* @param processId
* @param userId
* @param taskId
*/
void pushPendingMessage(Long processId, Long userId, String taskId);
}
service
/**
* <p>
* 菜单 服务实现类
* </p>
*/
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {
@Autowired
private WxMpService wxMpService;
//获取全部菜单
@Override
public List<MenuVo> findMenuInfo() {
List<MenuVo> list = new ArrayList<>();
//1 查询所有菜单list集合
List<Menu> menuList = baseMapper.selectList(null);
//2 查询所有一级菜单 parent_id=0,返回一级菜单list集合
List<Menu> oneMenuList = menuList.stream()
.filter(menu -> menu.getParentId().longValue() == 0)
.collect(Collectors.toList());
//3 一级菜单list集合遍历,得到每个一级菜单
for(Menu oneMenu : oneMenuList) {
//一级菜单Menu --- MenuVo
MenuVo oneMenuVo = new MenuVo();
BeanUtils.copyProperties(oneMenu,oneMenuVo);
//4 获取每个一级菜单里面所有二级菜单 id 和 parent_id比较
//一级菜单id 和 其他菜单parent_id
List<Menu> twoMenuList = menuList.stream()
.filter(menu -> menu.getParentId().longValue() == oneMenu.getId())
.collect(Collectors.toList());
//5 把一级菜单里面所有二级菜单获取到,封装一级菜单children集合里面
//List<Menu> -- List<MenuVo>
List<MenuVo> children = new ArrayList<>();
for(Menu twoMenu : twoMenuList) {
MenuVo twoMenuVo = new MenuVo();
BeanUtils.copyProperties(twoMenu,twoMenuVo);
children.add(twoMenuVo);
}
oneMenuVo.setChildren(children);
//把每个封装好的一级菜单放到最终list集合
list.add(oneMenuVo);
}
return list;
}
//同步菜单
@Override
public void syncMenu() {
//1 菜单数据查询出来,封装微信要求菜单格式
List<MenuVo> menuVoList = this.findMenuInfo();
//菜单
JSONArray buttonList = new JSONArray();
for(MenuVo oneMenuVo : menuVoList) {
JSONObject one = new JSONObject();
one.put("name", oneMenuVo.getName());
if(CollectionUtils.isEmpty(oneMenuVo.getChildren())) {
one.put("type", oneMenuVo.getType());
one.put("url", "http://localhost:9090/#"+oneMenuVo.getUrl());
} else {
JSONArray subButton = new JSONArray();
for(MenuVo twoMenuVo : oneMenuVo.getChildren()) {
JSONObject view = new JSONObject();
view.put("type", twoMenuVo.getType());
if(twoMenuVo.getType().equals("view")) {
view.put("name", twoMenuVo.getName());
//H5页面地址
view.put("url", "http://localhost:9090#"+twoMenuVo.getUrl());
} else {
view.put("name", twoMenuVo.getName());
view.put("key", twoMenuVo.getMeunKey());
}
subButton.add(view);
}
one.put("sub_button", subButton);
}
buttonList.add(one);
}
//菜单
JSONObject button = new JSONObject();
button.put("button", buttonList);
//2 调用工具里面的方法实现菜单推送
try {
wxMpService.getMenuService().menuCreate(button.toString());
} catch (WxErrorException e) {
throw new RuntimeException(e);
}
}
//删除菜单
@Override
public void removeMenu() {
try {
wxMpService.getMenuService().menuDelete();
} catch (WxErrorException e) {
throw new RuntimeException(e);
}
}
}
@Service
public class MessageServiceImpl implements MessageService {
@Autowired
private OaProcessService processService;
@Autowired
private SysUserService sysUserService;
@Autowired
private OaProcessTemplateService processTemplateService;
@Autowired
private WxMpService wxMpService;
@Override
public void pushPendingMessage(Long processId, Long userId, String taskId) {
//查询流程信息
Process process = processService.getById(processId);
//根据userid查询要推送人信息
SysUser sysUser = sysUserService.getById(userId);
//查询审批模板信息
ProcessTemplate processTemplate = processTemplateService.getById(process.getProcessTemplateId());
//获取提交审批人的信息
SysUser submitSysUser = sysUserService.getById(process.getUserId());
//获取要给的消息人的openid
String openId = sysUser.getOpenId();
if(StringUtils.isEmpty(openId)) {
//TODO 为了测试,添加默认值,当前自己的openid
openId = "oAFDq5ym5UPDjk1Te0WodW6_UzXc";
}
//设置消息发送信息
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
//给谁发送消息,openid值
.toUser(openId)
//创建模板信息的id值
.templateId("6LHiCVD5VqQ7k-bzdWzHbItaYZ4SeRk93xsL74y4c8E")
//点击消息,跳转的地址
.url("http://ggkt1.vipgz1.91tunnel.com/#/show/" + processId + "/" + taskId)
.build();
JSONObject jsonObject = JSON.parseObject(process.getFormValues());
JSONObject formShowData = jsonObject.getJSONObject("formShowData");
StringBuffer content = new StringBuffer();
for (Map.Entry entry : formShowData.entrySet()) {
content.append(entry.getKey()).append(":").append(entry.getValue()).append("\n ");
}
//设置模板里面参数值
templateMessage
.addData(new WxMpTemplateData("first",
submitSysUser.getName()+"提交"+processTemplate.getName()+",请注意查看","#272727"));
templateMessage.addData(new WxMpTemplateData("keyword1", process.getProcessCode(), "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword2", new DateTime(process.getCreateTime()).toString("yyyy-MM-dd HH:mm:ss"), "#272727"));
templateMessage.addData(new WxMpTemplateData("content", content.toString(), "#272727"));
//调用方法发送
try {
String msg = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
System.out.println(msg);
} catch (WxErrorException e) {
throw new RuntimeException(e);
}
}
}
jwt
//jwt工具类
public class JwtHelper {
private static long tokenExpiration = 365 * 24 * 60 * 60 * 1000;
private static String tokenSignKey = "123456";
//根据用户id和用户名称生成token字符串
public static String createToken(Long userId, String username) {
String token = Jwts.builder()
//分类
.setSubject("AUTH-USER")
//设置token有效时长
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
//设置主体部分
.claim("userId", userId)
.claim("username", username)
//签名部分
.signWith(SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
//从生成token字符串获取用户id
public static Long getUserId(String token) {
try {
if (StringUtils.isEmpty(token)) return null;
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
Integer userId = (Integer) claims.get("userId");
return userId.longValue();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//从生成token字符串获取用户名称
public static String getUsername(String token) {
try {
if (StringUtils.isEmpty(token)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return (String) claims.get("username");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
String token = JwtHelper.createToken(1L, "admin");
System.out.println(token);
Long userId = JwtHelper.getUserId(token);
String username = JwtHelper.getUsername(token);
System.out.println(userId);
System.out.println(username);
}
}
result
public class ResponseUtil {
public static void out(HttpServletResponse response, Result r) {
ObjectMapper mapper = new ObjectMapper();
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
try {
mapper.writeValue(response.getWriter(), r);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Getter
public enum ResultCodeEnum {
SUCCESS(200,"成功"),
FAIL(201, "失败"),
LOGIN_ERROR(208,"认证失败")
;
private Integer code;
private String message;
private ResultCodeEnum(Integer code,String message) {
this.code = code;
this.message = message;
}
}
@Data
public class Result<T> {
private Integer code;//状态码
private String message;//返回信息
private T data;//数据
//私有化
private Result() {}
//封装返回是数据
public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
Result<T> result = new Result<>();
//封装数据
if(body != null) {
result.setData(body);
}
//状态码
result.setCode(resultCodeEnum.getCode());
//返回信息
result.setMessage(resultCodeEnum.getMessage());
return result;
}
//成功
public static<T> Result<T> ok() {
return build(null,ResultCodeEnum.SUCCESS);
}
public static<T> Result<T> ok(T data) {
return build(data,ResultCodeEnum.SUCCESS);
}
//失败
public static<T> Result<T> fail() {
return build(null,ResultCodeEnum.FAIL);
}
public static<T> Result<T> fail(T data) {
return build(data,ResultCodeEnum.FAIL);
}
public Result<T> message(String msg){
this.setMessage(msg);
return this;
}
public Result<T> code(Integer code){
this.setCode(code);
return this;
}
}
entity
@Data
public class BaseEntity implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("create_time")
private Date createTime;
@TableField("update_time")
private Date updateTime;
@TableLogic //逻辑删除
@TableField("is_deleted")
private Integer isDeleted;
@TableField(exist = false)
private Map<String,Object> param = new HashMap<>();
}
vo
@Data
public class SysLoginLogQueryVo {
@ApiModelProperty(value = "用户账号")
private String username;
private String createTimeBegin;
private String createTimeEnd;
}
@Data
public class SysOperLogQueryVo {
private String title;
private String operName;
private String createTimeBegin;
private String createTimeEnd;
}
@Data
public class SysPostQueryVo {
//@ApiModelProperty(value = "岗位编码")
private String postCode;
//@ApiModelProperty(value = "岗位名称")
private String name;
//@ApiModelProperty(value = "状态(1正常 0停用)")
private Boolean status;
}
/**
* <p>
* 角色查询实体
* </p>
*/
public class SysRoleQueryVo implements Serializable {
private static final long serialVersionUID = 1L;
private String roleName;
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
/**
* <p>
* 用户查询实体
* </p>
*/
@Data
public class SysUserQueryVo implements Serializable {
private static final long serialVersionUID = 1L;
private String keyword;
private String createTimeBegin;
private String createTimeEnd;
private Long roleId;
private Long postId;
private Long deptId;
}
wechat vo
@Data
public class BindPhoneVo {
@ApiModelProperty(value = "手机")
private String phone;
@ApiModelProperty(value = "openId")
private String openId;
}
@Data
@ApiModel(description = "菜单")
public class MenuVo {
@ApiModelProperty(value = "id")
private Long id;
@ApiModelProperty(value = "id")
private Long parentId;
@ApiModelProperty(value = "名称")
private String name;
@ApiModelProperty(value = "类型")
private String type;
@ApiModelProperty(value = "url")
private String url;
@ApiModelProperty(value = "菜单key")
private String meunKey;
@ApiModelProperty(value = "排序")
private Integer sort;
@ApiModelProperty(value = "下级")
@TableField(exist = false)
private List<MenuVo> children;
}
Mapper
/**
* <p>
* 审批类型 Mapper 接口
* </p>
*/
@Mapper
public interface OaProcessMapper extends BaseMapper<Process> {
//审批管理列表
IPage<ProcessVo> selectPage(Page<ProcessVo> pageParam,@Param("vo") ProcessQueryVo processQueryVo);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.oumuanode.serviceoa.mapper.OaProcessMapper">
<select id="selectPage" resultType="com.oumuanode.model.vo.system.process.ProcessVo">
select
a.id,a.process_code,a.user_id,a.process_template_id,a.process_type_id,a.title,a.description,a.form_values,a.process_instance_id,a.current_auditor,a.status,a.create_time,a.update_time,
b.name as processTemplateName,
c.name as processTypeName,
d.name
from oa_process a
left join sys_user d on a.user_id =d.id
left join oa_process_template b on a.process_template_id = b.id
left join oa_process_type c on a.process_type_id = c.id
<where>
<if test="vo.keyword != null and vo.keyword != ''">
and (a.process_code like CONCAT('%',#{vo.keyword},'%') or
a.title like CONCAT('%',#{vo.keyword},'%'))
</if>
<if test="vo.userId != null and vo.userId != ''">
and a.user_id = #{vo.userId}
</if>
<if test="vo.status != null and vo.status != ''">
and a.status = #{vo.status}
</if>
<if test="vo.createTimeBegin != null and vo.createTimeBegin != ''">
and a.create_time >= #{vo.createTimeBegin}
</if>
<if test="vo.createTimeEnd != null and vo.createTimeEnd != ''">
and a.create_time <= #{vo.createTimeEnd}
</if>
</where>
</select>
</mapper>
/**
* <p>
* 菜单表 Mapper 接口
* </p>
*/
@Mapper
public interface SysMenuMapper extends BaseMapper<SysMenu> {
//多表关联查询:用户角色关系表 、 角色菜单关系表、 菜单表
List<SysMenu> findMenuListByUserId(@Param("userId") Long userId);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.oumuanode.serviceoa.mapper.SysMenuMapper">
<resultMap id="sysMenuMap"
type="com.oumuanode.model.model.system.SysMenu" autoMapping="true">
</resultMap>
<select id="findMenuListByUserId" resultMap="sysMenuMap">
select distinct
m.id,m.parent_id,m.name,m.type,m.path,m.component,m.perms,m.icon,m.sort_value,m.status,m.create_time,m.update_time,m.is_deleted
from sys_menu m
inner join sys_role_menu rm on rm.menu_id = m.id
inner join sys_user_role ur on ur.role_id = rm.role_id
where ur.user_id=#{userId}
and m.status = 1
and rm.is_deleted = 0
and ur.is_deleted = 0
and m.is_deleted = 0
</select>
</mapper>