OA板块整合与实现

This commit is contained in:
lijiaqi 2023-02-23 11:26:02 +08:00
parent 8a9252d227
commit 172e3520e0
67 changed files with 2753 additions and 26 deletions

View File

@ -1,5 +1,6 @@
package com.ydool; package com.ydool;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;

View File

@ -3,7 +3,10 @@ package com.ydool.common.advice;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.util.SaResult; import cn.dev33.satoken.util.SaResult;
import com.ydool.common.data.dto.AjaxResult; import com.ydool.common.data.dto.AjaxResult;
import com.ydool.common.data.dto.Result;
import com.ydool.common.exception.AjaxResultException;
import com.ydool.common.exception.ResultException; import com.ydool.common.exception.ResultException;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
@ -12,6 +15,7 @@ import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException; import javax.validation.ConstraintViolationException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -59,8 +63,8 @@ public class BaseControllerAdvice {
return AjaxResult.fail("参数校验失败"); return AjaxResult.fail("参数校验失败");
} }
@ExceptionHandler(ResultException.class) @ExceptionHandler(AjaxResultException.class)
public AjaxResult handlerResultException(ResultException e) { public AjaxResult handlerResultException(AjaxResultException e) {
return e.getResult(); return e.getResult();
} }
@ -93,4 +97,10 @@ public class BaseControllerAdvice {
} }
@ExceptionHandler(value = { ResultException.class, com.ydool.common.exception.NotLoginException.class })
public Result handlerResultException(ResultException e) {
return e.getResult();
}
} }

View File

@ -5,7 +5,7 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ydool.common.data.dto.AjaxResult; import com.ydool.common.data.dto.AjaxResult;
import com.ydool.common.exception.ResultException; import com.ydool.common.exception.AjaxResultException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -40,7 +40,7 @@ public class BaseController {
} }
public void renderJson(AjaxResult ajaxResult) { public void renderJson(AjaxResult ajaxResult) {
throw new ResultException(ajaxResult); throw new AjaxResultException(ajaxResult);
} }
public void fail(String msg) { public void fail(String msg) {

View File

@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ydool.common.data.dto.PageResult; import com.ydool.common.data.dto.PageResult;
import com.ydool.common.data.vo.BaseRequest; import com.ydool.common.data.vo.BaseRequest;
import com.ydool.common.exception.ResultException; import com.ydool.common.exception.AjaxResultException;
import com.ydool.common.factory.PageFactory; import com.ydool.common.factory.PageFactory;
import com.ydool.common.factory.PageResultFactory; import com.ydool.common.factory.PageResultFactory;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -16,7 +16,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Date;
public class BaseService<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> { public class BaseService<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> {
@ -36,7 +35,7 @@ public class BaseService<M extends BaseMapper<T>, T extends BaseEntity> extends
public T get(Serializable id) { public T get(Serializable id) {
T result = getById(id); T result = getById(id);
if(result == null) throw new ResultException("数据不存在"); if(result == null) throw new AjaxResultException("数据不存在");
return result; return result;
} }

View File

@ -27,6 +27,8 @@ public class ConfigCache {
*/ */
public static final String USER_INIT_PASSWORD = "sys.user.initPassword"; public static final String USER_INIT_PASSWORD = "sys.user.initPassword";
public static final String USER_ADMIN = "sys.user.admin";
public static final String SYS_USER_LEADERSHIP = "sys.user.leadership"; public static final String SYS_USER_LEADERSHIP = "sys.user.leadership";
public static final String SYS_USER_PERSONNEL_DIRECTOR= "sys.user.personnel.director"; public static final String SYS_USER_PERSONNEL_DIRECTOR= "sys.user.personnel.director";

View File

@ -0,0 +1,24 @@
package com.ydool.common.data.dto;
import java.io.Serializable;
/**
* 业务代码接口
*/
public interface IResultCode extends Serializable {
/**
* 获取消息
*
* @return
*/
String getMessage();
/**
* 获取状态码
*
* @return
*/
int getCode();
}

View File

@ -0,0 +1,245 @@
package com.ydool.common.data.dto;
import cn.hutool.core.util.ObjectUtil;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.lang.Nullable;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class Result<T> implements Serializable {
/**
* 默认成功消息
*/
public static final String DEFAULT_SUCCESS_MESSAGE = "操作成功";
/**
* 默认失败消息
*/
public static final String DEFAULT_FAILURE_MESSAGE = "操作失败";
/**
* 默认为空消息
*/
public static final String DEFAULT_NULL_MESSAGE = "暂无承载数据";
/**
* 默认未授权消息
*/
public static final String DEFAULT_UNAUTHORIZED_MESSAGE = "签名认证失败";
private int code;
private boolean success;
private T data;
private Integer count;
private String msg;
private Result(IResultCode resultCode) {
this(resultCode, null, resultCode.getMessage());
}
private Result(IResultCode resultCode, String msg) {
this(resultCode, null, msg);
}
private Result(IResultCode resultCode, T data) {
this(resultCode, data, resultCode.getMessage());
}
private Result(IResultCode resultCode, T data, String msg) {
this(resultCode.getCode(), data, msg);
}
private Result(int code, T data, String msg) {
this.code = code;
this.data = data;
this.msg = msg;
this.success = ResultCode.SUCCESS.code == code;
}
private Result(int code,int count, T data, String msg) {
this(code,data,msg);
this.count = count;
}
/**
* 判断返回是否为成功
*
* @param result Result
* @return 是否成功
*/
public static boolean isSuccess(@Nullable Result<?> result) {
return Optional.ofNullable(result)
.map(x -> ObjectUtil.equals(ResultCode.SUCCESS.code, x.code))
.orElse(Boolean.FALSE);
}
/**
* 判断返回是否为成功
*
* @param result Result
* @return 是否成功
*/
public static boolean isNotSuccess(@Nullable Result<?> result) {
return !Result.isSuccess(result);
}
/**
* 返回Result
*
* @param data 数据
* @param <T> T 泛型标记
* @return Result
*/
public static <T> Result<T> data(T data) {
return data(data, DEFAULT_SUCCESS_MESSAGE);
}
/**
* 返回Result
*
* @param data 数据
* @param msg 消息
* @param <T> T 泛型标记
* @return Result
*/
public static <T> Result<T> data(T data, String msg) {
return data(HttpServletResponse.SC_OK, data, msg);
}
/**
* 返回Result
*
* @param code 状态码
* @param data 数据
* @param msg 消息
* @param <T> T 泛型标记
* @return Result
*/
public static <T> Result<T> data(int code, T data, String msg) {
return new Result<>(code, data, data == null ? DEFAULT_NULL_MESSAGE : msg);
}
public static <T> Result<T> data(int code,int count, T data, String msg) {
return new Result<>(code,count, data, data == null ? DEFAULT_NULL_MESSAGE : msg);
}
public static <T> Result<List<T>> page(PageResult<T> pageResult) {
return data(HttpServletResponse.SC_OK,pageResult.getCount(),pageResult.getRows(),null);
}
/**
* 返回Result
*
* @param <T> T 泛型标记
* @return Result
*/
public static <T> Result<T> success() {
return new Result<>(ResultCode.SUCCESS, DEFAULT_SUCCESS_MESSAGE);
}
/**
* 返回Result
*
* @param msg 消息
* @param <T> T 泛型标记
* @return Result
*/
public static <T> Result<T> success(String msg) {
return new Result<>(ResultCode.SUCCESS, msg);
}
/**
* 返回Result
*
* @param resultCode 业务代码
* @param <T> T 泛型标记
* @return Result
*/
public static <T> Result<T> success(IResultCode resultCode) {
return new Result<>(resultCode);
}
/**
* 返回Result
*
* @param resultCode 业务代码
* @param msg 消息
* @param <T> T 泛型标记
* @return Result
*/
public static <T> Result<T> success(IResultCode resultCode, String msg) {
return new Result<>(resultCode, msg);
}
/**
* 返回Result
*
* @param msg 消息
* @param <T> T 泛型标记
* @return Result
*/
public static <T> Result<T> fail(String msg) {
return new Result<>(ResultCode.FAILURE, msg);
}
/**
* 返回Result
*
* @param code 状态码
* @param msg 消息
* @param <T> T 泛型标记
* @return Result
*/
public static <T> Result<T> fail(int code, String msg) {
return new Result<>(code, null, msg);
}
/**
* 返回Result
*
* @param resultCode 业务代码
* @param <T> T 泛型标记
* @return Result
*/
public static <T> Result<T> fail(IResultCode resultCode) {
return new Result<>(resultCode);
}
/**
* 返回Result
*
* @param resultCode 业务代码
* @param msg 消息
* @param <T> T 泛型标记
* @return Result
*/
public static <T> Result<T> fail(IResultCode resultCode, String msg) {
return new Result<>(resultCode, msg);
}
/**
* 返回Result
*
* @param flag 成功状态
* @return Result
*/
public static <T> Result<T> status(boolean flag) {
return flag ? success(DEFAULT_SUCCESS_MESSAGE) : fail(DEFAULT_FAILURE_MESSAGE);
}
}

View File

@ -0,0 +1,37 @@
package com.ydool.common.data.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import javax.servlet.http.HttpServletResponse;
@Getter
@AllArgsConstructor
public enum ResultCode implements IResultCode{
/**
* 操作成功
*/
SUCCESS(HttpServletResponse.SC_OK, "操作成功"),
/**
* 业务异常
*/
FAILURE(HttpServletResponse.SC_BAD_REQUEST, "业务异常"),
/**
* 请求未授权
*/
UN_AUTHORIZED(HttpServletResponse.SC_UNAUTHORIZED, "请求未授权"),
NOT_LOGIN(HttpServletResponse.SC_UNAUTHORIZED, "请先登录");
;
/**
* code编码
*/
final int code;
/**
* 中文信息描述
*/
final String message;
}

View File

@ -0,0 +1,26 @@
package com.ydool.common.exception;
import com.ydool.common.data.dto.AjaxResult;
public class AjaxResultException extends RuntimeException {
private AjaxResult ajaxResult;
public AjaxResultException(){
this.ajaxResult = AjaxResult.ok();
}
public AjaxResultException(String msg) {
this.ajaxResult = AjaxResult.fail(msg);
}
public AjaxResultException(AjaxResult ajaxResult){
this.ajaxResult = ajaxResult;
}
public AjaxResult getResult() {
return ajaxResult;
}
}

View File

@ -0,0 +1,17 @@
package com.ydool.common.exception;
import com.ydool.common.data.dto.Result;
import com.ydool.common.data.dto.ResultCode;
public class NotLoginException extends ResultException {
public NotLoginException() {
this.result = Result.fail(ResultCode.NOT_LOGIN);
}
public NotLoginException(String msg) {
this.result = Result.fail(ResultCode.NOT_LOGIN, msg);
}
}

View File

@ -1,26 +1,25 @@
package com.ydool.common.exception; package com.ydool.common.exception;
import com.ydool.common.data.dto.AjaxResult; import com.ydool.common.data.dto.Result;
public class ResultException extends RuntimeException { public class ResultException extends RuntimeException {
private AjaxResult ajaxResult; protected Result result;
public ResultException(){ public ResultException(){
this.ajaxResult = AjaxResult.ok(); this.result = Result.success();
} }
public ResultException(String msg) { public ResultException(String msg) {
this.ajaxResult = AjaxResult.fail(msg); this.result = Result.fail(msg);
} }
public ResultException(AjaxResult ajaxResult){ public ResultException(Result result){
this.ajaxResult = ajaxResult; this.result = result;
} }
public AjaxResult getResult() {
return ajaxResult;
}
public Result getResult() {
return result;
}
} }

View File

@ -2,7 +2,7 @@ package com.ydool.common.utils;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.ydool.common.data.dto.AjaxResult; import com.ydool.common.data.dto.AjaxResult;
import com.ydool.common.exception.ResultException; import com.ydool.common.exception.AjaxResultException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
@ -56,7 +56,7 @@ public class HttpServletUtil {
public static HttpServletRequest getRequest() { public static HttpServletRequest getRequest() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) { if (requestAttributes == null) {
throw new ResultException(AjaxResult.fail("获取不到http context请确认当前请求是http请求")); throw new AjaxResultException(AjaxResult.fail("获取不到http context请确认当前请求是http请求"));
} else { } else {
return requestAttributes.getRequest(); return requestAttributes.getRequest();
} }
@ -71,7 +71,7 @@ public class HttpServletUtil {
public static HttpServletResponse getResponse() { public static HttpServletResponse getResponse() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) { if (requestAttributes == null) {
throw new ResultException(AjaxResult.fail("获取不到http context请确认当前请求是http请求")); throw new AjaxResultException(AjaxResult.fail("获取不到http context请确认当前请求是http请求"));
} else { } else {
return requestAttributes.getResponse(); return requestAttributes.getResponse();
} }

View File

@ -0,0 +1,20 @@
package com.ydool.common.utils;
import org.springframework.util.StringUtils;
public class PathUtil {
public static String normalizePath(String path) {
if (StringUtils.hasText(path)) {
return path;
}
String normalizedPath = path;
if (!normalizedPath.startsWith("/")) {
normalizedPath = "/" + normalizedPath;
}
if (normalizedPath.endsWith("/")) {
normalizedPath = normalizedPath.substring(0, normalizedPath.length() - 1);
}
return normalizedPath;
}
}

View File

@ -0,0 +1,34 @@
package com.ydool.common.utils;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Date;
public class UploadUtil {
public static String uploadFile(MultipartFile file) {
try {
String fileName = getFileUploadPath() + "/" + IdUtil.simpleUUID() + "." + FileUtil.extName(file.getOriginalFilename());
String fullFilePath = getWebRoot() + fileName;
FileUtil.writeFromStream(file.getInputStream(), fullFilePath);
return fileName;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
public static String getFileUploadPath() {
String folder = DateUtil.format(new Date(), "yyyy/MM/dd");
return "/upload/" + folder;
}
public static String getWebRoot() {
return System.getProperty("user.dir");
}
}

View File

@ -0,0 +1,145 @@
package com.ydool.common.utils;
import cn.hutool.core.collection.ArrayIter;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ydool.common.data.dto.Result;
import com.ydool.common.exception.ResultException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.function.Predicate;
@Slf4j
public class WebUtil {
private static final String[] IP_HEADER_NAMES = new String[]{
"x-forwarded-for",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_CLIENT_IP",
"HTTP_X_FORWARDED_FOR"
};
private static final Predicate<String> IP_PREDICATE = (ip) -> StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip);
public static String getRemoteAddress() {
return getRemoteAddress(getRequest());
}
public static String getRemoteAddress(HttpServletRequest request) {
if (request == null) {
return "unknown";
}
String ip = null;
for (String ipHeader : IP_HEADER_NAMES) {
ip = request.getHeader(ipHeader);
if (!IP_PREDICATE.test(ip)) {
break;
}
}
if (IP_PREDICATE.test(ip)) {
ip = request.getRemoteAddr();
}
return StrUtil.isBlank(ip) ? null : StrUtil.splitTrim(ip, ",").get(0);
}
/**
* 获取当前请求的request对象
*
* @author fengshuonan
* @date 2020/10/15 17:48
*/
public static HttpServletRequest getRequest() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
throw new ResultException(Result.fail("获取不到http context请确认当前请求是http请求"));
} else {
return requestAttributes.getRequest();
}
}
/**
* 获取当前请求的response对象
*
* @author fengshuonan
* @date 2020/10/15 17:48
*/
public static HttpServletResponse getResponse() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
throw new ResultException(Result.fail("获取不到http context请确认当前请求是http请求"));
} else {
return requestAttributes.getResponse();
}
}
public static boolean write(Object result) {
HttpServletResponse response = getResponse();
return write(response, result);
}
public static boolean write(HttpServletResponse response, Object result) {
if(result instanceof Result){
response.setStatus(((Result) result).getCode());
}
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
try (PrintWriter out = response.getWriter()) {
out.append(JSONUtil.toJsonStr(result));
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return false;
}
public static Map<String, Cookie> readCookieMap(HttpServletRequest httpServletRequest) {
Cookie[] cookies = httpServletRequest.getCookies();
return ArrayUtil.isEmpty(cookies) ? MapUtil.empty() : IterUtil.toMap(new ArrayIter(httpServletRequest.getCookies()), new CaseInsensitiveMap(), Cookie::getName);
}
public static void addCookie(String name, String value) {
getResponse().addCookie(new Cookie(name, value));
}
public static void addCookie(String name, String value, int expiry) {
Cookie cookie = new Cookie(name, value);
cookie.setMaxAge(expiry);
cookie.setPath("/");
getResponse().addCookie(cookie);
}
public static String getCookie(String name) {
Map<String, Cookie> cookieMap = readCookieMap(getRequest());
return cookieMap.containsKey(name) ? cookieMap.get(name).getValue() : null;
}
public static boolean isAjax(){
HttpServletRequest request = getRequest();
/*if(!StrUtil.isBlank(request.getHeader("x-requested-with")) && request.getHeader("x-requested-with").equals("XMLHttpRequest")){
return true;
}*/
String accept = request.getHeader("accept");
if (accept != null && accept.indexOf("application/json") != -1) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,150 @@
package com.ydool.oa.flow.constant;
public interface FlowConstant {
/**
* 执行中
*/
String FLOW_STATUS_ACTIVE = "ACTIVE";
/**
* 通过
*/
String FLOW_STATUS_PASS = "PASS";
/**
* 驳回
*/
String FLOW_STATUS_REFUSE = "REFUSE";
/**
* 结束
*/
String FLOW_STATUS_DONE = "DONE";
/**
* 驳回
*/
String STEP_STATUS_ACTIVE = "ACTIVE";
/**
* 通过
*/
String STEP_STATUS_PASS = "PASS";
/**
* 驳回
*/
String STEP_STATUS_REFUSE = "REFUSE";
/**
* 审批人
*/
String PROCESS_TYPE_APPROVAL = "APPROVAL";
/**
* 抄送人
*/
String PROCESS_TYPE_CC = "CC";
/**
* 条件分支
*/
String PROCESS_TYPE_CONDITIONS = "CONDITIONS";
/**
* 延迟等待
*/
String PROCESS_TYPE_DELAY = "DELAY";
/**
* 触发器
*/
String PROCESS_TYPE_TRIGGER = "TRIGGER";
/**
* 并行分支
*/
String PROCESS_TYPE_CONCURRENTS = "CONCURRENTS";
/**
* 指定用户
*/
String ASSIGNED_TYPE_ASSIGN_USER = "ASSIGN_USER";
/**
* 指定部门
*/
String ASSIGNED_TYPE_ASSIGN_DEPT = "ASSIGN_DEPT";
/**
* 用户所在部门主管
*/
String ASSIGNED_TYPE_LEADER = "LEADER";
/**
* 发起人自己
*/
String ASSIGNED_TYPE_SELF = "SELF";
/**
* 表单内联系人
*/
String ASSIGNED_TYPE_FORM_USER = "FORM_USER";
/**
* 表单内部门
*/
String ASSIGNED_TYPE_FORM_DEPT = "FORM_DEPT";
/**
* 会签按选择顺序审批每个人必须同意
*/
String MODE_NEXT = "NEXT";
/**
* 会签可同时审批每个人必须同意
*/
String MODE_AND = "AND";
/**
* 或签有一人同意即可
*/
String MODE_OR = "OR";
/**
* 自动通过
*/
String NOBODY_O_PASS = "O_PASS";
/**
* 自动驳回
*/
String NOBODY_O_REFUSE = "O_REFUSE";
/**
* 转交审批管理员
*/
String NOBODY_TO_ADMIN = "TO_ADMIN";
/**
* 转交指定人员
*/
String NOBODY_O_USER = "O_user";
/**
* 直接结束流程
*/
String REFUSE_TYPE_TO_END = "TO_END";
/**
* 当前节点
*/
String REFUSE_TYPE_TO_CURRENT = "TO_CURRENT";
/**
* 驳回到指定节点
*/
String REFUSE_TYPE_TO_NODE = "TO_NODE";
}

View File

@ -0,0 +1,97 @@
package com.ydool.oa.flow.controller;
import com.ydool.common.data.dto.PageResult;
import com.ydool.common.data.dto.Result;
import com.ydool.oa.flow.data.dto.FlowDto;
import com.ydool.oa.flow.data.dto.FlowStepDto;
import com.ydool.oa.flow.data.entity.Flow;
import com.ydool.oa.flow.data.entity.FlowStep;
import com.ydool.oa.flow.data.vo.FlowStepVo;
import com.ydool.oa.flow.data.vo.FlowVo;
import com.ydool.oa.flow.data.vo.ProcessVo;
import com.ydool.oa.flow.data.wrapper.FlowStepWrapper;
import com.ydool.oa.flow.data.wrapper.FlowWrapper;
import com.ydool.oa.flow.data.wrapper.ProcessWrapper;
import com.ydool.oa.flow.service.FlowService;
import com.ydool.oa.flow.service.FlowStepService;
import com.ydool.oa.formily.constant.GrapeConst;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@RequestMapping(value = GrapeConst.API + "/formily/process")
@Api(tags = "流程管理", value = "流程管理")
public class ProcessController {
@Resource
private FlowService flowService;
@Resource
private FlowStepService flowStepService;
@PostMapping("{id}/start")
@ApiOperation("发起流程")
public Result start(@RequestBody FlowDto dto) {
return flowService.startProcessById(dto);
}
@PostMapping("{id}/restart")
@ApiOperation("重新发起流程")
public Result restart(@RequestBody FlowDto dto) {
return flowService.restartProcessById(dto);
}
@GetMapping("{id}/task")
@ApiOperation("查看任务")
public Result<ProcessVo> task(@PathVariable String id, String stepId) {
Flow flow = flowService.getById(id);
if(flow == null) return Result.fail("流程不存在");
FlowStep flowStep = null;
if(stepId != null) {
flowStep = flowStepService.getById(stepId);
if (flowStep == null) return Result.fail("流程节点不存在");
}
return Result.data(ProcessWrapper.build().toVo(flow, flowStep));
}
@PostMapping("{id}/complete")
@ApiOperation("完成任务")
public Result complete(@RequestBody FlowStepDto dto) {
return flowStepService.completeFlowStep(dto);
}
/**
* 我发起的
* @return
*/
@GetMapping("query_create")
public Result<PageResult<FlowVo>> queryCreate() {
return Result.data(FlowWrapper.build().toVo(flowService.getCreateList()));
}
/**
* 我待处理
* @return
*/
@GetMapping("query_todo")
public Result<PageResult<FlowStepVo>> queryTodo() {
return Result.data(FlowStepWrapper.build().toVo(flowStepService.getFlowTodoList(false)));
}
/**
* 已完成的
* @return
*/
@GetMapping("query_done")
public Result<PageResult<FlowStepVo>> queryDone() {
return Result.data(FlowStepWrapper.build().toVo(flowStepService.getFlowTodoList(true)));
}
}

View File

@ -0,0 +1,25 @@
package com.ydool.oa.flow.data.dto;
import cn.hutool.json.JSONObject;
import lombok.Data;
@Data
public class FlowDto {
/**
* 表单ID
*/
private String formId;
/**
* 表单内容
*/
private JSONObject data;
/**
* 流程ID
*/
private String flowId;
}

View File

@ -0,0 +1,29 @@
package com.ydool.oa.flow.data.dto;
import cn.hutool.json.JSONObject;
import lombok.Data;
@Data
public class FlowStepDto {
/**
* 流程 Step ID
*/
private String id;
/**
* 表单内容
*/
private JSONObject data;
/**
* 通过 or 驳回
* PASS or REFUSE
*/
private String action;
/**
* 驳回理由
*/
private String refuse;
}

View File

@ -0,0 +1,34 @@
package com.ydool.oa.flow.data.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ydool.common.base.BaseEntity;
import lombok.Data;
@Data
@TableName(value = "t_bc_flow")
public class
Flow extends BaseEntity {
private String formId;
private String userId;
private String name;
private Integer step;
private String stepName;
@JsonIgnore
private String items;
@JsonIgnore
private String data;
@JsonIgnore
private String formPerms;
private String status;
}

View File

@ -0,0 +1,50 @@
package com.ydool.oa.flow.data.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ydool.common.base.BaseEntity;
import lombok.Data;
@Data
@TableName(value = "t_bc_flow_step")
public class FlowStep extends BaseEntity {
@JsonSerialize(using = ToStringSerializer.class)
private String formId;
@JsonSerialize(using = ToStringSerializer.class)
private String flowId;
private Integer step;
private String stepName;
@JsonIgnore
private String userIds;
@JsonIgnore
private String mode;
/**
* 驳回后
*/
@JsonIgnore
private String refuseType;
private String status;
private String type;
@JsonIgnore
private String assignedType;
@JsonIgnore
private String nextStep;
@JsonIgnore
private String formPerms;
}

View File

@ -0,0 +1,33 @@
package com.ydool.oa.flow.data.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ydool.common.base.BaseEntity;
import lombok.Data;
@Data
@TableName(value = "t_bc_flow_step_log")
public class FlowStepLog extends BaseEntity {
private String formId;
private String flowId;
private String stepId;
private String userId;
private Integer step;
private String status;
/**
* Approval
* cc
* write
* done
*/
private String type;
private String msg;
}

View File

@ -0,0 +1,19 @@
package com.ydool.oa.flow.data.vo;
import com.ydool.oa.flow.data.entity.FlowStepLog;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class FlowStepLogVo extends FlowStepLog {
private String name;
private String avatar;
private String status;
private String msg;
}

View File

@ -0,0 +1,21 @@
package com.ydool.oa.flow.data.vo;
import com.ydool.oa.flow.data.entity.FlowStep;
import com.ydool.oa.formily.userdept.UserDept;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class FlowStepVo extends FlowStep {
private String name;
private List<UserDept> users;
private List<FlowStepLogVo> logs;
}

View File

@ -0,0 +1,14 @@
package com.ydool.oa.flow.data.vo;
import com.ydool.oa.flow.data.entity.Flow;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class FlowVo extends Flow {
private String avatar;
}

View File

@ -0,0 +1,24 @@
package com.ydool.oa.flow.data.vo;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class ProcessVo {
private FlowVo flow;
private JSONObject data;
private JSONArray items;
private JSONArray formPerms;
private List<FlowStepVo> steps;
}

View File

@ -0,0 +1,44 @@
package com.ydool.oa.flow.data.wrapper;
import cn.hutool.core.bean.BeanUtil;
import com.ydool.common.data.dto.PageResult;
import java.util.List;
import java.util.stream.Collectors;
public abstract class BaseWrapper<T, V> {
/**
* 单个实体类包装
*
* @param entity 实体类
* @return V
*/
public abstract V toVo(T entity);
/**
* 实体类集合包装
*
* @param list 实体类集合
* @return List<V>
*/
public List<V> toVo(List<T> list) {
return list.stream().map(this::toVo).collect(Collectors.toList());
}
/**
* 分页实体类集合包装
*
* @param t 分页对象
* @return IPage<V>
*/
public PageResult<V> toVo(PageResult<T> t) {
List<T> list = t.getRows();
List<V> dtoList = toVo(list);
PageResult<V> pageResult = new PageResult<>();
BeanUtil.copyProperties(t, pageResult);
pageResult.setRows(dtoList);
return pageResult;
}
}

View File

@ -0,0 +1,31 @@
package com.ydool.oa.flow.data.wrapper;
import cn.hutool.extra.spring.SpringUtil;
import com.ydool.oa.flow.data.entity.FlowStepLog;
import com.ydool.oa.flow.data.vo.FlowStepLogVo;
import com.ydool.oa.formily.userdept.IUserDeptService;
import com.ydool.oa.formily.userdept.UserDept;
public class FlowStepLogWrapper extends BaseWrapper<FlowStepLog, FlowStepLogVo> {
public static FlowStepLogWrapper build() {
return new FlowStepLogWrapper();
}
@Override
public FlowStepLogVo toVo(FlowStepLog entity) {
IUserDeptService userDeptService = SpringUtil.getBean(IUserDeptService.class);
FlowStepLogVo vo = new FlowStepLogVo();
vo.setStatus(entity.getStatus());
vo.setMsg(entity.getMsg());
vo.setCreatedAt(entity.getCreatedAt());
UserDept userDept = userDeptService.getUserById(entity.getUserId());
vo.setName(userDept.getName());
vo.setAvatar(userDept.getAvatar());
return vo;
}
}

View File

@ -0,0 +1,61 @@
package com.ydool.oa.flow.data.wrapper;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.ydool.oa.flow.data.entity.FlowStep;
import com.ydool.oa.flow.data.entity.FlowStepLog;
import com.ydool.oa.flow.data.vo.FlowStepVo;
import com.ydool.oa.flow.service.FlowStepLogService;
import com.ydool.oa.formily.data.entity.Formily;
import com.ydool.oa.formily.service.FormilyService;
import com.ydool.oa.formily.userdept.IUserDeptService;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class FlowStepWrapper extends BaseWrapper<FlowStep, FlowStepVo> {
public static FlowStepWrapper build() {
return new FlowStepWrapper();
}
@Override
public FlowStepVo toVo(FlowStep entity) {
IUserDeptService userDeptService = SpringUtil.getBean(IUserDeptService.class);
FlowStepVo vo = Objects.requireNonNull(BeanUtil.copyProperties(entity, FlowStepVo.class));
vo.setUsers(userDeptService.getUserListByUserId(StrUtil.split(entity.getUserIds(), ",")));
FormilyService formilyService = SpringUtil.getBean(FormilyService.class);
Formily formily = formilyService.getById(entity.getFormId());
if(formily != null) {
vo.setName(formily.getName());
}
return vo;
}
public FlowStepVo toLogVo(FlowStep entity) {
IUserDeptService userDeptService = SpringUtil.getBean(IUserDeptService.class);
FlowStepVo vo = new FlowStepVo();
vo.setStep(entity.getStep());
vo.setStepName(entity.getStepName());
vo.setUsers(userDeptService.getUserListByUserId(StrUtil.split(entity.getUserIds(), ",")));
vo.setCreatedAt(entity.getCreatedAt());
vo.setStatus(entity.getStatus());
vo.setType(entity.getType());
FlowStepLogService flowStepLogService = SpringUtil.getBean(FlowStepLogService.class);
List<FlowStepLog> logs = flowStepLogService.getFlowStepLogListByStepId(entity.getId());
vo.setLogs(FlowStepLogWrapper.build().toVo(logs));
return vo;
}
public List<FlowStepVo> toLogVo(List<FlowStep> list) {
return list.stream().map(this::toLogVo).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,23 @@
package com.ydool.oa.flow.data.wrapper;
import cn.hutool.core.bean.BeanUtil;
import com.ydool.oa.flow.data.entity.Flow;
import com.ydool.oa.flow.data.vo.FlowVo;
import java.util.Objects;
public class FlowWrapper extends BaseWrapper<Flow, FlowVo> {
public static FlowWrapper build() {
return new FlowWrapper();
}
@Override
public FlowVo toVo(Flow entity) {
FlowVo flowVo = Objects.requireNonNull(BeanUtil.copyProperties(entity, FlowVo.class));
return flowVo;
}
}

View File

@ -0,0 +1,47 @@
package com.ydool.oa.flow.data.wrapper;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.ydool.oa.flow.data.entity.Flow;
import com.ydool.oa.flow.data.entity.FlowStep;
import com.ydool.oa.flow.data.vo.FlowVo;
import com.ydool.oa.flow.data.vo.ProcessVo;
import com.ydool.oa.flow.service.FlowStepService;
import com.ydool.oa.formily.userdept.IUserDeptService;
import com.ydool.oa.formily.userdept.UserDept;
import java.util.List;
public class ProcessWrapper {
public static ProcessWrapper build() {
return new ProcessWrapper();
}
public ProcessVo toVo(Flow flow, FlowStep flowStep) {
IUserDeptService userDeptService = SpringUtil.getBean(IUserDeptService.class);
UserDept user = userDeptService.getUserById(flow.getUserId());
ProcessVo vo = new ProcessVo();
FlowVo fvo = new FlowVo();
fvo.setName(user.getName());
fvo.setAvatar(user.getAvatar());
fvo.setStatus(flow.getStatus());
fvo.setCreatedAt(flow.getCreatedAt());
vo.setFlow(fvo);
vo.setItems(JSONUtil.parseArray(flow.getItems()));
vo.setData(JSONUtil.parseObj(flow.getData()));
if(flowStep == null) {
vo.setFormPerms(JSONUtil.parseArray(flow.getFormPerms()));
}else {
vo.setFormPerms(JSONUtil.parseArray(flowStep.getFormPerms()));
}
FlowStepService flowStepService = SpringUtil.getBean(FlowStepService.class);
List<FlowStep> flowStepList = flowStepService.getFlowStepListByFlowId(flow.getId());
vo.setSteps(FlowStepWrapper.build().toLogVo(flowStepList));
return vo;
}
}

View File

@ -0,0 +1,22 @@
package com.ydool.oa.flow.engine;
import lombok.Data;
@Data
public class FlowProcess {
private String id;
private String parentId;
private String type;
private String name;
private String desc;
private ProcessProps props;
private FlowProcess children;
}

View File

@ -0,0 +1,14 @@
package com.ydool.oa.flow.engine;
import com.ydool.oa.formily.userdept.UserDept;
import lombok.Data;
import java.util.List;
@Data
public class ProcessNoBody {
private String handler;
private List<UserDept> assignedUser;
}

View File

@ -0,0 +1,34 @@
package com.ydool.oa.flow.engine;
import cn.hutool.json.JSONArray;
import com.ydool.oa.formily.userdept.UserDept;
import lombok.Data;
import java.util.List;
@Data
public class ProcessProps {
/**
* 审批方式
*/
private String assignedType;
/**
* 多人审批时审批方式
* NEXT 会签
* AND 会签
* OR 或签
*/
private String mode;
private ProcessNoBody nobody;
private ProcessRefuse refuse;
private JSONArray formPerms;
private List<UserDept> assignedUser;
private List<UserDept> assignedDept;
}

View File

@ -0,0 +1,14 @@
package com.ydool.oa.flow.engine;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ProcessRefuse {
private String type;
private String target;
}

View File

@ -0,0 +1,40 @@
package com.ydool.oa.flow.engine.factory;
import com.ydool.common.exception.ResultException;
import com.ydool.oa.flow.engine.service.IProcessType;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class ProcessTypeFactory implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
private Map<String, IProcessType> processTypeMap = new ConcurrentHashMap<>();
private ApplicationContext applicationContext;
public IProcessType getProcessType(String processType) {
IProcessType targetProcessType = Optional.ofNullable(processTypeMap.get(processType)).orElseThrow(() -> new ResultException("找不到 [" + processType + "] 流程节点的处理类"));
return targetProcessType;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Map<String, IProcessType> beansOfType = applicationContext.getBeansOfType(IProcessType.class);
beansOfType.values().forEach(source -> processTypeMap.put(source.getName(), source));
}
}

View File

@ -0,0 +1,17 @@
package com.ydool.oa.flow.engine.service;
import com.ydool.oa.flow.data.dto.FlowStepDto;
import com.ydool.oa.flow.data.entity.Flow;
import com.ydool.oa.flow.data.entity.FlowStep;
import com.ydool.oa.flow.engine.FlowProcess;
public interface IProcessType {
void execute(Flow flow, FlowProcess process);
void complete(Flow flow, FlowStep flowStep, FlowStepDto dto);
String getName();
}

View File

@ -0,0 +1,189 @@
package com.ydool.oa.flow.engine.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ydool.oa.flow.constant.FlowConstant;
import com.ydool.oa.flow.data.dto.FlowStepDto;
import com.ydool.oa.flow.data.entity.Flow;
import com.ydool.oa.flow.data.entity.FlowStep;
import com.ydool.oa.flow.data.entity.FlowStepLog;
import com.ydool.oa.flow.engine.FlowProcess;
import com.ydool.oa.flow.engine.ProcessNoBody;
import com.ydool.oa.flow.engine.ProcessProps;
import com.ydool.oa.flow.engine.service.IProcessType;
import com.ydool.oa.flow.service.FlowService;
import com.ydool.oa.flow.service.FlowStepLogService;
import com.ydool.oa.flow.service.FlowStepService;
import com.ydool.oa.formily.userdept.IUserDeptService;
import com.ydool.oa.formily.userdept.UserDept;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* 根据审批人进行审批逻辑
*/
@Service
public class ProcessApproval implements IProcessType {
@Resource
private IUserDeptService userDeptService;
@Resource
private FlowService flowService;
@Resource
private FlowStepService flowStepService;
@Resource
private FlowStepLogService flowStepLogService;
@Override
public void execute(Flow flow, FlowProcess process) {
ProcessProps props = process.getProps();
FlowStep flowStep = new FlowStep();
flowStep.setFlowId(flow.getId());
flowStep.setFormId(flow.getFormId());
flowStep.setStep(flow.getStep());
flowStep.setStepName(process.getName());
flowStep.setMode(props.getMode());
flowStep.setRefuseType(props.getRefuse().getType());
flowStep.setType(process.getType());
flowStep.setAssignedType(props.getAssignedType());
flowStep.setStatus(FlowConstant.STEP_STATUS_ACTIVE);
flowStep.setNextStep(JSONUtil.toJsonStr(process.getChildren()));
flowStep.setFormPerms(process.getProps().getFormPerms().toString());
switch (props.getAssignedType()){
case FlowConstant.ASSIGNED_TYPE_ASSIGN_USER: { // 指定用户
if(props.getAssignedUser().size() == 0) {
this.doNoBody(flowStep, props.getNobody());
} else {
flowStep.setUserIds(StrUtil.join(",", CollUtil.getFieldValues(props.getAssignedUser(), "id", String.class)));
}
}
case FlowConstant.ASSIGNED_TYPE_ASSIGN_DEPT: { // 指定部门负责人
List<UserDept> userDeptList = userDeptService.getUserListByDeptId(CollUtil.getFieldValues(props.getAssignedDept(), "id", String.class));
flowStep.setUserIds(StrUtil.join(",", CollUtil.getFieldValues(userDeptList, "id", String.class)));
}
case FlowConstant.ASSIGNED_TYPE_LEADER: { // 用户所在部门领导
UserDept leader = userDeptService.getUserDeptLeader(flow.getUserId());
if(leader == null) this.doNoBody(flowStep, props.getNobody());
else flowStep.setUserIds(leader.getId().toString());
}
case FlowConstant.ASSIGNED_TYPE_SELF: { // 发起人自己
flowStep.setUserIds(flow.getUserId().toString());
}
case FlowConstant.ASSIGNED_TYPE_FORM_USER: { // 表单内联系人
}
case FlowConstant.ASSIGNED_TYPE_FORM_DEPT: { // 表单内部门
}
}
if(StrUtil.isEmpty(flowStep.getUserIds())) {
this.doNoBody(flowStep, props.getNobody());
}
flowStepService.save(flowStep);
}
@Override
public void complete(Flow flow, FlowStep flowStep, FlowStepDto dto) {
UserDept current = userDeptService.getCurrentUser();
FlowStepLog log = new FlowStepLog();
log.setType(FlowConstant.PROCESS_TYPE_APPROVAL);
log.setFormId(flowStep.getFormId());
log.setFlowId(flowStep.getFlowId());
log.setStepId(flowStep.getId());
log.setStep(flowStep.getStep());
log.setStatus(dto.getAction());
log.setMsg(dto.getRefuse());
log.setUserId(current.getId());
flowStepLogService.save(log);
if(FlowConstant.STEP_STATUS_PASS.equals(dto.getAction())) {
// 判断是否全部完成
switch (flowStep.getMode()) {
case FlowConstant.MODE_OR: { // 会签 只要一个同意 就提交到下一节点
this.doCreateNextFlowStep(flow, flowStep);
}
case FlowConstant.MODE_AND: { // 与签 需要全部签完 才能提交到下一节点
if(flowStepLogService.isAllApproval(flowStep, StrUtil.split(flowStep.getUserIds(), ","))){
this.doCreateNextFlowStep(flow, flowStep);
}
}
}
} else { // 拒绝
this.doRefuse(flow, flowStep);
}
}
/**
* 找不到审批人或者审批部门
* @param flowStep
* @param noBody
*/
public void doNoBody(FlowStep flowStep, ProcessNoBody noBody) {
switch (noBody.getHandler()){
case FlowConstant.NOBODY_O_PASS: { // 直接通过
flowStep.setStatus(FlowConstant.STEP_STATUS_PASS);
}
case FlowConstant.NOBODY_O_REFUSE: { // 直接拒绝
flowStep.setStatus(FlowConstant.STEP_STATUS_REFUSE);
}
case FlowConstant.NOBODY_O_USER: { // 指定人员
flowStep.setUserIds(StrUtil.join(",", CollUtil.getFieldValues(noBody.getAssignedUser(), "id", String.class)));
}
case FlowConstant.NOBODY_TO_ADMIN: { // 指定管理员
UserDept admin = userDeptService.getAdminUserDepot();
if(admin != null) flowStep.setUserIds(admin.getId().toString());
}
}
}
/**
* 创建下一节点
* @param flow
* @param flowStep
*/
public void doCreateNextFlowStep(Flow flow, FlowStep flowStep) {
flowStep.setStatus(FlowConstant.STEP_STATUS_PASS);
flowStepService.updateById(flowStep);
FlowProcess nextFlow = JSONUtil.toBean(flowStep.getNextStep(), FlowProcess.class);
if(StrUtil.isEmpty(nextFlow.getId())) {
flow.setStatus(FlowConstant.FLOW_STATUS_PASS);
}else {
flow.setStep(flow.getStep()+1);
flow.setStepName(nextFlow.getName());
flowStepService.createFlowStep(flow, nextFlow);
}
}
/**
* 决绝操作
* @param flow
* @param flowStep
*/
public void doRefuse(Flow flow, FlowStep flowStep) {
flowStep.setStatus(FlowConstant.STEP_STATUS_REFUSE);
flowStepService.updateById(flowStep);
if(FlowConstant.REFUSE_TYPE_TO_END.equals(flowStep.getRefuseType())) {
flow.setStatus(FlowConstant.FLOW_STATUS_DONE);
}else {
flow.setStatus(FlowConstant.FLOW_STATUS_REFUSE);
}
}
@Override
public String getName() {
return FlowConstant.PROCESS_TYPE_APPROVAL;
}
}

View File

@ -0,0 +1,10 @@
package com.ydool.oa.flow.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ydool.oa.flow.data.entity.Flow;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface FlowMapper extends BaseMapper<Flow> {
}

View File

@ -0,0 +1,10 @@
package com.ydool.oa.flow.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ydool.oa.flow.data.entity.FlowStepLog;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface FlowStepLogMapper extends BaseMapper<FlowStepLog> {
}

View File

@ -0,0 +1,11 @@
package com.ydool.oa.flow.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ydool.oa.flow.data.entity.FlowStep;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface FlowStepMapper extends BaseMapper<FlowStep> {
}

View File

@ -0,0 +1,98 @@
package com.ydool.oa.flow.service;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ydool.common.base.BaseService;
import com.ydool.common.data.dto.PageResult;
import com.ydool.common.data.dto.Result;
import com.ydool.common.factory.PageFactory;
import com.ydool.common.factory.PageResultFactory;
import com.ydool.oa.flow.constant.FlowConstant;
import com.ydool.oa.flow.data.dto.FlowDto;
import com.ydool.oa.flow.data.entity.Flow;
import com.ydool.oa.flow.data.entity.FlowStep;
import com.ydool.oa.flow.engine.FlowProcess;
import com.ydool.oa.flow.mapper.FlowMapper;
import com.ydool.oa.formily.data.entity.Formily;
import com.ydool.oa.formily.service.FormilyService;
import com.ydool.oa.formily.userdept.IUserDeptService;
import com.ydool.oa.formily.userdept.UserDept;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
public class FlowService extends BaseService<FlowMapper, Flow> {
@Resource
private IUserDeptService userDeptService;
@Resource
private FormilyService formilyService;
@Resource
private FlowStepService flowStepService;
/**
* 我发起的
* @return
*/
public PageResult<Flow> getCreateList() {
UserDept currentUser = userDeptService.getCurrentUser();
LambdaQueryWrapper<Flow> qw = new LambdaQueryWrapper<>(Flow.class).eq(Flow::getUserId, currentUser.getId()).orderByDesc(Flow::getCreatedAt);
PageResult<Flow> pageResult = PageResultFactory.createPageResult(page(PageFactory.defaultPage(),qw));
return pageResult;
}
/**
* 根据表单ID开启一个流程
* @param dto
* @return
*/
@Transactional
public Result startProcessById(FlowDto dto) {
Formily formily = formilyService.getById(dto.getFormId());
if(formily == null) return Result.fail("表单不存在");
FlowProcess root = JSONUtil.toBean(formily.getProcess(), FlowProcess.class);
FlowProcess firstProcess = root.getChildren();
if(firstProcess == null) return Result.fail("表单内不存在第一步设置");
Flow flow = this.doCreateFlow(formily, dto, root, firstProcess);
flowStepService.createFlowStep(flow, firstProcess);
return Result.success();
}
@Transactional
public Result restartProcessById(FlowDto dto) {
Flow flow = getById(dto.getFlowId());
if(flow == null) return Result.fail("流程不存在");
FlowStep flowStep = flowStepService.getLastApprovalFlowStep(flow.getId());
if(flowStep != null) {
flow.setStep(flow.getStep() + 1);
flow.setData(dto.getData().toString());
this.updateById(flow);
flowStep.setStep(flow.getStep());
flowStep.setStatus(FlowConstant.STEP_STATUS_ACTIVE);
flowStepService.save(flowStep);
}
return Result.success();
}
public Flow doCreateFlow(Formily formily, FlowDto dto, FlowProcess root, FlowProcess firstProcess) {
Flow flow = new Flow();
flow.setUserId(userDeptService.getCurrentUser().getId());
flow.setFormId(dto.getFormId());
flow.setName(formily.getName());
flow.setStep(1);
flow.setStepName(firstProcess.getName());
flow.setName(firstProcess.getName());
flow.setItems(formily.getItems());
flow.setData(dto.getData().toString());
flow.setFormPerms(root.getProps().getFormPerms().toString());
flow.setStatus(FlowConstant.FLOW_STATUS_ACTIVE);
save(flow);
return flow;
}
}

View File

@ -0,0 +1,37 @@
package com.ydool.oa.flow.service;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ydool.common.base.BaseService;
import com.ydool.oa.flow.data.entity.FlowStep;
import com.ydool.oa.flow.data.entity.FlowStepLog;
import com.ydool.oa.flow.mapper.FlowStepLogMapper;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class FlowStepLogService extends BaseService<FlowStepLogMapper, FlowStepLog> {
/**
* 判断是否已全部审批
* @param flowStep 审批节点
* @param userIds 需要审批的用户id
* @return
*/
public Boolean isAllApproval(FlowStep flowStep, List<String> userIds) {
List<FlowStepLog> flowStepLogList = getFlowStepLogListByStepId(flowStep.getId());
List<String> idList = CollUtil.getFieldValues(flowStepLogList, "userId", String.class);
List<String> reduceId = userIds.stream().filter(item -> !idList.contains(item)).collect(Collectors.toList());
return reduceId.isEmpty();
}
public List<FlowStepLog> getFlowStepLogListByStepId(String stepId) {
LambdaQueryWrapper<FlowStepLog> qw = new LambdaQueryWrapper<>();
qw.eq(FlowStepLog::getStepId, stepId).orderByDesc(FlowStepLog::getCreatedAt);
return list(qw);
}
}

View File

@ -0,0 +1,90 @@
package com.ydool.oa.flow.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ydool.common.base.BaseService;
import com.ydool.common.data.dto.PageResult;
import com.ydool.common.data.dto.Result;
import com.ydool.common.factory.PageFactory;
import com.ydool.common.factory.PageResultFactory;
import com.ydool.oa.flow.constant.FlowConstant;
import com.ydool.oa.flow.data.dto.FlowStepDto;
import com.ydool.oa.flow.data.entity.Flow;
import com.ydool.oa.flow.data.entity.FlowStep;
import com.ydool.oa.flow.engine.FlowProcess;
import com.ydool.oa.flow.engine.factory.ProcessTypeFactory;
import com.ydool.oa.flow.mapper.FlowStepMapper;
import com.ydool.oa.formily.userdept.IUserDeptService;
import com.ydool.oa.formily.userdept.UserDept;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class FlowStepService extends BaseService<FlowStepMapper, FlowStep> {
@Resource
private ProcessTypeFactory processTypeFactory;
@Resource
private IUserDeptService userDeptService;
@Resource
private FlowService flowService;
/**
* 我待处理
* @param isDone true: 返回 PASS REFUSE false: 返回 ACTIVE
* @return
*/
public PageResult<FlowStep> getFlowTodoList(Boolean isDone) {
UserDept currentUser = userDeptService.getCurrentUser();
LambdaQueryWrapper<FlowStep> qw = new LambdaQueryWrapper<>(FlowStep.class).orderByDesc(FlowStep::getCreatedAt);
if(isDone) qw.ne(FlowStep::getStatus, FlowConstant.FLOW_STATUS_DONE);
else qw.eq(FlowStep::getStatus, FlowConstant.STEP_STATUS_ACTIVE);
qw.apply("FIND_IN_SET (" + currentUser.getId() + ", user_ids)");
PageResult<FlowStep> pageResult = PageResultFactory.createPageResult(page(PageFactory.defaultPage(),qw));
return pageResult;
}
// 设置审批人
public void createFlowStep(Flow flow, FlowProcess process) {
processTypeFactory.getProcessType(process.getType()).execute(flow, process);
}
public Result completeFlowStep(FlowStepDto dto) {
FlowStep flowStep = getById(dto.getId());
if(flowStep != null) {
Flow flow = flowService.getById(flowStep.getFlowId());
flow.setData(dto.getData().toString());
processTypeFactory.getProcessType(flowStep.getType()).complete(flow, flowStep, dto);
flowService.updateById(flow);
}
return Result.success();
}
/**
* 返回最后一个审批节点
* @param flowId 流程ID
* @return
*/
public FlowStep getLastApprovalFlowStep(String flowId) {
LambdaQueryWrapper<FlowStep> qw = new LambdaQueryWrapper<>();
qw.eq(FlowStep::getFlowId, flowId).eq(FlowStep::getType, FlowConstant.PROCESS_TYPE_APPROVAL).orderByDesc(FlowStep::getCreatedAt);
qw.last("limit 1");
return getOne(qw);
}
/**
* 返回所有的步骤
* @param flowId
* @return
*/
public List<FlowStep> getFlowStepListByFlowId(String flowId) {
LambdaQueryWrapper<FlowStep> qw = new LambdaQueryWrapper<>();
qw.eq(FlowStep::getFlowId, flowId).orderByAsc(FlowStep::getCreatedAt);
return list(qw);
}
}

View File

@ -0,0 +1,7 @@
package com.ydool.oa.formily.constant;
public interface GrapeConst {
String API = "/api";
}

View File

@ -0,0 +1,107 @@
package com.ydool.oa.formily.controller;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.ydool.common.data.dto.Result;
import com.ydool.common.utils.UploadUtil;
import com.ydool.oa.flow.engine.FlowProcess;
import com.ydool.oa.flow.service.FlowService;
import com.ydool.oa.formily.constant.GrapeConst;
import com.ydool.oa.formily.data.dto.FormilyDto;
import com.ydool.oa.formily.data.entity.Formily;
import com.ydool.oa.formily.data.vo.FormilyStartVo;
import com.ydool.oa.formily.service.FormilyService;
import com.ydool.oa.formily.userdept.IUserDeptService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.regex.Pattern;
@RestController
@RequestMapping(value = GrapeConst.API + "/formily")
@Api(tags = "表单", value = "表单")
public class FormilyController {
@Resource
private FormilyService formilyService;
@Resource
private FlowService flowService;
@Resource
private IUserDeptService userDeptService;
@PostMapping
@ApiOperation("新增表单")
public Result save(@Validated @RequestBody FormilyDto dto){
Formily formily = new Formily();
BeanUtil.copyProperties(dto,formily);
boolean flag = formilyService.save(formily);
return flag ? Result.success() : Result.fail("操作失败");
}
@PostMapping("update")
@ApiOperation("修改表单")
public Result update(@Validated @RequestBody FormilyDto dto){
Formily formily = formilyService.get(dto.getId());
BeanUtil.copyProperties(dto,formily);
boolean flag = formilyService.updateById(formily);
return flag ? Result.success() : Result.fail("操作失败");
}
@GetMapping("{id}")
@ApiOperation( "表单详情")
public Result show(@PathVariable String id){
Formily formily = formilyService.get(id);
if(formily == null) return Result.fail("表单不存在");
return Result.data(formily);
}
@GetMapping("{id}/items")
@ApiOperation( "获取流程表单")
public Result<FormilyStartVo> items(@PathVariable String id) {
Formily formily = formilyService.get(id);
if(formily == null) return Result.fail("表单不存在");
JSONArray items = JSONUtil.parseArray(formily.getItems());
FlowProcess root = JSONUtil.toBean(formily.getProcess(), FlowProcess.class);
JSONArray formPerms = root.getProps().getFormPerms();
FormilyStartVo vo = new FormilyStartVo(items, formPerms);
return Result.data(vo);
}
@DeleteMapping("{id}")
@ApiOperation("删除表单")
public Result delete(@PathVariable String id){
boolean flag = formilyService.removeById(id);
return flag ? Result.success() : Result.fail("操作失败");
}
@GetMapping("menu_list")
public Result getMenuList() {
return Result.data(userDeptService.getUserMenuList());
}
@PostMapping("upload")
@ApiOperation("上传文件")
public Result upload(@NotNull(message = "请选择需要上传的文件") MultipartFile file) {
String reg = ".+(.exe|.bat|.sh|.java|.php|.js|.py)$";
String suffix = FileUtil.getSuffix(file.getOriginalFilename());
if (Pattern.matches(reg, file.getOriginalFilename())) {
return Result.fail("不能上传后缀为" + suffix + "的文件");
}
String path = UploadUtil.uploadFile(file);
Dict data = Dict.create().set("path", path).set("preview", path).set("name", file.getOriginalFilename());
return (StrUtil.isNotBlank(path) ? Result.data(data) : Result.fail("上传失败"));
}
}

View File

@ -0,0 +1,77 @@
package com.ydool.oa.formily.controller;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ydool.common.data.dto.Result;
import com.ydool.oa.formily.constant.GrapeConst;
import com.ydool.oa.formily.data.dto.GroupDto;
import com.ydool.oa.formily.data.entity.Group;
import com.ydool.oa.formily.data.vo.FormilyVo;
import com.ydool.oa.formily.data.vo.GroupVo;
import com.ydool.oa.formily.service.FormilyService;
import com.ydool.oa.formily.service.GroupService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping(value = GrapeConst.API + "/group")
@Api(tags = "应用分组", value = "应用分组")
public class GroupController {
@Resource
private GroupService groupService;
@Resource
private FormilyService formilyService;
@PostMapping
@ApiOperation("新增应用分组")
public Result save(@Validated @RequestBody GroupDto dto){
Group group = new Group();
BeanUtil.copyProperties(dto,group);
boolean flag = groupService.save(group);
return flag ? Result.success() : Result.fail("操作失败");
}
@GetMapping
@ApiOperation("应用分组列表")
public Result list() {
List<Group> list = groupService.list(new LambdaQueryWrapper<>(Group.class).orderByAsc(Group::getSortNo));
List<GroupVo> groupList = new ArrayList<>();
for(Group group : list) {
GroupVo vo = new GroupVo(group);
vo.setFormilies(formilyService.getFormilyByGroup(vo.getId()).stream().map(formily -> new FormilyVo(formily)).collect(Collectors.toList()));
groupList.add(vo);
}
return Result.data(groupList);
}
@PostMapping(value = "update_position")
@ApiOperation("更新应用分组顺序")
public Result updatePosition(@RequestBody JSONObject body) {
JSONArray ids = body.getJSONArray("ids");
for(int i = 0; i < ids.size(); i++) {
Object id = ids.get(i);
LambdaUpdateWrapper<Group> uw = new LambdaUpdateWrapper();
uw.set(Group::getSortNo, i + 1).eq(Group::getId, id.toString());
groupService.update(uw);
}
return Result.success();
}
}

View File

@ -0,0 +1,35 @@
package com.ydool.oa.formily.controller;
import com.ydool.common.data.dto.Result;
import com.ydool.oa.formily.constant.GrapeConst;
import com.ydool.oa.formily.userdept.IUserDeptService;
import com.ydool.oa.formily.userdept.UserDept;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping(value = GrapeConst.API + "/tree")
@Api(tags = "部门/用户", value = "部门/用户")
public class TreeController {
@Resource
private IUserDeptService userDeptService;
/**
* 获取部门或者用户
* @param type user or depot
* @return
*/
@GetMapping
public Result getList(String type,String deptId) {
List<UserDept> result = userDeptService.getTree(type, deptId);
return Result.data(result);
}
}

View File

@ -0,0 +1,9 @@
package com.ydool.oa.formily.data.dto;
import com.ydool.oa.formily.data.entity.Formily;
import lombok.Data;
@Data
public class FormilyDto extends Formily {
}

View File

@ -0,0 +1,10 @@
package com.ydool.oa.formily.data.dto;
import com.ydool.oa.formily.data.entity.Group;
import lombok.Data;
@Data
public class GroupDto extends Group {
}

View File

@ -0,0 +1,32 @@
package com.ydool.oa.formily.data.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ydool.common.base.BaseEntity;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
@TableName(value = "t_bc_formily")
public class Formily extends BaseEntity {
@NotBlank(message = "请输入表单名称")
private String name;
@NotBlank(message = "请选择应用分组")
@JsonSerialize(using = ToStringSerializer.class)
private String groupId;
private String items;
private String logo;
private String process;
private String remark;
private String settings;
}

View File

@ -0,0 +1,22 @@
package com.ydool.oa.formily.data.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ydool.common.base.BaseEntity;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
@TableName(value = "t_bc_group")
public class Group extends BaseEntity {
@NotBlank(message = "请输入应用分组名称")
private String name;
private Integer sortNo;
public Group() {
this.sortNo = 99;
}
}

View File

@ -0,0 +1,17 @@
package com.ydool.oa.formily.data.vo;
import cn.hutool.json.JSONArray;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
public class FormilyStartVo {
private JSONArray items;
private JSONArray formPerms;
}

View File

@ -0,0 +1,24 @@
package com.ydool.oa.formily.data.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ydool.oa.formily.data.entity.Formily;
import lombok.Data;
@Data
public class FormilyVo {
@JsonSerialize(using = ToStringSerializer.class)
private String id;
private String name;
private String logo;
public FormilyVo(Formily formily) {
this.id = formily.getId();
this.name = formily.getName();
this.logo = formily.getLogo();
}
}

View File

@ -0,0 +1,23 @@
package com.ydool.oa.formily.data.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ydool.oa.formily.data.entity.Group;
import lombok.Data;
import java.util.List;
@Data
public class GroupVo {
@JsonSerialize(using = ToStringSerializer.class)
private String id;
private String name;
private List<FormilyVo> formilies;
public GroupVo(Group group) {
this.id = group.getId();
this.name = group.getName();
}
}

View File

@ -0,0 +1,9 @@
package com.ydool.oa.formily.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ydool.oa.formily.data.entity.Formily;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface FormilyMapper extends BaseMapper<Formily> {
}

View File

@ -0,0 +1,10 @@
package com.ydool.oa.formily.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ydool.oa.formily.data.entity.Group;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface GroupMapper extends BaseMapper<Group> {
}

View File

@ -0,0 +1,19 @@
package com.ydool.oa.formily.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ydool.common.base.BaseService;
import com.ydool.oa.formily.data.entity.Formily;
import com.ydool.oa.formily.mapper.FormilyMapper;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class FormilyService extends BaseService<FormilyMapper, Formily> {
public List<Formily> getFormilyByGroup(String groupId) {
return list(new LambdaQueryWrapper<>(Formily.class).eq(Formily::getGroupId, groupId));
}
}

View File

@ -0,0 +1,11 @@
package com.ydool.oa.formily.service;
import com.ydool.common.base.BaseService;
import com.ydool.oa.formily.data.entity.Group;
import com.ydool.oa.formily.mapper.GroupMapper;
import org.springframework.stereotype.Service;
@Service
public class GroupService extends BaseService<GroupMapper, Group> {
}

View File

@ -0,0 +1,60 @@
package com.ydool.oa.formily.userdept;
import java.util.List;
public interface IUserDeptService {
/**
* 返回用户部门列表
* @param type user or dept
* @param deptId 部门ID
* @return
*/
List<UserDept> getTree(String type, String deptId);
/**
* 根据用户ID 返回用户所在部门的领导
* @param userId
* @return
*/
UserDept getUserDeptLeader(String userId);
/**
* 根据部门ID返回该部门负责人
* @param deptIds
* @return
*/
List<UserDept> getUserListByDeptId(List<String> deptIds) ;
/**
* 根据部门ID返回该部门负责人
* @param userIds
* @return
*/
List<UserDept> getUserListByUserId(List<String> userIds);
/**
* 当审批人员为空时转交给管理员审批
* @return
*/
UserDept getAdminUserDepot();
/**
* 根据ID 返回用户
* @param userId
* @return
*/
UserDept getUserById(String userId);
/**
* 返回当前登录的用户
* @return
*/
UserDept getCurrentUser();
/**
* 返回当前登录用户的菜单列表
* @return
*/
Object getUserMenuList();
}

View File

@ -0,0 +1,98 @@
package com.ydool.oa.formily.userdept;
public class UserDept {
/**
* 主键
*/
private String id;
/**
* 姓名
*/
private String name;
/**
* 类型 user or dept
*/
private String type;
/**
* 头像
*/
private String avatar;
/**
* 是否领导
*/
private Boolean isLeader;
/**
* 选中状态
*/
private Boolean selected;
public UserDept(String id, String name, String type) {
this.id = id;
this.name = name;
this.type = type;
}
public UserDept(String id, String name, String type, String avatar, Boolean isLeader) {
this.id = id;
this.name = name;
this.type = type;
this.avatar = avatar;
this.isLeader = isLeader;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public Boolean getLeader() {
return isLeader;
}
public void setLeader(Boolean leader) {
isLeader = leader;
}
public Boolean getSelected() {
return selected;
}
public void setSelected(Boolean selected) {
this.selected = selected;
}
}

View File

@ -0,0 +1,200 @@
package com.ydool.oa.formily.userdept.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ydool.common.cache.ConfigCache;
import com.ydool.oa.formily.userdept.IUserDeptService;
import com.ydool.oa.formily.userdept.UserDept;
import com.ydool.system.entity.Dept;
import com.ydool.system.entity.Job;
import com.ydool.system.entity.User;
import com.ydool.system.service.impl.DeptServiceImpl;
import com.ydool.system.service.impl.JobServiceImpl;
import com.ydool.system.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserDeptServiceImpl implements IUserDeptService {
@Autowired
private UserServiceImpl userService;
@Autowired
private JobServiceImpl jobService;
@Autowired
private DeptServiceImpl deptService;
@Override
public List<UserDept> getTree(String type, String deptId) {
List<UserDept> result = new ArrayList<UserDept>();
List<Dept> depts = deptService.list(new QueryWrapper<Dept>().lambda()
.eq(Dept::getPid, StrUtil.isNotBlank(deptId) ? deptId : "0")
);
if (CollUtil.isNotEmpty(depts)) {
depts.forEach(dept -> {
result.add(new UserDept(dept.getId(), dept.getName(), "dept"));
});
}
if ("user".equals(type) && StrUtil.isNotBlank(deptId)) {
List<User> users = userService.list(new QueryWrapper<User>().lambda()
.eq(User::getDept, deptId)
.eq(User::getStatus, true)
);
if (CollUtil.isNotEmpty(users)) {
users.forEach(user -> {
result.add(new UserDept(user.getId(), user.getUserName(), "user", "", isLeader(user.getJob())));
});
}
}
return result;
}
@Override
public UserDept getUserDeptLeader(String userId) {
if (StrUtil.isBlank(userId)) return null;
User user = userService.getOne(new QueryWrapper<User>().lambda()
.eq(User::getId, userId)
.eq(User::getStatus, true)
.last("limit 1")
);
if (ObjectUtil.isNull(user)) return null;
Boolean isLeader = isLeader(user.getJob());
if (isLeader) {
return new UserDept(user.getId(), user.getUserName(), "user", "", isLeader(user.getJob()));
}
String leaderJobId = getLeaderJobId();
User leaderUser = userService.getOne(new QueryWrapper<User>().lambda()
.eq(User::getDept, user.getDept())
.eq(User::getJob, leaderJobId)
.eq(User::getStatus, true)
.last("limit 1")
);
if (ObjectUtil.isNull(leaderUser)) return null;
return new UserDept(leaderUser.getId(), leaderUser.getUserName(), "user", "", isLeader(user.getJob()));
}
@Override
public List<UserDept> getUserListByDeptId(List<String> deptIds) {
if (CollUtil.isEmpty(deptIds)) return null;
List<User> userList = userService.list(new QueryWrapper<User>().lambda()
.in(User::getDept, deptIds)
.eq(User::getJob, getLeaderJobId())
.eq(User::getStatus, true)
);
List<UserDept> userDeptList = new ArrayList<UserDept>();
if (CollUtil.isNotEmpty(userList)) {
userList.forEach(user -> {
userDeptList.add(new UserDept(user.getId(), user.getUserName(), "user", "", isLeader(user.getJob())));
});
}
return userDeptList;
}
@Override
public List<UserDept> getUserListByUserId(List<String> userIds) {
if (CollUtil.isEmpty(userIds)) return null;
List<User> userList = userService.list(new QueryWrapper<User>().lambda()
.in(User::getId, userIds)
.eq(User::getStatus, true)
);
if (CollUtil.isEmpty(userList)) return null;
List<String> deptIds = userList.stream().map(User::getDept).collect(Collectors.toList());
return getUserListByDeptId(deptIds);
}
@Override
public UserDept getAdminUserDepot() {
User user = userService.getOne(new QueryWrapper<User>().lambda()
.eq(User::getLoginName, ConfigCache.getStr(ConfigCache.USER_ADMIN, "admin"))
.eq(User::getStatus, true)
.last("limit 1")
);
if (ObjectUtil.isNull(user)) return null;
return new UserDept(user.getId(), user.getUserName(), "user", "", isLeader(user.getJob()));
}
@Override
public UserDept getUserById(String userId) {
User user = userService.getOne(new QueryWrapper<User>().lambda()
.eq(User::getId, userId)
.eq(User::getStatus, true)
.last("limit 1")
);
if (ObjectUtil.isNull(user)) return null;
return new UserDept(user.getId(), user.getUserName(), "user", "", isLeader(user.getJob()));
}
@Override
public UserDept getCurrentUser() {
User user = userService.getOne(new QueryWrapper<User>().lambda()
.eq(User::getId, StpUtil.getLoginIdAsString())
.eq(User::getStatus, true)
.last("limit 1")
);
if (ObjectUtil.isNull(user)) return null;
return new UserDept(user.getId(), user.getUserName(), "user", "", isLeader(user.getJob()));
}
@Override
public Object getUserMenuList() {
return userService.menuTreesByUserId(StpUtil.getLoginIdAsString());
}
private Boolean isLeader(String jobId) {
Job job = jobService.getOne(new QueryWrapper<Job>().lambda()
.eq(Job::getId, jobId)
.eq(Job::getStatus, true)
.last("limit 1")
);
boolean isLeader = false;
if (ObjectUtil.isNotNull(job)) {
if ("负责人".equals(job.getName())) {
isLeader = true;
}
}
return isLeader;
}
private String getLeaderJobId() {
Job job = jobService.getOne(new QueryWrapper<Job>().lambda()
.eq(Job::getName, "负责人")
.eq(Job::getStatus, true)
.last("limit 1")
);
String jobId = "";
if (ObjectUtil.isNotNull(job)) {
jobId = job.getId();
}
return jobId;
}
}

View File

@ -11,7 +11,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
@MapperScan("com.ydool.**.mapper.**") @MapperScan({"com.ydool.**.mapper.**","com.ydool.oa.**.mapper.**"})
public class MybatisPlusConfig { public class MybatisPlusConfig {
/** /**

View File

@ -59,6 +59,38 @@ public class SwaggerConfig {
return docket; return docket;
} }
@Bean(value = "defaultApi3")
public Docket defaultApi3() {
String groupName="OA-flow";
Docket docket=new Docket(DocumentationType.SWAGGER_2)
.host("http://www.ydool.com/")
.apiInfo(apiInfo())
.groupName(groupName)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ydool.oa.flow.controller"))
.paths(PathSelectors.any())
.build()
//赋予插件体系
.extensions(openApiExtensionResolver.buildExtensions(groupName));
return docket;
}
@Bean(value = "defaultApi4")
public Docket defaultApi4() {
String groupName="OA-formily";
Docket docket=new Docket(DocumentationType.SWAGGER_2)
.host("http://www.ydool.com/")
.apiInfo(apiInfo())
.groupName(groupName)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ydool.oa.formily.controller"))
.paths(PathSelectors.any())
.build()
//赋予插件体系
.extensions(openApiExtensionResolver.buildExtensions(groupName));
return docket;
}
private ApiInfo apiInfo() { private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("接口文档系统") return new ApiInfoBuilder().title("接口文档系统")
.description("接口文档系统") .description("接口文档系统")

View File

@ -112,6 +112,13 @@ public interface IUserService {
*/ */
List<Menu> menuTreesByUser(String id); List<Menu> menuTreesByUser(String id);
/**
* 获取菜单树
* @param id
* @return
*/
List<Menu> menuTreesByUserId(String id);
/** /**
* 获取菜单 * 获取菜单
* @param id * @param id

View File

@ -315,6 +315,71 @@ public class UserServiceImpl extends BaseService<UserMapper, User> implements IU
return null; return null;
} }
/**
* 获取菜单树
*
* @return
*/
@Override
public List<Menu> menuTreesByUserId(String id) {
List<Role> roles = roleListByUser(id);
if (CollUtil.isNotEmpty(roles)) {
String menus = roles.stream().map(Role::getMenus).collect(Collectors.joining(","));
List<Map<String, Object>> menuMap = menuService.listMaps(new QueryWrapper<Menu>().lambda()
.in(Menu::getId, Arrays.asList(menus.split(
",")))
.eq(Menu::getStatus, true)
.eq(Menu::getVisible, false)
.ne(Menu::getMenuType, "F")
.orderByAsc(Menu::getSortNo)
);
//树结构下搜索,如果子级符合条件,就把父级也放进去,确保其能显示
Iterator<Map<String, Object>> iterator = menuMap.iterator();
List<Map<String, Object>> parentList = new ArrayList<>();
QueryWrapper<Menu> wrapper = new QueryWrapper<Menu>();
wrapper.lambda().eq(Menu::getStatus, true)
.eq(Menu::getVisible, false).orderByAsc(Menu::getSortNo);
//获取所有菜单
List<Map<String, Object>> allMenuMap = menuService.listMaps(wrapper);
Map<String, Object> allMap = new HashMap<String, Object>();
allMenuMap.forEach(item -> {
HashMap map = (HashMap) item;
allMap.put(map.get("id").toString(), map);
});
while (iterator.hasNext()) {
Map map = (HashMap) iterator.next();
String pid = map.get("pid").toString();
if (!"0".equals(pid)) {
for (; ; ) {
Map pMap = (HashMap) allMap.get(pid);
if (pMap != null && !menuMap.contains(pMap) && !parentList.contains(pMap)) {
parentList.add(pMap);
pid = pMap.get("pid").toString();
} else {
break;
}
}
}
}
menuMap.addAll(parentList);
TreeMapUtil treeMapUtil = new TreeMapUtil();
treeMapUtil.tree(menuMap);
//转换为集合对象
JSONArray jsonArray = new JSONArray();
jsonArray.addAll(menuMap);
List<Menu> menuList = jsonArray.toList(Menu.class);
return menuList;
}
return null;
}
/** /**
* 获取菜单 * 获取菜单
* *

View File

@ -43,6 +43,8 @@ spring:
use-global-data-source-stat: true use-global-data-source-stat: true
# 通过connectProperties属性来打开mergeSql功能慢SQL记录 # 通过connectProperties属性来打开mergeSql功能慢SQL记录
connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
hikari:
auto-commit: true
# 配置监控服务器 # 配置监控服务器
stat-view-servlet: stat-view-servlet:
login-username: admin login-username: admin
@ -62,6 +64,10 @@ spring:
thymeleaf: thymeleaf:
prefix: classpath:/html/ prefix: classpath:/html/
suffix: .html suffix: .html
jackson:
default-property-inclusion: non_null
main:
allow-circular-references: true
mybatis-plus: mybatis-plus:
global-config: global-config: