Commit 345d86ab authored by mengmeng's avatar mengmeng

20210125 init

parents
Pipeline #201 failed with stages
# hmit-security-enterprise
\ No newline at end of file
version: '2'
services:
hmit-admin:
image: hmit/hmit-admin
ports:
- "8080:8080"
environment:
- spring.profiles.active=dev
hmit-api:
image: hmit/hmit-api
ports:
- "8081:8081"
environment:
- spring.profiles.active=dev
\ No newline at end of file
FROM java:8
EXPOSE 8080
VOLUME /tmp
ADD hmit-admin.jar /app.jar
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-jar","/app.jar"]
<?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">
<parent>
<groupId>io.hmit</groupId>
<artifactId>hmit-enterprise</artifactId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hmit-admin</artifactId>
<packaging>jar</packaging>
<properties>
<quartz.version>2.3.0</quartz.version>
<shiro.version>1.4.0</shiro.version>
<kaptcha.version>0.0.9</kaptcha.version>
<easypoi.version>4.1.0</easypoi.version>
<qiniu.version>[7.2.0, 7.2.99]</qiniu.version>
<aliyun.oss.version>2.8.3</aliyun.oss.version>
<aliyun.core.version>3.2.2</aliyun.core.version>
<aliyun.dysmsapi.version>1.1.0</aliyun.dysmsapi.version>
<qcloud.cos.version>5.4.4</qcloud.cos.version>
<qcloud.qcloudsms.version>1.0.5</qcloud.qcloudsms.version>
<fastdfs.version>1.26.2</fastdfs.version>
<freemarker.version>2.3.28</freemarker.version>
<activiti.version>5.22.0</activiti.version>
</properties>
<dependencies>
<dependency>
<groupId>io.hmit</groupId>
<artifactId>hmit-common</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>io.hmit</groupId>
<artifactId>hmit-dynamic-datasource</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
<exclusion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-java6</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>com.github.axet</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>${easypoi.version}</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>${easypoi.version}</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>${easypoi.version}</version>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>${qiniu.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun.oss.version}</version>
</dependency>
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>${qcloud.cos.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>${aliyun.core.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>${aliyun.dysmsapi.version}</version>
</dependency>
<dependency>
<groupId>com.github.qcloudsms</groupId>
<artifactId>qcloudsms</artifactId>
<version>${qcloud.qcloudsms.version}</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>${fastdfs.version}</version>
</dependency>
<!-- activiti -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>${activiti.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-explorer</artifactId>
<version>${activiti.version}</version>
<exclusions>
<exclusion>
<artifactId>vaadin</artifactId>
<groupId>com.vaadin</groupId>
</exclusion>
<exclusion>
<artifactId>dcharts-widget</artifactId>
<groupId>org.vaadin.addons</groupId>
</exclusion>
<exclusion>
<artifactId>activiti-simple-workflow</artifactId>
<groupId>org.activiti</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-modeler</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- end activiti -->
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>${docker.plugin.version}</version>
<configuration>
<imageName>hmit/${project.artifactId}</imageName>
<dockerDirectory>${project.basedir}/</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package io.hmit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* @author zsh 408538940@qq.com
*/
@SpringBootApplication(exclude = {
org.activiti.spring.boot.SecurityAutoConfiguration.class,
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class
})
public class AdminApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(AdminApplication.class);
}
}
package io.hmit.common.annotation;
import java.lang.annotation.*;
/**
* 数据过滤注解
*
* @author zsh 408538940@qq.com
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataFilter {
/**
* 表的别名
*/
String tableAlias() default "";
/**
* 用户ID
*/
String userId() default "creator";
/**
* 部门ID
*/
String deptId() default "dept_id";
}
\ No newline at end of file
package io.hmit.common.annotation;
import java.lang.annotation.*;
/**
* 操作日志注解
*
* @author zsh 408538940@qq.com
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogOperation {
String value() default "";
}
package io.hmit.common.aspect;
import cn.hutool.core.collection.CollUtil;
import io.hmit.common.annotation.DataFilter;
import io.hmit.common.constant.Constant;
import io.hmit.common.exception.ErrorCode;
import io.hmit.common.interceptor.DataScope;
import io.hmit.modules.security.user.SecurityUser;
import io.hmit.modules.security.user.UserDetail;
import io.hmit.modules.sys.enums.SuperAdminEnum;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
/**
* 数据过滤,切面处理类
*
* @author zsh 408538940@qq.com
*/
@Aspect
@Component
public class DataFilterAspect {
@Pointcut("@annotation(io.hmit.common.annotation.DataFilter)")
public void dataFilterCut() {
}
@Before("dataFilterCut()")
public void dataFilter(JoinPoint point) {
Object params = point.getArgs()[0];
if (params != null && params instanceof Map) {
UserDetail user = SecurityUser.getUser();
//如果是超级管理员,则不进行数据过滤
if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) {
return;
}
try {
//否则进行数据过滤
Map map = (Map) params;
String sqlFilter = getSqlFilter(user, point);
map.put(Constant.SQL_FILTER, new DataScope(sqlFilter));
} catch (Exception e) {
}
return;
}
throw new io.hmit.common.exception.HmitException(ErrorCode.DATA_SCOPE_PARAMS_ERROR);
}
/**
* 获取数据过滤的SQL
*/
private String getSqlFilter(UserDetail user, JoinPoint point) throws Exception {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = point.getTarget().getClass().getDeclaredMethod(signature.getName(), signature.getParameterTypes());
DataFilter dataFilter = method.getAnnotation(DataFilter.class);
//获取表的别名
String tableAlias = dataFilter.tableAlias();
if (StringUtils.isNotBlank(tableAlias)) {
tableAlias += ".";
}
StringBuilder sqlFilter = new StringBuilder();
sqlFilter.append(" (");
//部门ID列表
List<Long> deptIdList = user.getDeptIdList();
if (CollUtil.isNotEmpty(deptIdList)) {
sqlFilter.append(tableAlias).append(dataFilter.deptId());
sqlFilter.append(" in(").append(StringUtils.join(deptIdList, ",")).append(")");
}
//查询本人数据
if (CollUtil.isNotEmpty(deptIdList)) {
sqlFilter.append(" or ");
}
sqlFilter.append(tableAlias).append(dataFilter.userId()).append("=").append(user.getId());
sqlFilter.append(")");
return sqlFilter.toString();
}
}
\ No newline at end of file
package io.hmit.common.aspect;
import com.alibaba.fastjson.JSON;
import io.hmit.common.annotation.LogOperation;
import io.hmit.common.utils.HttpContextUtils;
import io.hmit.common.utils.IpUtils;
import io.hmit.modules.log.entity.SysLogOperationEntity;
import io.hmit.modules.log.enums.OperationStatusEnum;
import io.hmit.modules.log.service.SysLogOperationService;
import io.hmit.modules.security.user.SecurityUser;
import io.hmit.modules.security.user.UserDetail;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
/**
* 操作日志,切面处理类
*
* @author zsh 408538940@qq.com
*/
@Aspect
@Component
public class LogOperationAspect {
@Autowired
private SysLogOperationService sysLogOperationService;
@Pointcut("@annotation(io.hmit.common.annotation.LogOperation)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
try {
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveLog(point, time, OperationStatusEnum.SUCCESS.value());
return result;
} catch (Exception e) {
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveLog(point, time, OperationStatusEnum.FAIL.value());
throw e;
}
}
private void saveLog(ProceedingJoinPoint joinPoint, long time, Integer status) throws Exception {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(), signature.getParameterTypes());
LogOperation annotation = method.getAnnotation(LogOperation.class);
SysLogOperationEntity log = new SysLogOperationEntity();
if (annotation != null) {
//注解上的描述
log.setOperation(annotation.value());
}
//登录用户信息
UserDetail user = SecurityUser.getUser();
if (user != null) {
log.setCreatorName(user.getUsername());
}
log.setStatus(status);
log.setRequestTime((int) time);
//请求相关信息
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
log.setIp(IpUtils.getIpAddr(request));
log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
log.setRequestUri(request.getRequestURI());
log.setRequestMethod(request.getMethod());
//请求参数
Object[] args = joinPoint.getArgs();
try {
String params = JSON.toJSONString(args[0]);
log.setRequestParams(params);
} catch (Exception e) {
}
//保存到DB
sysLogOperationService.save(log);
}
}
\ No newline at end of file
package io.hmit.common.config;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
/**
* 生成验证码配置
*
* @author zsh 408538940@qq.com
*/
@Configuration
public class KaptchaConfig {
@Bean
public DefaultKaptcha producer() {
Properties properties = new Properties();
properties.put("kaptcha.border", "no");
properties.put("kaptcha.textproducer.font.color", "black");
properties.put("kaptcha.textproducer.font.names", "Arial,Courier,cmr10,宋体,楷体,微软雅黑");
properties.put("kaptcha.textproducer.char.space", "5");
Config config = new Config(properties);
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
package io.hmit.common.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import io.hmit.common.interceptor.DataFilterInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/**
* mybatis-plus配置
*
* @author zsh 408538940@qq.com
* @since 1.0.0
*/
@Configuration
public class MybatisPlusConfig {
/**
* 配置数据权限
*/
@Bean
@Order(1)
public DataFilterInterceptor dataFilterInterceptor() {
return new DataFilterInterceptor();
}
/**
* 配置分页
*/
@Bean
@Order(0)
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
\ No newline at end of file
package io.hmit.common.config;
import io.hmit.common.constant.Constant;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
/**
* Swagger配置
*
* @author zsh 408538940@qq.com
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
// http://localhost:8080/swagger-ui.html 原路径
// http://localhost:8080/doc.html bootstrap版本路径
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//加了ApiOperation注解的类,生成接口文档
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
//包下的类,生成接口文档
//.apis(RequestHandlerSelectors.basePackage("io.hmit.modules.job.controller"))
.paths(PathSelectors.any())
.build()
.directModelSubstitute(java.util.Date.class, String.class)
.securitySchemes(security());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("hmit-enterprise")
.contact(new Contact("zsh", "", "408538940@qq.com"))
.description("接口文档")
.termsOfServiceUrl("")
.version("1.0.0")
.build();
}
private List<ApiKey> security() {
return newArrayList(
new ApiKey(Constant.TOKEN_HEADER, Constant.TOKEN_HEADER, "header")
);
}
}
\ No newline at end of file
package io.hmit.common.exception;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import io.hmit.common.utils.HttpContextUtils;
import io.hmit.common.utils.IpUtils;
import io.hmit.common.utils.Result;
import io.hmit.modules.log.entity.SysLogErrorEntity;
import io.hmit.modules.log.service.SysLogErrorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* 异常处理器
*
* @author zsh 408538940@qq.com
* @since 1.0.0
*/
@RestControllerAdvice
public class HmitExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(HmitExceptionHandler.class);
@Autowired
private SysLogErrorService sysLogErrorService;
/**
* 处理自定义异常
*/
@ExceptionHandler(HmitException.class)
public Result handleHmitException(HmitException ex) {
Result result = new Result();
result.error(ex.getCode(), ex.getMsg());
return result;
}
@ExceptionHandler(DuplicateKeyException.class)
public Result handleDuplicateKeyException(DuplicateKeyException ex) {
Result result = new Result();
result.error(ErrorCode.DB_RECORD_EXISTS);
return result;
}
@ExceptionHandler(Exception.class)
public Result handleException(Exception ex) {
logger.error(ex.getMessage(), ex);
saveLog(ex);
return new Result().error();
}
/**
* 保存异常日志
*/
private void saveLog(Exception ex) {
SysLogErrorEntity log = new SysLogErrorEntity();
//请求相关信息
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
log.setIp(IpUtils.getIpAddr(request));
log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
log.setRequestUri(request.getRequestURI());
log.setRequestMethod(request.getMethod());
Map<String, String> params = HttpContextUtils.getParameterMap(request);
if (MapUtil.isNotEmpty(params)) {
log.setRequestParams(JSON.toJSONString(params));
}
//异常信息
log.setErrorInfo(ExceptionUtils.getErrorStackTrace(ex));
//保存
sysLogErrorService.save(log);
}
}
\ No newline at end of file
package io.hmit.common.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import io.hmit.modules.security.user.SecurityUser;
import io.hmit.modules.security.user.UserDetail;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 公共字段,自动填充值
*
* @author zsh 408538940@qq.com
*/
@Component
public class FieldMetaObjectHandler implements MetaObjectHandler {
private final static String CREATE_DATE = "createDate";
private final static String CREATOR = "creator";
private final static String UPDATE_DATE = "updateDate";
private final static String UPDATER = "updater";
private final static String DEPT_ID = "deptId";
@Override
public void insertFill(MetaObject metaObject) {
UserDetail user = SecurityUser.getUser();
Date date = new Date();
//创建者
setInsertFieldValByName(CREATOR, user.getId(), metaObject);
//创建时间
setInsertFieldValByName(CREATE_DATE, date, metaObject);
//创建者所属部门
setInsertFieldValByName(DEPT_ID, user.getDeptId(), metaObject);
//更新者
setInsertFieldValByName(UPDATER, user.getId(), metaObject);
//更新时间
setInsertFieldValByName(UPDATE_DATE, date, metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
//更新者
setUpdateFieldValByName(UPDATER, SecurityUser.getUserId(), metaObject);
//更新时间
setUpdateFieldValByName(UPDATE_DATE, new Date(), metaObject);
}
}
\ No newline at end of file
package io.hmit.common.interceptor;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.sql.Connection;
import java.util.Map;
import java.util.Properties;
/**
* 数据过滤
*
* @author zsh 408538940@qq.com
* @since 1.0.0
*/
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataFilterInterceptor extends AbstractSqlParserHandler implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
// SQL解析
this.sqlParser(metaObject);
// 先判断是不是SELECT操作
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
return invocation.proceed();
}
// 针对定义了rowBounds,做为mapper接口方法的参数
BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
String originalSql = boundSql.getSql();
Object paramObj = boundSql.getParameterObject();
// 判断参数里是否有DataScope对象
DataScope scope = null;
if (paramObj instanceof DataScope) {
scope = (DataScope) paramObj;
} else if (paramObj instanceof Map) {
for (Object arg : ((Map) paramObj).values()) {
if (arg instanceof DataScope) {
scope = (DataScope) arg;
break;
}
}
}
// 不用数据过滤
if (scope == null) {
return invocation.proceed();
}
// 拼接新SQL
originalSql = getSelect(originalSql, scope);
// 重写SQL
metaObject.setValue("delegate.boundSql.sql", originalSql);
return invocation.proceed();
}
private String getSelect(String originalSql, DataScope scope) {
try {
Select select = (Select) CCJSqlParserUtil.parse(originalSql);
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
Expression expression = plainSelect.getWhere();
if (expression == null) {
plainSelect.setWhere(new StringValue(scope.getSqlFilter()));
} else {
AndExpression andExpression = new AndExpression(expression, new StringValue(scope.getSqlFilter()));
plainSelect.setWhere(andExpression);
}
return select.toString().replaceAll("'", "");
} catch (JSQLParserException e) {
return originalSql;
}
}
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {
}
}
\ No newline at end of file
package io.hmit.common.interceptor;
/**
* 数据范围
*
* @author zsh 408538940@qq.com
* @since 1.0.0
*/
public class DataScope {
private String sqlFilter;
public DataScope(String sqlFilter) {
this.sqlFilter = sqlFilter;
}
public String getSqlFilter() {
return sqlFilter;
}
public void setSqlFilter(String sqlFilter) {
this.sqlFilter = sqlFilter;
}
@Override
public String toString() {
return this.sqlFilter;
}
}
\ No newline at end of file
package io.hmit.common.utils;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
* excel工具类
*
* @author zsh 408538940@qq.com
*/
public class ExcelUtils {
/**
* Excel导出
*
* @param response response
* @param fileName 文件名
* @param list 数据List
* @param pojoClass 对象Class
*/
public static void exportExcel(HttpServletResponse response, String fileName, Collection<?> list,
Class<?> pojoClass) throws IOException {
if (StringUtils.isBlank(fileName)) {
//当前日期
fileName = DateUtils.format(new Date());
}
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), pojoClass, list);
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xls");
ServletOutputStream out = response.getOutputStream();
workbook.write(out);
out.flush();
}
/**
* Excel导出,先sourceList转换成List<targetClass>,再导出
*
* @param response response
* @param fileName 文件名
* @param sourceList 原数据List
* @param targetClass 目标对象Class
*/
public static void exportExcelToTarget(HttpServletResponse response, String fileName, Collection<?> sourceList,
Class<?> targetClass) throws Exception {
List targetList = new ArrayList<>(sourceList.size());
for (Object source : sourceList) {
Object target = targetClass.newInstance();
BeanUtils.copyProperties(source, target);
targetList.add(target);
}
exportExcel(response, fileName, targetList, targetClass);
}
}
package io.hmit.common.validator.group;
/**
* 阿里云
*
* @author zsh 408538940@qq.com
*/
public interface AliyunGroup {
}
package io.hmit.common.validator.group;
/**
* FastDFS
*
* @author zsh 408538940@qq.com
*/
public interface FastDFSGroup {
}
package io.hmit.common.validator.group;
/**
* 本地上传
*
* @author zsh 408538940@qq.com
*/
public interface LocalGroup {
}
package io.hmit.common.validator.group;
/**
* 腾讯云
*
* @author zsh 408538940@qq.com
*/
public interface QcloudGroup {
}
package io.hmit.common.validator.group;
/**
* 七牛
*
* @author zsh 408538940@qq.com
*/
public interface QiniuGroup {
}
package io.hmit.modules.activiti.config;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;
/**
* 流程配置信息
*
* @author zsh 408538940@qq.com
*/
@Configuration
public class ProcessEngineConfig implements ProcessEngineConfigurationConfigurer {
@Override
public void configure(SpringProcessEngineConfiguration processEngineConfiguration) {
processEngineConfiguration.setActivityFontName("宋体");
processEngineConfiguration.setLabelFontName("宋体");
processEngineConfiguration.setAnnotationFontName("宋体");
processEngineConfiguration.setJobExecutorActivate(false);
processEngineConfiguration.setAsyncExecutorEnabled(false);
//自定义流程图样式
//processEngineConfiguration.setProcessDiagramGenerator(customProcessDiagramGenerator);
}
}
package io.hmit.modules.activiti.controller;
import io.hmit.common.annotation.LogOperation;
import io.hmit.common.constant.Constant;
import io.hmit.common.page.PageData;
import io.hmit.common.utils.Result;
import io.hmit.common.validator.ValidatorUtils;
import io.hmit.modules.activiti.dto.ModelDTO;
import io.hmit.modules.activiti.service.ActModelService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.activiti.engine.repository.Model;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* 模型管理
*
* @author zsh 408538940@qq.com
*/
@RestController
@RequestMapping("/act/model")
@Api(tags = "模型管理")
public class ActModelController {
@Autowired
private ActModelService actModelService;
@GetMapping("page")
@ApiOperation("分页")
@ApiImplicitParams({
@ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = "key", value = "key", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "name", value = "name", paramType = "query", dataType = "String")
})
@RequiresPermissions("sys:model:all")
public Result<PageData<Model>> page(@ApiIgnore @RequestParam Map<String, Object> params) {
PageData<Model> page = actModelService.page(params);
return new Result<PageData<Model>>().ok(page);
}
@PostMapping
@ApiOperation("新增模型")
@LogOperation("新增模型")
@RequiresPermissions("sys:model:all")
public Result save(@RequestBody ModelDTO dto) throws Exception {
//校验数据
ValidatorUtils.validateEntity(dto);
actModelService.save(dto.getName(), dto.getKey(), dto.getDescription());
return new Result();
}
@PostMapping("deploy/{id}")
@ApiOperation("部署")
@LogOperation("部署")
@RequiresPermissions("sys:model:all")
public Result deploy(@PathVariable("id") String id) {
actModelService.deploy(id);
return new Result();
}
@GetMapping("export/{id}")
@ApiOperation("导出")
@LogOperation("导出")
@RequiresPermissions("sys:model:all")
public void export(@PathVariable("id") String id, @ApiIgnore HttpServletResponse response) {
actModelService.export(id, response);
}
@DeleteMapping
@ApiOperation("删除")
@LogOperation("删除")
@RequiresPermissions("sys:model:all")
public Result delete(@RequestBody String[] ids) {
for (String id : ids) {
actModelService.delete(id);
}
return new Result();
}
@GetMapping("image/{deploymentId}")
@ApiOperation(value = "查看流程图", produces = "application/octet-stream")
@LogOperation("查看流程图")
@RequiresPermissions("sys:model:all")
public void viewDeployImage(@PathVariable("deploymentId") String deploymentId, @ApiIgnore HttpServletResponse response) {
actModelService.deployImage(deploymentId, response);
}
}
\ No newline at end of file
package io.hmit.modules.activiti.controller;
import io.hmit.common.annotation.LogOperation;
import io.hmit.common.constant.Constant;
import io.hmit.common.exception.ErrorCode;
import io.hmit.common.page.PageData;
import io.hmit.common.utils.ConvertUtils;
import io.hmit.common.utils.Result;
import io.hmit.modules.activiti.dto.ProcessBizRouteAndProcessInstanceDTO;
import io.hmit.modules.activiti.dto.ProcessBizRouteDTO;
import io.hmit.modules.activiti.dto.ProcessInstanceDTO;
import io.hmit.modules.activiti.service.ActHistoryService;
import io.hmit.modules.activiti.service.ActProcessService;
import io.hmit.modules.activiti.service.ProcessBizRouteService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* 流程管理
*
* @author zsh 408538940@qq.com
*/
@RestController
@RequestMapping("/act/process")
@Api(tags = "流程管理")
public class ActProcessController {
@Autowired
private ActProcessService actProcessService;
@Autowired
private ProcessBizRouteService processBizRouteService;
@Autowired
private ActHistoryService historyService;
@GetMapping("page")
@ApiOperation("流程管理-分页")
@ApiImplicitParams({
@ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = "key", value = "key", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "processName", value = "processName", paramType = "query", dataType = "String")
})
@RequiresPermissions("sys:process:all")
public Result<PageData<Map<String, Object>>> page(@ApiIgnore @RequestParam Map<String, Object> params) {
PageData<Map<String, Object>> page = actProcessService.page(params);
return new Result<PageData<Map<String, Object>>>().ok(page);
}
@GetMapping("lastestPage")
@ApiOperation("发起流程-分页")
@ApiImplicitParams({
@ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = "key", value = "key", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "processName", value = "processName", paramType = "query", dataType = "String")
})
@RequiresPermissions("sys:process:all")
public Result<PageData<Map<String, Object>>> lastestPage(@ApiIgnore @RequestParam Map<String, Object> params) {
params.put("isLatestVersion", true);
PageData<Map<String, Object>> page = actProcessService.page(params);
return new Result<PageData<Map<String, Object>>>().ok(page);
}
@PostMapping("deploy")
@ApiOperation("部署流程文件")
@LogOperation("部署流程文件")
@ApiImplicitParam(name = "processFile", value = "流程文件", paramType = "query", dataType = "file")
@RequiresPermissions("sys:process:all")
public Result deploy(@RequestParam("processFile") MultipartFile file) throws IOException {
if (file.isEmpty()) {
return new Result().error(ErrorCode.UPLOAD_FILE_EMPTY);
}
actProcessService.deploy(file);
return new Result();
}
@PutMapping("active/{id}")
@LogOperation("激活流程")
@RequiresPermissions("sys:process:all")
public Result active(@PathVariable("id") String id) {
actProcessService.active(id);
return new Result();
}
@PutMapping("suspend/{id}")
@ApiOperation("挂起流程")
@LogOperation("挂起流程")
@RequiresPermissions("sys:process:all")
public Result suspend(@PathVariable("id") String id) {
actProcessService.suspend(id);
return new Result();
}
@PostMapping("convertToModel/{id}")
@ApiOperation("将部署的流程转换为模型")
@LogOperation("将部署的流程转换为模型")
@RequiresPermissions("sys:process:all")
public Result convertToModel(@PathVariable("id") String id) throws Exception {
actProcessService.convertToModel(id);
return new Result();
}
@DeleteMapping
@ApiOperation("删除流程")
@LogOperation("删除流程")
@RequiresPermissions("sys:process:all")
public Result delete(@RequestBody String[] deploymentIds) {
for (String deploymentId : deploymentIds) {
actProcessService.deleteDeployment(deploymentId);
}
return new Result();
}
@GetMapping(value = "resource")
@ApiOperation(value = "获取资源文件", produces = "application/octet-stream")
@ApiImplicitParams({
@ApiImplicitParam(name = "deploymentId", value = "部署ID", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "resourceName", value = "资源名称", paramType = "query", dataType = "String")
})
@RequiresPermissions("sys:process:all")
public void resource(String deploymentId, String resourceName, @ApiIgnore HttpServletResponse response) throws Exception {
InputStream resourceAsStream = actProcessService.getResourceAsStream(deploymentId, resourceName);
String[] fileNames = resourceName.split("\\.");
if (fileNames.length > 1) {
if (fileNames[fileNames.length - 1].toLowerCase().equals("png")) {
response.setHeader("Content-Type", "image/png");
} else if (fileNames[fileNames.length - 1].toLowerCase().equals("xml")) {
response.setHeader("Content-Type", "text/xml");
response.setHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode(resourceName, "UTF-8"));
}
}
response.setHeader("Cache-Control", "no-store, no-cache");
IOUtils.copy(resourceAsStream, response.getOutputStream());
}
@GetMapping(value = "getProcDefBizRoute/{id}")
@ApiOperation("根据流程ID获取业务路由配置")
public Result getProcDefBizRoute(@PathVariable("id") String id) {
ProcessBizRouteDTO processBizRouteDTO = processBizRouteService.getProcDefBizRoute(id);
return new Result().ok(processBizRouteDTO);
}
@GetMapping(value = "getLatestProcDefBizRoute")
@ApiOperation("根据流程定义KEY获取最新的流程配置信息")
@ApiImplicitParam(name = "procDefKey", value = "流程定义KEY", paramType = "query", dataType = "String")
public Result getLatestProcDefBizRoute(String procDefKey) {
ProcessBizRouteDTO processBizRouteDTO = processBizRouteService.getLatestProcDefBizRoute(procDefKey);
return new Result().ok(processBizRouteDTO);
}
@GetMapping(value = "getProcDefBizRouteAndProcessInstance")
@ApiOperation("根据业务ID获取流程业务路由配置和实例信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "procDefKey", value = "流程定义KEY", required = true, paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "businessKey", value = "业务KEY", required = true, paramType = "query", dataType = "String")
})
public Result getProcDefBizRouteAndProcessInstance(String procDefKey, String businessKey) {
if (StringUtils.isEmpty(businessKey)) {
return new Result().error(ErrorCode.PARAMS_GET_ERROR);
}
if (StringUtils.isEmpty(procDefKey)) {
return new Result().error(ErrorCode.PARAMS_GET_ERROR);
}
ProcessInstanceDTO processInstanceDTO = historyService.getHistoryProcessInstanceByBusinessKey(procDefKey, businessKey);
if (null == processInstanceDTO) {
return new Result();
}
ProcessBizRouteDTO processBizRouteDTO = processBizRouteService.getProcDefBizRoute(processInstanceDTO.getProcessDefinitionId());
ProcessBizRouteAndProcessInstanceDTO dto = ConvertUtils.sourceToTarget(processBizRouteDTO, ProcessBizRouteAndProcessInstanceDTO.class);
if (null != dto) {
dto.setProcessDefinitionId(processInstanceDTO.getProcessDefinitionId());
dto.setProcessDefinitionKey(procDefKey);
dto.setProcessDefinitionName(processInstanceDTO.getProcessDefinitionName());
dto.setProcessInstanceId(processInstanceDTO.getProcessInstanceId());
}
return new Result().ok(dto);
}
@PostMapping("saveProcBizRoute")
@ApiOperation("保存业务路由配置")
public Result saveProcBizRoute(@RequestBody ProcessBizRouteDTO processBizRouteDTO) {
processBizRouteService.save(processBizRouteDTO);
return new Result();
}
@PutMapping("saveProcBizRoute")
@ApiOperation("更新业务路由配置")
public Result updateProcBizRoute(@RequestBody ProcessBizRouteDTO processBizRouteDTO) {
processBizRouteService.updateProcBizRoute(processBizRouteDTO);
return new Result();
}
}
\ No newline at end of file
package io.hmit.modules.activiti.controller;
import io.hmit.common.annotation.LogOperation;
import io.hmit.common.constant.Constant;
import io.hmit.common.page.PageData;
import io.hmit.common.utils.Result;
import io.hmit.modules.activiti.dto.ProcessInstanceDTO;
import io.hmit.modules.activiti.dto.ProcessStartDTO;
import io.hmit.modules.activiti.service.ActRunningService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import java.util.Map;
/**
* 运行中的流程
*
* @author zsh 408538940@qq.com
*/
@RestController
@RequestMapping("/act/running")
@Api(tags = "运行中的流程")
public class ActRunningController {
@Autowired
private ActRunningService actRunningService;
@GetMapping("page")
@ApiOperation("分页")
@ApiImplicitParams({
@ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = "id", value = "实例ID", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "definitionKey", value = "definitionKey", paramType = "query", dataType = "String")
})
@RequiresPermissions("sys:running:all")
public Result<PageData<Map<String, Object>>> page(@ApiIgnore @RequestParam Map<String, Object> params) {
PageData<Map<String, Object>> page = actRunningService.page(params);
return new Result<PageData<Map<String, Object>>>().ok(page);
}
@DeleteMapping("{id}")
@ApiOperation("删除")
@LogOperation("删除")
@RequiresPermissions("sys:running:all")
@ApiImplicitParam(name = "id", value = "ID", paramType = "query", dataType = "String")
public Result deleteInstance(@PathVariable("id") String id) {
actRunningService.delete(id);
return new Result();
}
@PostMapping("start")
@ApiOperation("启动流程实例,依据流程定义KEY,启动流程实例")
@LogOperation("启动流程实例,依据流程定义KEY,启动流程实例")
@ApiImplicitParam(name = "key", value = "流程定义标识key", paramType = "query", dataType = "String")
@RequiresPermissions("sys:running:all")
public Result<ProcessInstanceDTO> start(String key) {
ProcessInstanceDTO dto = actRunningService.startProcess(key);
return new Result().ok(dto);
}
@PostMapping("startOfBusinessKey")
@ApiOperation("启动流程实例,依据流程定义ID和业务唯一标示启动实例")
@LogOperation("启动流程实例,依据流程定义ID和业务唯一标示启动实例")
@RequiresPermissions("sys:running:all")
public Result<ProcessInstanceDTO> startOfBusinessKey(@RequestBody ProcessStartDTO processStartDTO) {
ProcessInstanceDTO dto = actRunningService.startOfBusinessKey(processStartDTO);
return new Result().ok(dto);
}
}
\ No newline at end of file
package io.hmit.modules.activiti.controller;
import io.hmit.common.constant.Constant;
import io.hmit.common.page.PageData;
import io.hmit.common.utils.Result;
import io.hmit.modules.activiti.dto.HistoryDetailDTO;
import io.hmit.modules.activiti.dto.ProcessActivityDTO;
import io.hmit.modules.activiti.dto.ProcessInstanceDTO;
import io.hmit.modules.activiti.service.ActHistoryService;
import io.hmit.modules.activiti.service.ActivitiService;
import io.hmit.modules.sys.dto.SysUserDTO;
import io.hmit.modules.sys.service.SysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
/**
* 流程的历史信息
*
* @author Jone
*/
@RestController
@RequestMapping("/act/his")
@Api(tags = "流程历史")
public class HistoryController {
@Autowired
private ActHistoryService historyService;
@Autowired
private ActivitiService activitiService;
@Autowired
private SysUserService sysUserService;
@GetMapping("getInstImage")
@ApiOperation(value = "获取流程活动图", produces = "application/octet-stream")
@ApiImplicitParam(name = "processInstanceId", value = "流程实例ID", paramType = "query", dataType = "String")
@RequiresPermissions("sys:his:all")
public void getProcessInstanceDiagram(String processInstanceId, @ApiIgnore HttpServletResponse response) throws Exception {
historyService.getProcessInstanceDiagram(processInstanceId, response);
}
@GetMapping("getHistoryProcessInstancePage")
@ApiOperation("历史流程实例列表")
@ApiImplicitParams({
@ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = "processInstanceId", value = "实例ID", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "businessKey", value = "业务KEY", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "processDefinitionId", value = "流程定义ID", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "finishedBeginTime", value = "流程完成开始时间", paramType = "query", dataType = "Date"),
@ApiImplicitParam(name = "finishedEndTime", value = "流程完成结束时间", paramType = "query", dataType = "Date"),
@ApiImplicitParam(name = "startBeginTime", value = "流程启动开始时间", paramType = "query", dataType = "Date"),
@ApiImplicitParam(name = "startEndTime", value = "流程启动结束时间", paramType = "query", dataType = "Date"),
@ApiImplicitParam(name = "ended", value = "是否完成(true, false)", paramType = "query", dataType = "String")
})
@RequiresPermissions("sys:his:all")
public Result<ProcessInstanceDTO> getHistoryProcessInstancePage(@ApiIgnore @RequestParam Map<String, Object> params) {
PageData<ProcessInstanceDTO> page = historyService.getHistoryProcessInstancePage(params);
return new Result().ok(page);
}
@GetMapping("getMyProcessInstancePage")
@ApiOperation("我发起的流程")
@ApiImplicitParams({
@ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = "processInstanceId", value = "实例ID", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "businessKey", value = "业务KEY", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "processDefinitionId", value = "流程定义ID", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "startBeginTime", value = "流程启动开始时间", paramType = "query", dataType = "Date"),
@ApiImplicitParam(name = "startEndTime", value = "流程启动结束时间", paramType = "query", dataType = "Date"),
@ApiImplicitParam(name = "ended", value = "是否接受(true:是,false:否)", paramType = "query", dataType = "String")
})
@RequiresPermissions("sys:his:all")
public Result<ProcessInstanceDTO> getMyProcessInstancePage(@ApiIgnore @RequestParam Map<String, Object> params) {
PageData<ProcessInstanceDTO> page = historyService.getMyProcessInstancePage(params);
return new Result().ok(page);
}
@GetMapping("getMyHandledInstancePage")
@ApiOperation("已办任务:根据登录账号查询用户已办任务")
@ApiImplicitParams({
@ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = "processInstanceId", value = "实例ID", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "businessKey", value = "业务KEY", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "processDefinitionId", value = "流程定义ID", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "startBeginTime", value = "流程启动开始时间", paramType = "query", dataType = "Date"),
@ApiImplicitParam(name = "startEndTime", value = "流程启动结束时间", paramType = "query", dataType = "Date"),
@ApiImplicitParam(name = "finishedBeginTime", value = "流程完成开始时间", paramType = "query", dataType = "Date"),
@ApiImplicitParam(name = "finishedEndTime", value = "流程完成结束时间", paramType = "query", dataType = "Date")
})
@RequiresPermissions("sys:his:all")
public Result<ProcessActivityDTO> getMyHandledInstancePage(@ApiIgnore @RequestParam Map<String, Object> params) {
PageData<ProcessActivityDTO> page = activitiService.getMyProcessInstancePage(params);
for (ProcessActivityDTO activityDTO : page.getList()) {
if (StringUtils.isNotEmpty(activityDTO.getStartUserId())) {
SysUserDTO userDTO = sysUserService.get(Long.valueOf(activityDTO.getStartUserId()));
activityDTO.setStartUserName(userDTO.getRealName());
}
if (StringUtils.isNotEmpty(activityDTO.getAssignee())) {
SysUserDTO userDTO = sysUserService.get(Long.valueOf(activityDTO.getAssignee()));
activityDTO.setAssigneeName(userDTO.getRealName());
}
}
return new Result().ok(page);
}
@GetMapping("getTaskHandleDetailInfo")
@ApiOperation("获取流程处理详情")
@ApiImplicitParams({
@ApiImplicitParam(name = "processInstanceId", value = "实例ID", paramType = "query", dataType = "String")
})
@RequiresPermissions("sys:his:all")
public Result<HistoryDetailDTO> getTaskHandleDetailInfo(String processInstanceId) {
List<HistoryDetailDTO> list = activitiService.getTaskHandleDetailInfo(processInstanceId);
return new Result().ok(list);
}
}
\ No newline at end of file
package io.hmit.modules.activiti.dao;
import io.hmit.common.dao.BaseDao;
import io.hmit.modules.activiti.entity.HistoryDetailEntity;
import io.hmit.modules.activiti.entity.ProcessActivityEntity;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.context.annotation.Primary;
import java.util.List;
import java.util.Map;
/**
* @author Jone
*/
@Mapper
@Primary
public interface ProcessActivityDao extends BaseDao<ProcessActivityEntity> {
List<ProcessActivityEntity> getMyProcessInstancePage(Map<String, Object> params);
List<HistoryDetailEntity> getTaskHandleDetailInfo(String processInstanceId);
}
package io.hmit.modules.activiti.dao;
import io.hmit.common.dao.BaseDao;
import io.hmit.modules.activiti.entity.ProcessBizRouteEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @Author:Jone
*/
@Mapper
public interface ProcessBizRouteDao extends BaseDao<ProcessBizRouteEntity> {
ProcessBizRouteEntity getProcDefBizRoute(@Param("proDefId") String id);
List<ProcessBizRouteEntity> getLatestProcDefBizRoute(@Param("procDefKey") String procDefKey);
}
package io.hmit.modules.activiti.demo.controller;
import io.hmit.common.annotation.LogOperation;
import io.hmit.common.constant.Constant;
import io.hmit.common.page.PageData;
import io.hmit.common.utils.Result;
import io.hmit.common.validator.AssertUtils;
import io.hmit.common.validator.ValidatorUtils;
import io.hmit.common.validator.group.AddGroup;
import io.hmit.common.validator.group.DefaultGroup;
import io.hmit.common.validator.group.UpdateGroup;
import io.hmit.modules.activiti.demo.dto.CorrectionDTO;
import io.hmit.modules.activiti.demo.service.CorrectionService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import java.util.HashMap;
import java.util.Map;
/**
* 转正申请
*
* @author zsh 408538940@qq.com
*/
@RestController
@RequestMapping("act/demo/correction")
@Api(tags = "转正申请")
public class CorrectionController {
@Autowired
private CorrectionService correctionService;
@GetMapping("page")
@ApiOperation("分页")
@ApiImplicitParams({
@ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.ORDER_FIELD, value = "排序字段", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = Constant.ORDER, value = "排序方式,可选值(asc、desc)", paramType = "query", dataType = "String")
})
@RequiresPermissions("activiti:correction:all")
public Result<PageData<CorrectionDTO>> page(@ApiIgnore @RequestParam Map<String, Object> params) {
PageData<CorrectionDTO> page = correctionService.page(params);
return new Result<PageData<CorrectionDTO>>().ok(page);
}
@GetMapping("{id}")
@ApiOperation("信息")
@RequiresPermissions("activiti:correction:all")
public Result<CorrectionDTO> get(@PathVariable("id") Long id) {
CorrectionDTO data = correctionService.get(id);
return new Result<CorrectionDTO>().ok(data);
}
@PostMapping
@ApiOperation("保存")
@LogOperation("保存")
@RequiresPermissions("activiti:correction:all")
public Result save(@RequestBody CorrectionDTO dto) {
//校验数据
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
correctionService.save(dto);
Map<String, Object> map = new HashMap<>();
map.put("businessKey", dto.getId().toString());
return new Result().ok(map);
}
@PutMapping
@ApiOperation("修改")
@LogOperation("修改")
@RequiresPermissions("activiti:correction:all")
public Result update(@RequestBody CorrectionDTO dto) {
//校验数据
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
correctionService.update(dto);
return new Result();
}
@DeleteMapping
@ApiOperation("删除")
@LogOperation("删除")
@RequiresPermissions("activiti:correction:all")
public Result delete(@RequestBody Long[] ids) {
//校验数据
AssertUtils.isArrayEmpty(ids, "id");
correctionService.delete(ids);
return new Result();
}
@PostMapping("updateInstanceId")
@ApiOperation("更新实例ID")
@LogOperation("更新实例ID")
@RequiresPermissions("activiti:correction:all")
@ApiImplicitParams({
@ApiImplicitParam(name = "businessKey", value = "业务KEY", paramType = "query", required = true, dataType = "String"),
@ApiImplicitParam(name = "processInstanceId", value = "实例ID", paramType = "query", required = true, dataType = "String")
})
public Result updateInstanceId(String businessKey, String processInstanceId) {
Long id = Long.valueOf(businessKey);
correctionService.updateInstanceId(processInstanceId, id);
return new Result();
}
}
\ No newline at end of file
package io.hmit.modules.activiti.demo.dao;
import io.hmit.common.dao.BaseDao;
import io.hmit.modules.activiti.demo.entity.CorrectionEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* 转正申请
*
* @author zsh 408538940@qq.com
*/
@Mapper
public interface CorrectionDao extends BaseDao<CorrectionEntity> {
void updateInstanceId(String instanceId, Long id);
}
\ No newline at end of file
package io.hmit.modules.activiti.demo.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 转正申请
*
* @author zsh 408538940@qq.com
*/
@Data
@ApiModel(value = "转正申请")
public class CorrectionDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "id")
private Long id;
@ApiModelProperty(value = "申请岗位")
private String applyPost;
@ApiModelProperty(value = "入职日期")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date entryDate;
@ApiModelProperty(value = "转正日期")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date correctionDate;
@ApiModelProperty(value = "工作内容")
private String workContent;
@ApiModelProperty(value = "工作成绩")
private String achievement;
@ApiModelProperty(value = "创建者")
private Long creator;
@ApiModelProperty(value = "创建时间")
private Date createDate;
@ApiModelProperty(value = "实例ID")
private String instanceId;
}
\ No newline at end of file
package io.hmit.modules.activiti.demo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.hmit.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 转正申请
*
* @author zsh 408538940@qq.com
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("tb_correction")
public class CorrectionEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 申请岗位
*/
private String applyPost;
/**
* 入职日期
*/
private Date entryDate;
/**
* 转正日期
*/
private Date correctionDate;
/**
* 工作内容
*/
private String workContent;
/**
* 工作成绩
*/
private String achievement;
private String instanceId;
}
\ No newline at end of file
package io.hmit.modules.activiti.demo.service;
import io.hmit.common.service.CrudService;
import io.hmit.modules.activiti.demo.dto.CorrectionDTO;
import io.hmit.modules.activiti.demo.entity.CorrectionEntity;
/**
* 转正申请
*
* @author zsh 408538940@qq.com
*/
public interface CorrectionService extends CrudService<CorrectionEntity, CorrectionDTO> {
void updateInstanceId(String instanceId, Long id);
}
\ No newline at end of file
package io.hmit.modules.activiti.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.hmit.common.service.impl.CrudServiceImpl;
import io.hmit.modules.activiti.demo.dao.CorrectionDao;
import io.hmit.modules.activiti.demo.dto.CorrectionDTO;
import io.hmit.modules.activiti.demo.entity.CorrectionEntity;
import io.hmit.modules.activiti.demo.service.CorrectionService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* 转正申请
*
* @author zsh 408538940@qq.com
*/
@Service
public class CorrectionServiceImpl extends CrudServiceImpl<CorrectionDao, CorrectionEntity, CorrectionDTO> implements CorrectionService {
@Override
public QueryWrapper<CorrectionEntity> getWrapper(Map<String, Object> params) {
String id = (String) params.get("id");
QueryWrapper<CorrectionEntity> wrapper = new QueryWrapper<>();
wrapper.eq(StringUtils.isNotBlank(id), "id", id);
return wrapper;
}
@Override
public void updateInstanceId(String instanceId, Long id) {
baseDao.updateInstanceId(instanceId, id);
}
}
\ No newline at end of file
package io.hmit.modules.activiti.dto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* 历史细节
*
* @author Jone
*/
@Data
@Api(tags = "历史细节")
public class HistoryDetailDTO {
@ApiModelProperty(value = "ID")
private String id;
@ApiModelProperty(value = "环节名称")
private String activityName;
@ApiModelProperty(value = "环节类型")
private String activityType;
@ApiModelProperty(value = "流程定义ID")
private String processDefinitionId;
@ApiModelProperty(value = "实例ID")
private String processInstanceId;
@ApiModelProperty(value = "任务ID")
private String taskId;
@ApiModelProperty(value = "执行ID")
private String executionId;
@ApiModelProperty(value = "受理人")
private String assignee;
@ApiModelProperty(value = "开始时间")
private Date startTime;
@ApiModelProperty(value = "结束时间")
private Date endTime;
@ApiModelProperty(value = "时长(秒)")
private Long durationInSeconds;
@ApiModelProperty(value = "审批意见")
private String comment;
}
package io.hmit.modules.activiti.dto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* 模型
*
* @author zsh 408538940@qq.com
*/
@Data
@Api(tags = "模型")
public class ModelDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "模型名称")
@NotBlank(message = "{model.name.require}")
private String name;
@ApiModelProperty(value = "模型标识")
@NotBlank(message = "{model.key.require}")
private String key;
@ApiModelProperty(value = "模型描述")
private String description;
}
\ No newline at end of file
package io.hmit.modules.activiti.dto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* @author zsh 408538940@qq.com
*/
@Data
@Api(tags = "流程活动")
public class ProcessActivityDTO {
@ApiModelProperty(value = "流程定义ID")
private String processDefinitionId;
@ApiModelProperty(value = "流程定义名称")
private String processDefinitionName;
@ApiModelProperty(value = "流程定义版本")
private Integer processDefinitionVersion;
@ApiModelProperty(value = "实例ID")
private String processInstanceId;
@ApiModelProperty(value = "业务KEY")
private String businessKey;
@ApiModelProperty(value = "发起时间")
private Date startTime;
@ApiModelProperty(value = "结束时间")
private Date endTime;
@ApiModelProperty(value = "发起人")
private String startUserId;
@ApiModelProperty(value = "发起人姓名")
private String startUserName;
@ApiModelProperty(value = "受理人")
private String assignee;
@ApiModelProperty(value = "受理人姓名")
private String assigneeName;
}
package io.hmit.modules.activiti.dto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author zsh 408538940@qq.com
*/
@Data
@Api(tags = "流程业务配置和实例信息")
public class ProcessBizRouteAndProcessInstanceDTO extends ProcessBizRouteDTO {
@ApiModelProperty(value = "实例ID")
private String processInstanceId;
@ApiModelProperty(value = "流程定义ID")
private String processDefinitionId;
@ApiModelProperty(value = "流程定义名称")
private String processDefinitionName;
@ApiModelProperty(value = "流程定义KEY")
private String processDefinitionKey;
}
package io.hmit.modules.activiti.dto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author zsh 408538940@qq.com
*/
@Data
@Api(tags = "流程业务配置")
public class ProcessBizRouteDTO {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "id")
private Long id;
@ApiModelProperty(value = "流程定义ID")
@NotBlank(message = "{processBizRoute.procDefId.require}")
private String procDefId;
@ApiModelProperty(value = "业务路由")
@NotBlank(message = "{processBizRoute.bizRoute.require}")
private String bizRoute;
@ApiModelProperty(value = "流程定义KEY")
@NotBlank(message = "{processBizRoute.procDefKey.require}")
private String procDefKey;
@ApiModelProperty(value = "版本号")
@NotNull(message = "{processBizRoute.version.require}")
private Integer version;
}
package io.hmit.modules.activiti.dto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* @author zsh 408538940@qq.com
*/
@Data
@Api(tags = "实例")
public class ProcessInstanceDTO {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "实例ID")
private String processInstanceId;
@ApiModelProperty(value = "流程定义ID")
private String processDefinitionId;
@ApiModelProperty(value = "流程定义名称")
private String processDefinitionName;
@ApiModelProperty(value = "流程定义KEY")
private String processDefinitionKey;
@ApiModelProperty(value = "流程定义版本")
private Integer processDefinitionVersion;
@ApiModelProperty(value = "部署ID")
private String deploymentId;
@ApiModelProperty(value = "业务唯一KEY")
private String businessKey;
@ApiModelProperty(value = "实例名称")
private String name;
@ApiModelProperty(value = "描述")
private String description;
@ApiModelProperty(value = "是否结束")
private boolean isEnded;
@ApiModelProperty(value = "是否挂起")
private boolean isSuspended;
@ApiModelProperty(value = "结束时间")
private Date endTime;
@ApiModelProperty(value = "开始时间")
private Date startTime;
@ApiModelProperty(value = "发起人ID")
private String createUserId;
@ApiModelProperty(value = "当前任务")
private List<TaskDTO> currentTaskList;
}
package io.hmit.modules.activiti.dto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.Map;
/**
* @author zsh 408538940@qq.com
*/
@Data
@Api(tags = "流程启动参数")
public class ProcessStartDTO {
@ApiModelProperty(value = "流程KEY")
@NotBlank(message = "{ProcessStart.processDefinitionKey.require}")
private String processDefinitionKey;
@ApiModelProperty(value = "业务KEY")
@NotBlank(message = "{ProcessStart.businessKey.require}")
private String businessKey;
@ApiModelProperty(value = "流程参数")
private Map<String, Object> variables;
}
package io.hmit.modules.activiti.dto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author zsh 408538940@qq.com
*/
@Data
@Api(tags = "任务")
public class TaskDTO {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "任务ID")
private String taskId;
@ApiModelProperty(value = "任务名称")
private String taskName;
@ApiModelProperty(value = "用户ID")
private String userId;
@ApiModelProperty(value = "任务参数")
private Map<String, Object> params;
@ApiModelProperty(value = "流程定义ID")
private String processDefinitionId;
@ApiModelProperty(value = "实例ID")
private String processInstanceId;
@ApiModelProperty(value = "角色")
private String roleIds;
@ApiModelProperty(value = "受理人")
private String assignee;
@ApiModelProperty(value = "受理人姓名")
private String assigneeName;
@ApiModelProperty(value = "任务所有人")
private String owner;
@ApiModelProperty(value = "审核意见")
private String comment;
@ApiModelProperty(value = "活动节点ID")
private String activityId;
@ApiModelProperty(value = "角色组")
private List<String> lstGroupId;
@ApiModelProperty(value = "候选人")
private List<String> lstUserIds;
@ApiModelProperty(value = "处理时间")
private Date dueDate;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "业务ID")
private String businessKey;
@ApiModelProperty(value = "流程定义名称")
private String processDefinitionName;
@ApiModelProperty(value = "流程定义KEY")
private String processDefinitionKey;
@ApiModelProperty(value = "流程发起时间")
private Date startTime;
}
package io.hmit.modules.activiti.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* @author Jone
*/
@Data
@EqualsAndHashCode(callSuper=false)
public class HistoryDetailEntity {
private static final long serialVersionUID = 1L;
private String id;
private String activityName;
private String activityType;
private String processDefinitionId;
private String processInstanceId;
private String taskId;
private String executionId;
private String assignee;
private Date startTime;
private Date endTime;
private Long durationInSeconds;
private String comment;
}
package io.hmit.modules.activiti.entity;
import io.hmit.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* @author Jone
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ProcessActivityEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
private String processDefinitionId;
private String processDefinitionName;
private Integer processDefinitionVersion;
private String processInstanceId;
private String businessKey;
private Date startTime;
private Date endTime;
private String startUserId;
private String assignee;
}
package io.hmit.modules.activiti.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* @Author:Jone
*/
@Data
@TableName("tb_process_biz_route")
public class ProcessBizRouteEntity {
private static final long serialVersionUID = 1L;
private Long id;
private String procDefId;
private String bizRoute;
private String procDefKey;
private Integer version;
}
package io.hmit.modules.activiti.org.activiti.rest.editor.main;
import org.activiti.engine.ActivitiException;
import org.apache.commons.io.IOUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.io.InputStream;
/**
* Stencilset
*
* @author zsh 408538940@qq.com
*/
@RestController
@RequestMapping("service")
public class StencilsetRestResource {
@RequestMapping(value = "/editor/stencilset", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
public @ResponseBody
String getStencilset() {
InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("stencilset.json");
try {
return IOUtils.toString(stencilsetStream, "utf-8");
} catch (Exception e) {
throw new ActivitiException("Error while loading stencil set", e);
}
}
}
package io.hmit.modules.activiti.org.activiti.rest.editor.model;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* Model Editor
*
* @author zsh 408538940@qq.com
*/
@RestController
@RequestMapping("service")
public class ModelEditorJsonRestResource implements ModelDataJsonConstants {
protected static final Logger LOGGER = LoggerFactory.getLogger(ModelEditorJsonRestResource.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
@RequestMapping(value = "/model/{modelId}/json", method = RequestMethod.GET, produces = "application/json")
public ObjectNode getEditorJson(@PathVariable String modelId) {
ObjectNode modelNode = null;
Model model = repositoryService.getModel(modelId);
if (model != null) {
try {
if (StringUtils.isNotEmpty(model.getMetaInfo())) {
modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
} else {
modelNode = objectMapper.createObjectNode();
modelNode.put(MODEL_NAME, model.getName());
}
modelNode.put(MODEL_ID, model.getId());
ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(
new String(repositoryService.getModelEditorSource(model.getId()), "utf-8"));
modelNode.set("model", editorJsonNode);
} catch (Exception e) {
LOGGER.error("Error creating model JSON", e);
throw new ActivitiException("Error creating model JSON", e);
}
}
return modelNode;
}
}
package io.hmit.modules.activiti.org.activiti.rest.editor.model;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.hmit.common.xss.XssHttpServletRequestWrapper;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
/**
* Model Rest
*
* @author zsh 408538940@qq.com
*/
@RestController
@RequestMapping("service")
public class ModelSaveRestResource implements ModelDataJsonConstants {
protected static final Logger LOGGER = LoggerFactory.getLogger(ModelSaveRestResource.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
@RequestMapping(value = "/model/{modelId}/save", method = RequestMethod.PUT)
@ResponseStatus(value = HttpStatus.OK)
public void saveModel(@PathVariable String modelId, HttpServletRequest request) {
try {
HttpServletRequest orgRequest = XssHttpServletRequestWrapper.getOrgRequest(request);
String name = orgRequest.getParameter("name");
String description = orgRequest.getParameter("description");
String jsonXml = orgRequest.getParameter("json_xml");
String svgXml = orgRequest.getParameter("svg_xml");
Model model = repositoryService.getModel(modelId);
ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
modelJson.put(MODEL_NAME, name);
modelJson.put(MODEL_DESCRIPTION, description);
model.setMetaInfo(modelJson.toString());
model.setName(name);
repositoryService.saveModel(model);
repositoryService.addModelEditorSource(model.getId(), jsonXml.getBytes("utf-8"));
InputStream svgStream = new ByteArrayInputStream(svgXml.getBytes("utf-8"));
TranscoderInput input = new TranscoderInput(svgStream);
PNGTranscoder transcoder = new PNGTranscoder();
// Setup output
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
TranscoderOutput output = new TranscoderOutput(outStream);
// Do the transformation
transcoder.transcode(input, output);
final byte[] result = outStream.toByteArray();
repositoryService.addModelEditorSourceExtra(model.getId(), result);
outStream.close();
} catch (Exception e) {
LOGGER.error("Error saving model", e);
throw new ActivitiException("Error saving model", e);
}
}
}
package io.hmit.modules.activiti.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.hmit.common.constant.Constant;
import io.hmit.common.exception.ErrorCode;
import io.hmit.common.exception.HmitException;
import io.hmit.common.page.PageData;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
/**
* 模型管理
*
* @author zsh 408538940@qq.com
*/
@Service
public class ActModelService {
@Autowired
protected RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
public PageData<Model> page(Map<String, Object> params) {
String key = (String) params.get("key");
String name = (String) params.get("name");
//分页参数
int curPage = 1;
int limit = 10;
if (params.get(Constant.PAGE) != null) {
curPage = Integer.parseInt((String) params.get(Constant.PAGE));
}
if (params.get(Constant.LIMIT) != null) {
limit = Integer.parseInt((String) params.get(Constant.LIMIT));
}
ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion().orderByLastUpdateTime().desc();
if (StringUtils.isNotEmpty(key)) {
modelQuery.modelKey(key);
}
if (StringUtils.isNotEmpty(name)) {
modelQuery.modelName(name);
}
List<Model> list = modelQuery.listPage((curPage - 1) * limit, limit);
return new PageData<>(list, (int) modelQuery.count());
}
public void save(String name, String key, String description) throws UnsupportedEncodingException {
//新建一个空模型
Model model = repositoryService.newModel();
//metaInfo信息
ObjectNode metaInfo = objectMapper.createObjectNode();
metaInfo.put(ModelDataJsonConstants.MODEL_NAME, name);
metaInfo.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
metaInfo.put(ModelDataJsonConstants.MODEL_REVISION, model.getVersion());
model.setKey(key);
model.setName(name);
model.setMetaInfo(metaInfo.toString());
repositoryService.saveModel(model);
ObjectNode editorNode = objectMapper.createObjectNode();
editorNode.put("id", "canvas");
editorNode.put("resourceId", "canvas");
ObjectNode stencilset = objectMapper.createObjectNode();
stencilset.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
editorNode.set("stencilset", stencilset);
repositoryService.addModelEditorSource(model.getId(), editorNode.toString().getBytes("utf-8"));
}
public void deploy(String id) {
try {
Model model = repositoryService.getModel(id);
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(model.getId()));
BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
if (bpmnModel.getProcesses().isEmpty()) {
throw new HmitException(ErrorCode.ACT_DEPLOY_ERROR);
}
String processName = model.getName();
if (!StringUtils.endsWith(processName, ".bpmn20.xml")) {
processName += ".bpmn20.xml";
}
ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
Deployment deployment = repositoryService.createDeployment().name(model.getName()).addInputStream(processName, in).deploy();
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
if (list.size() == 0) {
throw new HmitException(ErrorCode.ACT_DEPLOY_ERROR);
}
} catch (Exception e) {
throw new HmitException(ErrorCode.ACT_MODEL_IMG_ERROR, e);
}
}
/**
* 导出模型
*/
public void export(String id, HttpServletResponse response) {
try {
Model model = repositoryService.getModel(id);
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(model.getId()));
BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
IOUtils.copy(in, response.getOutputStream());
String filename = bpmnModel.getMainProcess().getId() + ".bpmn20.xml";
response.setHeader("Content-Disposition", "attachment; filename=" + filename);
response.flushBuffer();
} catch (Exception e) {
throw new HmitException(ErrorCode.ACT_MODEL_EXPORT_ERROR, id);
}
}
/**
* 删除模型
*
* @param id 模型ID
*/
public void delete(String id) {
repositoryService.deleteModel(id);
}
public void deployImage(String deploymentId, HttpServletResponse response) {
List<String> names = repositoryService.getDeploymentResourceNames(deploymentId);
String imageName = null;
for (String name : names) {
if (name.indexOf(".png") >= 0) {
imageName = name;
break;
}
}
InputStream in = null;
InputStream in1 = null;
try {
if (StringUtils.isNotEmpty(imageName)) {
in = repositoryService.getResourceAsStream(deploymentId, imageName);
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(imageName, "UTF-8"));
response.setHeader("Content-Type", "image/png");
response.setHeader("Cache-Control", "no-store, no-cache");
BufferedImage bufferedImage = ImageIO.read(in);
ImageIO.write(bufferedImage, "png", response.getOutputStream());
} else {
response.getWriter().println("No image Info!");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != in) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package io.hmit.modules.activiti.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.hmit.common.constant.Constant;
import io.hmit.common.exception.ErrorCode;
import io.hmit.common.exception.HmitException;
import io.hmit.common.page.PageData;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;
/**
* 流程管理
*
* @author zsh 408538940@qq.com
*/
@Service
public class ActProcessService {
@Autowired
protected RepositoryService repositoryService;
@Autowired
protected HistoryService historyService;
@Autowired
protected ProcessEngineConfiguration processEngineConfiguration;
/**
* 流程列表
*/
public PageData<Map<String, Object>> page(Map<String, Object> params) {
String key = (String) params.get("key");
String processName = (String) params.get("processName");
boolean isLatestVersion = params.get("isLatestVersion") == null ? false : (boolean) params.get("isLatestVersion");
//分页参数
int curPage = 1;
int limit = 10;
if (params.get(Constant.PAGE) != null) {
curPage = Integer.parseInt((String) params.get(Constant.PAGE));
}
if (params.get(Constant.LIMIT) != null) {
limit = Integer.parseInt((String) params.get(Constant.LIMIT));
}
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
.orderByProcessDefinitionId().desc().orderByProcessDefinitionKey().desc();
if (isLatestVersion) {
processDefinitionQuery.latestVersion();
}
if (StringUtils.isNotEmpty(key)) {
processDefinitionQuery.processDefinitionKeyLike(key);
}
if (StringUtils.isNotEmpty(processName)) {
processDefinitionQuery.processDefinitionNameLike(processName);
}
List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage((curPage - 1) * limit, limit);
List<Map<String, Object>> objectList = new ArrayList<>();
for (ProcessDefinition processDefinition : processDefinitionList) {
objectList.add(processDefinitionConvert(processDefinition));
}
return new PageData<>(objectList, (int) processDefinitionQuery.count());
}
/**
* 流程定义信息
*/
private Map<String, Object> processDefinitionConvert(ProcessDefinition processDefinition) {
String deploymentId = processDefinition.getDeploymentId();
Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
Map<String, Object> map = new HashMap<>(9);
map.put("suspended", processDefinition.isSuspended());
map.put("id", processDefinition.getId());
map.put("deploymentId", processDefinition.getDeploymentId());
map.put("name", processDefinition.getName());
map.put("key", processDefinition.getKey());
map.put("version", processDefinition.getVersion());
map.put("resourceName", processDefinition.getResourceName());
map.put("diagramResourceName", processDefinition.getDiagramResourceName());
map.put("deploymentTime", deployment.getDeploymentTime());
return map;
}
/**
* 部署
*
* @param file 文件
*/
public void deploy(MultipartFile file) throws IOException {
String fileName = file.getOriginalFilename();
String extension = FilenameUtils.getExtension(fileName);
if ("zip".equalsIgnoreCase(extension) || "bar".equalsIgnoreCase(extension)) {
ZipInputStream zip = new ZipInputStream(file.getInputStream());
repositoryService.createDeployment().addZipInputStream(zip).deploy();
} else if (fileName.indexOf("bpmn20.xml") != -1) {
repositoryService.createDeployment().addInputStream(fileName, file.getInputStream()).deploy();
} else if ("bpmn".equalsIgnoreCase(extension)) {
repositoryService.createDeployment().addInputStream(fileName, file.getInputStream()).deploy();
} else {
throw new HmitException(ErrorCode.ACT_DEPLOY_FORMAT_ERROR);
}
}
/**
* 激活流程
*
* @param id 流程ID
*/
public void active(String id) {
repositoryService.activateProcessDefinitionById(id, true, null);
}
/**
* 挂起流程
*
* @param id 流程ID
*/
public void suspend(String id) {
repositoryService.suspendProcessDefinitionById(id, true, null);
}
/**
* 将部署的流程转换为模型
*
* @param id 流程ID
*/
public Model convertToModel(String id) throws UnsupportedEncodingException, XMLStreamException {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
processDefinition.getResourceName());
XMLInputFactory xif = XMLInputFactory.newInstance();
InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
XMLStreamReader xtr = xif.createXMLStreamReader(in);
BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
BpmnJsonConverter converter = new BpmnJsonConverter();
ObjectNode modelNode = converter.convertToJson(bpmnModel);
Model modelData = repositoryService.newModel();
modelData.setKey(processDefinition.getKey());
modelData.setName(processDefinition.getResourceName());
modelData.setCategory(processDefinition.getCategory());
modelData.setDeploymentId(processDefinition.getDeploymentId());
modelData.setVersion(Integer.parseInt(String.valueOf(repositoryService.createModelQuery().modelKey(modelData.getKey()).count() + 1)));
ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, modelData.getVersion());
modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
modelData.setMetaInfo(modelObjectNode.toString());
repositoryService.saveModel(modelData);
repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
return modelData;
}
/**
* 删除部署
*
* @param deploymentId 部署ID
*/
public void deleteDeployment(String deploymentId) {
repositoryService.deleteDeployment(deploymentId, true);
}
/**
* 获取资源文件
*
* @param deploymentId 部署ID
* @param resourceName 资源名称
*/
public InputStream getResourceAsStream(String deploymentId, String resourceName) {
InputStream resourceAsStream = repositoryService.getResourceAsStream(deploymentId, resourceName);
return resourceAsStream;
}
}
package io.hmit.modules.activiti.service;
import io.hmit.common.constant.Constant;
import io.hmit.common.exception.ErrorCode;
import io.hmit.common.exception.HmitException;
import io.hmit.common.page.PageData;
import io.hmit.modules.activiti.dto.ProcessInstanceDTO;
import io.hmit.modules.activiti.dto.ProcessStartDTO;
import io.hmit.modules.security.user.SecurityUser;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 运行中的流程
*
* @author zsh 408538940@qq.com
*/
@Service
public class ActRunningService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private IdentityService identityService;
@Autowired
protected RepositoryService repositoryService;
/**
* 流程定义列表
*/
public PageData<Map<String, Object>> page(Map<String, Object> params) {
String id = (String) params.get("id");
String definitionKey = (String) params.get("definitionKey");
//分页参数
int curPage = 1;
int limit = 10;
if (params.get(Constant.PAGE) != null) {
curPage = Integer.parseInt((String) params.get(Constant.PAGE));
}
if (params.get(Constant.LIMIT) != null) {
limit = Integer.parseInt((String) params.get(Constant.LIMIT));
}
ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
if (StringUtils.isNotBlank(id)) {
processInstanceQuery.processInstanceId(id);
}
if (StringUtils.isNotBlank(definitionKey)) {
processInstanceQuery.processDefinitionKey(definitionKey);
}
List<ProcessInstance> processInstanceList = processInstanceQuery.listPage((curPage - 1) * limit, limit);
List<Map<String, Object>> objectList = new ArrayList<>();
for (ProcessInstance processInstance : processInstanceList) {
objectList.add(processInstanceConvert(processInstance));
}
return new PageData<>(objectList, (int) processInstanceQuery.count());
}
/**
* 流程实例信息
*/
private Map<String, Object> processInstanceConvert(ProcessInstance processInstance) {
Map<String, Object> map = new HashMap<>(9);
map.put("id", processInstance.getId());
map.put("processInstanceId", processInstance.getProcessInstanceId());
map.put("processDefinitionId", processInstance.getProcessDefinitionId());
map.put("processDefinitionName", processInstance.getProcessDefinitionName());
map.put("processDefinitionKey", processInstance.getProcessDefinitionKey());
map.put("businessKey", processInstance.getBusinessKey());
map.put("activityId", processInstance.getActivityId());
ProcessDefinitionEntity definition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processInstance.getProcessDefinitionId());
ActivityImpl activity = definition.findActivity(processInstance.getActivityId());
map.put("activityName", activity.getProperty("name"));
map.put("suspended", processInstance.isSuspended());
return map;
}
/**
* 删除实例
*
* @param id 实例ID
*/
public void delete(String id) {
runtimeService.deleteProcessInstance(id, null);
}
/**
* 启动流程实例
*
* @param key 流程定义标识key
*/
public ProcessInstanceDTO startProcess(String key) {
String userId = SecurityUser.getUserId().toString();
identityService.setAuthenticatedUserId(userId);
ProcessDefinitionEntity definition = (ProcessDefinitionEntity) repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).latestVersion().singleResult();
if (definition.isSuspended()) {
throw new HmitException(ErrorCode.PROCESS_START_ERROR);
}
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key);
ProcessInstanceDTO dto = new ProcessInstanceDTO();
this.convertInstance(processInstance, dto);
return dto;
}
private void convertInstance(ProcessInstance processInstance, ProcessInstanceDTO dto) {
dto.setBusinessKey(processInstance.getBusinessKey());
dto.setDeploymentId(processInstance.getDeploymentId());
dto.setDescription(processInstance.getDescription());
dto.setName(processInstance.getName());
dto.setEnded(processInstance.isEnded());
dto.setSuspended(processInstance.isSuspended());
dto.setProcessDefinitionId(processInstance.getProcessDefinitionId());
dto.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
dto.setProcessDefinitionName(processInstance.getProcessDefinitionName());
dto.setProcessDefinitionVersion(processInstance.getProcessDefinitionVersion());
dto.setProcessInstanceId(processInstance.getProcessInstanceId());
}
/**
* 根据流程Key,启动实例
*
* @param processStartDTO
* @return
*/
public ProcessInstanceDTO startOfBusinessKey(ProcessStartDTO processStartDTO) {
String userId = SecurityUser.getUserId().toString();
identityService.setAuthenticatedUserId(userId);
ProcessDefinitionEntity definition = (ProcessDefinitionEntity) repositoryService.createProcessDefinitionQuery().processDefinitionKey(processStartDTO.getProcessDefinitionKey()).latestVersion().singleResult();
if (definition.isSuspended()) {
throw new HmitException(ErrorCode.PROCESS_START_ERROR);
}
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processStartDTO.getProcessDefinitionKey(), processStartDTO.getBusinessKey(), processStartDTO.getVariables());
ProcessInstanceDTO dto = new ProcessInstanceDTO();
this.convertInstance(processInstance, dto);
return dto;
}
}
package io.hmit.modules.activiti.service;
import io.hmit.common.page.PageData;
import io.hmit.common.service.BaseService;
import io.hmit.modules.activiti.dto.HistoryDetailDTO;
import io.hmit.modules.activiti.dto.ProcessActivityDTO;
import io.hmit.modules.activiti.entity.ProcessActivityEntity;
import java.util.List;
import java.util.Map;
/**
* 流程自定义查询
*
* @author Jone
*/
public interface ActivitiService extends BaseService<ProcessActivityEntity> {
PageData<ProcessActivityDTO> getMyProcessInstancePage(Map<String, Object> params);
List<HistoryDetailDTO> getTaskHandleDetailInfo(String processInstanceId);
}
package io.hmit.modules.activiti.service;
import io.hmit.common.service.BaseService;
import io.hmit.modules.activiti.dto.ProcessBizRouteDTO;
import io.hmit.modules.activiti.entity.ProcessBizRouteEntity;
/**
* @Author:Jone
*/
public interface ProcessBizRouteService extends BaseService<ProcessBizRouteEntity> {
ProcessBizRouteDTO getProcDefBizRoute(String id);
void save(ProcessBizRouteDTO processBizRouteDTO);
void updateProcBizRoute(ProcessBizRouteDTO processBizRouteDTO);
ProcessBizRouteDTO getLatestProcDefBizRoute(String procDefKey);
}
package io.hmit.modules.activiti.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.hmit.common.page.PageData;
import io.hmit.common.service.impl.BaseServiceImpl;
import io.hmit.common.utils.ConvertUtils;
import io.hmit.modules.activiti.dao.ProcessActivityDao;
import io.hmit.modules.activiti.dto.HistoryDetailDTO;
import io.hmit.modules.activiti.dto.ProcessActivityDTO;
import io.hmit.modules.activiti.entity.HistoryDetailEntity;
import io.hmit.modules.activiti.entity.ProcessActivityEntity;
import io.hmit.modules.activiti.service.ActivitiService;
import io.hmit.modules.security.user.SecurityUser;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
* 工作流
*
* @author Jone
*/
@Service
public class ActivitiServiceImpl extends BaseServiceImpl<ProcessActivityDao, ProcessActivityEntity> implements ActivitiService {
@Override
public PageData<ProcessActivityDTO> getMyProcessInstancePage(Map<String, Object> params) {
params.put("userId", SecurityUser.getUserId().toString());
IPage<ProcessActivityEntity> page = getPage(params, null, false);
List<ProcessActivityEntity> list = baseDao.getMyProcessInstancePage(params);
return getPageData(list, page.getTotal(), ProcessActivityDTO.class);
}
@Override
public List<HistoryDetailDTO> getTaskHandleDetailInfo(String processInstanceId) {
List<HistoryDetailEntity> listEntity = baseDao.getTaskHandleDetailInfo(processInstanceId);
for (HistoryDetailEntity entity : listEntity) {
if (entity.getEndTime() != null && entity.getStartTime() != null) {
long diff = entity.getEndTime().getTime() - entity.getStartTime().getTime();
entity.setDurationInSeconds(diff / 1000);
}
}
return ConvertUtils.sourceToTarget(listEntity, HistoryDetailDTO.class);
}
}
package io.hmit.modules.activiti.service.impl;
import io.hmit.common.service.impl.BaseServiceImpl;
import io.hmit.common.utils.ConvertUtils;
import io.hmit.modules.activiti.dao.ProcessBizRouteDao;
import io.hmit.modules.activiti.dto.ProcessBizRouteDTO;
import io.hmit.modules.activiti.entity.ProcessBizRouteEntity;
import io.hmit.modules.activiti.service.ProcessBizRouteService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author:Jone
*/
@Service
public class ProcessBizRouteServiceImpl extends BaseServiceImpl<ProcessBizRouteDao, ProcessBizRouteEntity> implements ProcessBizRouteService {
@Override
public ProcessBizRouteDTO getProcDefBizRoute(String id) {
ProcessBizRouteEntity entity = baseDao.getProcDefBizRoute(id);
ProcessBizRouteDTO dto = ConvertUtils.sourceToTarget(entity, ProcessBizRouteDTO.class);
return dto;
}
@Override
public void save(ProcessBizRouteDTO processBizRouteDTO) {
ProcessBizRouteEntity entity = ConvertUtils.sourceToTarget(processBizRouteDTO, ProcessBizRouteEntity.class);
this.insert(entity);
}
@Override
public void updateProcBizRoute(ProcessBizRouteDTO processBizRouteDTO) {
ProcessBizRouteEntity entity = ConvertUtils.sourceToTarget(processBizRouteDTO, ProcessBizRouteEntity.class);
this.updateById(entity);
}
@Override
public ProcessBizRouteDTO getLatestProcDefBizRoute(String procDefKey) {
List<ProcessBizRouteEntity> list = baseDao.getLatestProcDefBizRoute(procDefKey);
if (list.isEmpty()) {
return null;
}
ProcessBizRouteEntity entity = list.get(0);
return ConvertUtils.sourceToTarget(entity, ProcessBizRouteDTO.class);
}
}
package io.hmit.modules.demo.controller;
import io.hmit.common.annotation.LogOperation;
import io.hmit.common.constant.Constant;
import io.hmit.common.page.PageData;
import io.hmit.common.utils.Result;
import io.hmit.common.validator.AssertUtils;
import io.hmit.common.validator.ValidatorUtils;
import io.hmit.common.validator.group.AddGroup;
import io.hmit.common.validator.group.DefaultGroup;
import io.hmit.common.validator.group.UpdateGroup;
import io.hmit.modules.demo.dto.NewsDTO;
import io.hmit.modules.demo.service.NewsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import java.util.Arrays;
import java.util.Map;
/**
* 新闻
*
* @author zsh 408538940@qq.com
*/
@RestController
@RequestMapping("demo/news")
@Api(tags = "新闻管理")
public class NewsController {
@Autowired
private NewsService newsService;
@GetMapping("page")
@ApiOperation("分页")
@ApiImplicitParams({
@ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.ORDER_FIELD, value = "排序字段", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = Constant.ORDER, value = "排序方式,可选值(asc、desc)", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "title", value = "标题", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "startDate", value = "开始时间", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "endDate", value = "结束时间", paramType = "query", dataType = "String")
})
@RequiresPermissions("demo:news:all")
public Result<PageData<NewsDTO>> page(@ApiIgnore @RequestParam Map<String, Object> params) {
PageData<NewsDTO> page = newsService.page(params);
return new Result<PageData<NewsDTO>>().ok(page);
}
@ApiOperation("信息")
@GetMapping("{id}")
@RequiresPermissions("demo:news:all")
public Result<NewsDTO> info(@PathVariable("id") Long id) {
NewsDTO news = newsService.get(id);
return new Result<NewsDTO>().ok(news);
}
@PostMapping
@ApiOperation("保存")
@LogOperation("保存")
@RequiresPermissions("demo:news:all")
public Result save(NewsDTO dto) {
//校验数据
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
newsService.save(dto);
return new Result();
}
@PutMapping
@ApiOperation("修改")
@LogOperation("修改")
@RequiresPermissions("demo:news:all")
public Result update(NewsDTO dto) {
//校验数据
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
newsService.update(dto);
return new Result();
}
@DeleteMapping
@ApiOperation("删除")
@LogOperation("删除")
@RequiresPermissions("demo:news:all")
public Result delete(@RequestBody Long[] ids) {
//校验数据
AssertUtils.isArrayEmpty(ids, "id");
newsService.deleteBatchIds(Arrays.asList(ids));
return new Result();
}
}
\ No newline at end of file
package io.hmit.modules.demo.dao;
import io.hmit.common.dao.BaseDao;
import io.hmit.modules.demo.entity.NewsEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
/**
* 新闻
*
* @author zsh 408538940@qq.com
*/
@Mapper
public interface NewsDao extends BaseDao<NewsEntity> {
List<NewsEntity> getList(Map<String, Object> params);
}
package io.hmit.modules.demo.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.hmit.common.validator.group.AddGroup;
import io.hmit.common.validator.group.DefaultGroup;
import io.hmit.common.validator.group.UpdateGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import java.io.Serializable;
import java.util.Date;
/**
* 新闻管理
*
* @author zsh 408538940@qq.com
*/
@Data
@ApiModel(value = "新闻管理")
public class NewsDTO implements Serializable {
@ApiModelProperty(value = "id")
@Null(message = "{id.null}", groups = AddGroup.class)
@NotNull(message = "{id.require}", groups = UpdateGroup.class)
private Long id;
@ApiModelProperty(value = "标题")
@NotBlank(message = "{news.title.require}", groups = DefaultGroup.class)
private String title;
@ApiModelProperty(value = "内容")
@NotBlank(message = "{news.content.require}", groups = DefaultGroup.class)
private String content;
@ApiModelProperty(value = "发布时间")
private Date pubDate;
@ApiModelProperty(value = "创建时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date createDate;
}
package io.hmit.modules.demo.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.hmit.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 新闻
*
* @author zsh 408538940@qq.com
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("tb_news")
public class NewsEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 标题
*/
private String title;
/**
* 内容
*/
private String content;
/**
* 发布时间
*/
private Date pubDate;
/**
* 创建者dept_id
*/
@TableField(fill = FieldFill.INSERT)
private Long deptId;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updater;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
}
\ No newline at end of file
package io.hmit.modules.demo.service;
import io.hmit.common.page.PageData;
import io.hmit.common.service.BaseService;
import io.hmit.modules.demo.dto.NewsDTO;
import io.hmit.modules.demo.entity.NewsEntity;
import java.util.Map;
/**
* 新闻
*
* @author zsh 408538940@qq.com
*/
public interface NewsService extends BaseService<NewsEntity> {
PageData<NewsDTO> page(Map<String, Object> params);
NewsDTO get(Long id);
void save(NewsDTO dto);
void update(NewsDTO dto);
}
package io.hmit.modules.demo.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.hmit.common.annotation.DataFilter;
import io.hmit.common.constant.Constant;
import io.hmit.common.page.PageData;
import io.hmit.common.service.impl.BaseServiceImpl;
import io.hmit.common.utils.ConvertUtils;
import io.hmit.modules.demo.dao.NewsDao;
import io.hmit.modules.demo.dto.NewsDTO;
import io.hmit.modules.demo.entity.NewsEntity;
import io.hmit.modules.demo.service.NewsService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class NewsServiceImpl extends BaseServiceImpl<NewsDao, NewsEntity> implements NewsService {
/**
* mybatis数据权限演示
*/
@Override
@DataFilter
public PageData<NewsDTO> page(Map<String, Object> params) {
paramsToLike(params, "title");
//分页
IPage<NewsEntity> page = getPage(params, Constant.CREATE_DATE, false);
//查询
List<NewsEntity> list = baseDao.getList(params);
return getPageData(list, page.getTotal(), NewsDTO.class);
}
// /**
// * mybatis-plus数据权限演示
// */
// @Override
// @DataFilter
// public PageData<NewsDTO> page(Map<String, Object> params) {
// IPage<NewsEntity> page = baseDao.selectPage(
// getPage(params, Constant.CREATE_DATE, false),
// getWrapper(params)
// );
// return getPageData(page, NewsDTO.class);
// }
//
// private QueryWrapper<NewsEntity> getWrapper(Map<String, Object> params){
// String title = (String)params.get("title");
// String startDate = (String)params.get("startDate");
// String endDate = (String)params.get("endDate");
//
// QueryWrapper<NewsEntity> wrapper = new QueryWrapper<>();
// wrapper.like(StringUtils.isNotBlank(title), "title", title);
// wrapper.ge(StringUtils.isNotBlank(startDate),"pub_date", startDate);
// wrapper.le(StringUtils.isNotBlank(endDate),"pub_date", endDate);
//
// //数据过滤
// wrapper.apply(params.get(Constant.SQL_FILTER) != null, params.get(Constant.SQL_FILTER).toString());
//
// return wrapper;
// }
@Override
public NewsDTO get(Long id) {
NewsEntity entity = baseDao.selectById(id);
return ConvertUtils.sourceToTarget(entity, NewsDTO.class);
}
@Override
public void save(NewsDTO dto) {
NewsEntity entity = ConvertUtils.sourceToTarget(dto, NewsEntity.class);
insert(entity);
}
@Override
public void update(NewsDTO dto) {
NewsEntity entity = ConvertUtils.sourceToTarget(dto, NewsEntity.class);
updateById(entity);
}
}
package io.hmit.modules.job.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
import java.util.Properties;
/**
* 定时任务配置
*
* @author zsh 408538940@qq.com
*/
@Configuration
public class ScheduleConfig {
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
//quartz参数
Properties prop = new Properties();
prop.put("org.quartz.scheduler.instanceName", "hmitScheduler");
prop.put("org.quartz.scheduler.instanceId", "AUTO");
//线程池配置
prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount", "20");
prop.put("org.quartz.threadPool.threadPriority", "5");
//JobStore配置
prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
//集群配置
prop.put("org.quartz.jobStore.isClustered", "true");
prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
prop.put("org.quartz.jobStore.misfireThreshold", "12000");
prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
//PostgreSQL数据库,需要打开此注释
//prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
factory.setQuartzProperties(prop);
factory.setSchedulerName("hmitScheduler");
//延时启动
factory.setStartupDelay(30);
factory.setApplicationContextSchedulerContextKey("applicationContextKey");
//可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
factory.setOverwriteExistingJobs(true);
//设置自动启动,默认为true
factory.setAutoStartup(true);
return factory;
}
}
package io.hmit.modules.job.controller;
import io.hmit.common.constant.Constant;
import io.hmit.common.page.PageData;
import io.hmit.common.utils.Result;
import io.hmit.modules.job.dto.ScheduleJobLogDTO;
import io.hmit.modules.job.service.ScheduleJobLogService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import java.util.Map;
/**
* 定时任务日志
*
* @author zsh 408538940@qq.com
*/
@RestController
@RequestMapping("/sys/scheduleLog")
@Api(tags = "定时任务日志")
public class ScheduleJobLogController {
@Autowired
private ScheduleJobLogService scheduleJobLogService;
@GetMapping("page")
@ApiOperation("分页")
@ApiImplicitParams({
@ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query", required = true, dataType = "int"),
@ApiImplicitParam(name = Constant.ORDER_FIELD, value = "排序字段", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = Constant.ORDER, value = "排序方式,可选值(asc、desc)", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "jobId", value = "jobId", paramType = "query", dataType = "String")
})
@RequiresPermissions("sys:schedule:log")
public Result<PageData<ScheduleJobLogDTO>> page(@ApiIgnore @RequestParam Map<String, Object> params) {
PageData<ScheduleJobLogDTO> page = scheduleJobLogService.page(params);
return new Result<PageData<ScheduleJobLogDTO>>().ok(page);
}
@GetMapping("{id}")
@ApiOperation("信息")
@RequiresPermissions("sys:schedule:log")
public Result<ScheduleJobLogDTO> info(@PathVariable("id") Long id) {
ScheduleJobLogDTO log = scheduleJobLogService.get(id);
return new Result<ScheduleJobLogDTO>().ok(log);
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment