OA板块整合与实现

This commit is contained in:
lijiaqi 2023-03-06 16:19:12 +08:00
parent 5c6d551d8a
commit a9254f42e9
22 changed files with 368 additions and 20 deletions

View File

@ -52,6 +52,7 @@ public interface FlowConstant {
*/
String PROCESS_TYPE_CONDITIONS = "CONDITIONS";
/**
* 延迟等待
*/
@ -113,6 +114,25 @@ public interface FlowConstant {
*/
String MODE_OR = "OR";
/**
* 分支条件组外关系(满足其中一组即可)
*/
String FLOW_TYPE_GROUPS_OR = "OR";
/**
* 分支条件组外关系(必须满足所有条件组)
*/
String FLOW_TYPE_GROUPS_AND = "AND";
/**
* 分支条件组内关系(满足组内其中一个条件即可)
*/
String FLOW_TYPE_GROUP_OR = "OR";
/**
* 分支条件组内关系(必须满足所有组内条件)
*/
String FLOW_TYPE_GROUP_AND = "AND";
/**
* 自动通过
*/
@ -147,4 +167,30 @@ public interface FlowConstant {
* 驳回到指定节点
*/
String REFUSE_TYPE_TO_NODE = "TO_NODE";
/**
* 发起人
*/
String VALUE_TYPE_ORIGINATOR = "Originator";
/**
* 选择用户
*/
String VALUE_TYPE_USER = "User";
/**
* 选择部门
*/
String VALUE_TYPE_DEPT = "Dept";
/**
* 字符类型
*/
String VALUE_TYPE_STRING = "String";
/**
* 时间类型
*/
String VALUE_TYPE_DATE = "Date";
/**
* 数字类型
*/
String VALUE_TYPE_NUMBER = "Number";
}

View File

@ -2,6 +2,8 @@ package com.ydool.oa.flow.engine;
import lombok.Data;
import java.util.List;
@Data
public class FlowProcess {
@ -17,6 +19,8 @@ public class FlowProcess {
private ProcessProps props;
private List<FlowProcess> branchs;
private FlowProcess children;
}

View File

@ -0,0 +1,18 @@
package com.ydool.oa.flow.engine;
import cn.hutool.json.JSONArray;
import lombok.Data;
@Data
public class ProcessCondition {
private String title;
private String id;
private String valueType;
private String compare;
private JSONArray value;
}

View File

@ -0,0 +1,16 @@
package com.ydool.oa.flow.engine;
import cn.hutool.json.JSONArray;
import lombok.Data;
import java.util.List;
@Data
public class ProcessGroup {
private String groupType;
private JSONArray cids;
private List<ProcessCondition> conditions;
}

View File

@ -31,4 +31,11 @@ public class ProcessProps {
private List<UserDept> assignedUser;
private List<UserDept> assignedDept;
private String groupsType;
private List<ProcessGroup> groups;
private String expression;
}

View File

@ -19,7 +19,6 @@ 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;
@ -33,8 +32,6 @@ public class ProcessApproval implements IProcessType {
@Resource
private IUserDeptService userDeptService;
@Resource
private FlowService flowService;
@Resource
private FlowStepService flowStepService;
@ -58,7 +55,7 @@ public class ProcessApproval implements IProcessType {
flowStep.setNextStep(JSONUtil.toJsonStr(process.getChildren()));
flowStep.setFormPerms(process.getProps().getFormPerms().toString());
switch (props.getAssignedType()){
case FlowConstant.ASSIGNED_TYPE_ASSIGN_USER: { // 指定用户
case FlowConstant.ASSIGNED_TYPE_ASSIGN_USER: { // 指定用户
if(props.getAssignedUser().size() == 0) {
this.doNoBody(flowStep, props.getNobody());
} else {
@ -82,10 +79,10 @@ public class ProcessApproval implements IProcessType {
break;
}
case FlowConstant.ASSIGNED_TYPE_FORM_USER: { // 表单内联系人
break;
}
case FlowConstant.ASSIGNED_TYPE_FORM_DEPT: { // 表单内部门
break;
}
}
@ -171,10 +168,10 @@ public class ProcessApproval implements IProcessType {
flowStep.setStatus(FlowConstant.STEP_STATUS_PASS);
flowStepService.updateById(flowStep);
FlowProcess nextFlow = JSONUtil.toBean(flowStep.getNextStep(), FlowProcess.class);
if(StrUtil.isEmpty(nextFlow.getId())) {
if(StrUtil.isBlank(nextFlow.getId())) {
flow.setStatus(FlowConstant.FLOW_STATUS_DONE);
}else {
flow.setStep(flow.getStep()+1);
if (FlowConstant.PROCESS_TYPE_APPROVAL.equals(nextFlow.getType())) {flow.setStep(flow.getStep() + 1);}
flow.setStepName(nextFlow.getName());
flowStepService.createFlowStep(flow, nextFlow);
}

View File

@ -0,0 +1,260 @@
package com.ydool.oa.flow.engine.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ydool.common.base.TreeEntity;
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.ProcessCondition;
import com.ydool.oa.flow.engine.ProcessGroup;
import com.ydool.oa.flow.engine.ProcessProps;
import com.ydool.oa.flow.engine.service.IProcessType;
import com.ydool.oa.flow.service.FlowStepService;
import com.ydool.system.entity.Dept;
import com.ydool.system.service.impl.DeptServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class ProcessConditions implements IProcessType {
@Resource
private FlowStepService flowStepService;
@Resource
private DeptServiceImpl deptService;
@Override
public void execute(Flow flow, FlowProcess process) {
//分支条件集合
List<FlowProcess> branchs = process.getBranchs();
if (CollUtil.isNotEmpty(branchs)) {
executeBranchs(flow, branchs);
}
}
@Override
public void complete(Flow flow, FlowStep flowStep, FlowStepDto dto) {
}
@Override
public String getName() {
return FlowConstant.PROCESS_TYPE_CONDITIONS;
}
public void executeBranchs(Flow flow, List<FlowProcess> branchs) {
boolean flag = false;
for (FlowProcess branch : branchs) {//分支条件
//分支条件具体内容
ProcessProps props = branch.getProps();
//下一流程
FlowProcess nextFlow = branch.getChildren();
if (ObjUtil.isNotNull(props)) {
//条件组集合判断关系
String groupsType = props.getGroupsType();
// 条件组集合
List<ProcessGroup> groups = props.getGroups();
if (CollUtil.isNotEmpty(groups)) {
flag = executeGroups(flow, groupsType, groups);
if (flag) { //满足条件 创造下一个节点
doCreateNextFlowStep(flow, nextFlow);
break;
}
}
}
}
if (!flag) { //没有一个分支条件满足
FlowStep flowStep = flowStepService.getLastApprovalFlowStep(flow.getId());
doRefuse(flow, flowStep);
}
}
public boolean executeGroups(Flow flow, String groupsType, List<ProcessGroup> groups) {
boolean flag = false;
for (ProcessGroup group : groups) {
//条件组内关系
String groupType = group.getGroupType();
//获取条件集合
List<ProcessCondition> conditions = group.getConditions();
if (CollUtil.isNotEmpty(conditions)) {
//其中一个条件组满足
if (FlowConstant.FLOW_TYPE_GROUPS_OR.equals(groupsType)) {
flag = executeGroup(flow, groupType, conditions);
if (flag) break;
}
//所有条件组都满足
if (FlowConstant.FLOW_TYPE_GROUPS_AND.equals(groupsType)) {
flag = executeGroup(flow, groupType, conditions);
if (!flag) break;
}
}
}
return flag;
}
public boolean executeGroup(Flow flow, String groupType, List<ProcessCondition> conditions) {
boolean flag = false;
for (ProcessCondition condition : conditions) {
//其中一个条件满足
if (FlowConstant.FLOW_TYPE_GROUP_OR.equals(groupType)) {
flag = executeCondition(flow, condition);
if (flag) break;
}
//所有条件都满足
if (FlowConstant.FLOW_TYPE_GROUP_AND.equals(groupType)) {
flag = executeCondition(flow, condition);
if (!flag) break;
}
}
return flag;
}
public boolean executeCondition(Flow flow, ProcessCondition condition) {
boolean flag = false;
switch (condition.getValueType()) {
case FlowConstant.VALUE_TYPE_ORIGINATOR:
flag = executeOriginator(flow, condition);
break;
case FlowConstant.VALUE_TYPE_USER:
flag = executeUser(flow, condition);
break;
case FlowConstant.VALUE_TYPE_DEPT:
flag = executeDept(flow, condition);
break;
case FlowConstant.VALUE_TYPE_STRING:
flag = executeString(flow, condition);
break;
case FlowConstant.VALUE_TYPE_DATE:
break;
case FlowConstant.VALUE_TYPE_NUMBER:
break;
}
return flag;
}
public boolean executeOriginator(Flow flow, ProcessCondition condition) {
JSONArray userList = condition.getValue();
if (!userList.isEmpty()) {
for (int i = 0; i < userList.size(); i++) {
JSONObject user = userList.getJSONObject(i);
if (flow.getCreatedId().equals(user.getStr("id"))) {
return true;
}
}
}
return false;
}
public boolean executeUser(Flow flow, ProcessCondition condition) {
//表格填写数据
JSONArray formUserList = JSONUtil.parseArray(JSONUtil.parseObj(flow.getData()).getStr(condition.getId()));
JSONArray userList = condition.getValue();
if (!userList.isEmpty() && !formUserList.isEmpty()) {
for (int i = 0; i < userList.size(); i++) {
JSONObject user = userList.getJSONObject(i);
for (int j = 0; j < formUserList.size(); j++) {
JSONObject formUser = formUserList.getJSONObject(j);
if (formUser.getStr("id").equals(user.getStr("id"))) {
return true;
}
}
}
}
return false;
}
private boolean executeDept(Flow flow, ProcessCondition condition) {
//表格填写数据
JSONArray formDeptList = JSONUtil.parseArray(JSONUtil.parseObj(flow.getData()).getStr(condition.getId()));
JSONArray deptList = condition.getValue();
if (!deptList.isEmpty() && !formDeptList.isEmpty()) {
for (int i = 0; i < deptList.size(); i++) {
JSONObject dept = deptList.getJSONObject(i);
for (int j = 0; j < formDeptList.size(); j++) {
JSONObject formDept = formDeptList.getJSONObject(j);
//是否相同部门
if (formDept.getStr("id").equals(dept.getStr("id"))) {
return true;
}
//是否下级子部门
int count = deptService.count(new QueryWrapper<Dept>().lambda()
.eq(Dept::getId, formDept.getStr("id"))
.like(TreeEntity::getTreeIds, dept.getStr("id"))
);
if (count > 0) {
return true;
}
}
}
}
return false;
}
private boolean executeString(Flow flow, ProcessCondition condition) {
//表格填写数据
String formString = JSONUtil.parseObj(flow.getData()).getStr(condition.getId());
JSONArray StringList = condition.getValue();
if (!StringList.isEmpty() && StrUtil.isNotBlank(formString)) {
if ("=".equals(condition.getCompare()) && StringList.getStr(0).equals(formString)){
return true;
}
}
// todo 包含关系待定
return false;
}
public void doCreateNextFlowStep(Flow flow, FlowProcess nextFlow) {
if (ObjUtil.isNotNull(nextFlow) && StrUtil.isNotBlank(nextFlow.getId())) {
if (FlowConstant.PROCESS_TYPE_APPROVAL.equals(nextFlow.getType())) {
flow.setStep(flow.getStep() + 1);
}
flow.setStepName(nextFlow.getName());
flowStepService.createFlowStep(flow, nextFlow);
} else {
// todo 找出当前条件节点的下一步 暂时无法实现
}
}
public void doRefuse(Flow flow, FlowStep flowStep) {
if (ObjUtil.isNotNull(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);
}
}
}

View File

@ -107,7 +107,7 @@ sa-token:
# token有效期单位s 默认30天, -1代表永不过期
timeout: 864000
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
activity-timeout: 1800
activity-timeout: 864000
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
.design i[data-v-3fb380a7]{padding:10px;font-size:xx-large;background:#fff;border:1px dashed #8c8c8c}[data-v-3fb380a7] .el-upload--picture-card{width:80px;height:80px;line-height:87px}[data-v-3fb380a7] .el-upload-list__item{width:80px;height:80px}[data-v-3fb380a7] .el-upload-list__item .el-upload-list__item-actions>span+span{margin:1px}.fileList[data-v-3fb380a7]{display:flex}.fileList .fileList_item[data-v-3fb380a7]{width:80px;height:80px;margin-right:10px;margin-bottom:10px;cursor:pointer;border:1px solid #e5e7ec;border-radius:4px;padding:2px}.fileList .fileList_item img[data-v-3fb380a7]{width:100%}

View File

@ -0,0 +1 @@
.design i[data-v-150590c9]{padding:10px;font-size:xx-large;background:#fff;border:1px dashed #8c8c8c}[data-v-150590c9] .el-upload--picture-card{width:80px;height:80px;line-height:87px}[data-v-150590c9] .el-upload-list__item{width:80px;height:80px}[data-v-150590c9] .el-upload-list__item .el-upload-list__item-actions>span+span{margin:1px}.fileList[data-v-150590c9]{display:flex}.fileList .fileList_item[data-v-150590c9]{width:80px;height:80px;margin-right:10px;margin-bottom:10px;cursor:pointer;border:1px solid #e5e7ec;border-radius:4px;padding:2px}.fileList .fileList_item img[data-v-150590c9]{width:100%}

View File

@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-308b5d9e"],{6265:function(e,t,i){"use strict";i("87cf")},"87cf":function(e,t,i){},b435:function(e,t,i){"use strict";i.r(t);var a=function(){var e=this,t=e._self._c;return"R"==e.perm?t("div",{staticClass:"fileList"},e._l(e.fileList,(function(e,i){return t("div",{key:i,staticClass:"fileList_item"},[t("el-image",{staticStyle:{width:"100%",height:"100%"},attrs:{src:e.url,"preview-src-list":[e.url]}})],1)})),0):t("div",[t("el-upload",{attrs:{"file-list":e.fileList,action:"/api/formily/upload",limit:e.maxSize,"with-credentials":"",multiple:e.maxSize>0,data:e.uploadParams,"list-type":"picture-card","before-upload":e.beforeUpload,"on-success":e.onSuccess},scopedSlots:e._u([{key:"file",fn:function({file:i}){return t("div",{},[t("img",{staticClass:"el-upload-list__item-thumbnail",attrs:{src:i.url,alt:""}}),t("span",{staticClass:"el-upload-list__item-actions"},[t("span",{staticClass:"el-upload-list__item-preview",on:{click:function(t){return e.handlePictureCardPreview(i)}}},[t("i",{staticClass:"el-icon-zoom-in"})]),e.disabled?e._e():t("span",{staticClass:"el-upload-list__item-delete",on:{click:function(t){return e.handleDownload(i)}}},[t("i",{staticClass:"el-icon-download"})]),"R"!==e.perm?t("span",{staticClass:"el-upload-list__item-delete",on:{click:function(t){return e.handleRemove(i)}}},[t("i",{staticClass:"el-icon-delete"})]):e._e()])])}}])},[t("i",{staticClass:"el-icon-plus",attrs:{slot:"default"},slot:"default"}),"R"!==e.perm?t("div",{staticClass:"el-upload__tip",attrs:{slot:"tip"},slot:"tip"},[e._v(" "+e._s(e.placeholder)+" "+e._s(e.sizeTip)+" ")]):e._e()])],1)},s=[],l=(i("14d9"),i("ba89")),n={mixins:[l["a"]],name:"ImageUpload",components:{},props:{value:{type:Array,default:()=>[]},placeholder:{type:String,default:"请选择图片"},maxSize:{type:Number,default:5},maxNumber:{type:Number,default:10},enableZip:{type:Boolean,default:!0},perm:{type:String,default:null}},watch:{isShow(){this.fileList=JSON.parse(JSON.stringify(this.value))}},computed:{sizeTip(){return this.maxSize>0?`| 每张图不超过${this.maxSize}MB`:""},isShow(){return this._value.length}},data(){return{disabled:!1,uploadParams:{},fileList:[]}},created(){this.fileList=JSON.parse(JSON.stringify(this.value))},methods:{beforeUpload(e){console.log(e);const t=["image/jpeg","image/png","image/gif","image/jpg"];if(-1===t.indexOf(e.type))this.$message.warning("存在不支持的图片格式");else{if(!(this.maxSize>0&&e.size/1024/1024>this.maxSize))return!0;this.$message.warning(`单张图片最大不超过 ${this.maxSize}MB`)}return!1},handleRemove(e){let t=e.path;this.value.forEach((e,i)=>{e.path===t&&this.value.splice(i,1)})},handlePictureCardPreview(e){console.log(e)},handleDownload(e){console.log(e)},onSuccess(e){this._value.push({...e.data,url:e.data.preview}),this.$set(this,"_value",this.value)}}},r=n,o=(i("6265"),i("2877")),u=Object(o["a"])(r,a,s,!1,null,"3fb380a7",null);t["default"]=u.exports},ba89:function(e,t,i){"use strict";t["a"]={props:{mode:{type:String,default:"DESIGN"},required:{type:Boolean,default:!1}},data(){return{}},computed:{_value:{get(){return this.value},set(e){this.$emit("input",e)}}}}}}]);

View File

@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-4a561d9f"],{"7bd5":function(e,t,i){"use strict";i("990a")},"990a":function(e,t,i){},b435:function(e,t,i){"use strict";i.r(t);var a=function(){var e=this,t=e._self._c;return"R"==e.perm?t("div",{staticClass:"fileList"},e._l(e.fileList,(function(e,i){return t("div",{key:i,staticClass:"fileList_item"},[t("el-image",{staticStyle:{width:"100%",height:"100%"},attrs:{src:e.url,"preview-src-list":[e.url]}})],1)})),0):t("div",[t("el-upload",{attrs:{"file-list":e.fileList,action:"/api/formily/upload",limit:e.maxSize,"with-credentials":"",multiple:e.maxSize>0,data:e.uploadParams,"list-type":"picture-card","before-upload":e.beforeUpload,"on-success":e.onSuccess,headers:e.headers},scopedSlots:e._u([{key:"file",fn:function({file:i}){return t("div",{},[t("img",{staticClass:"el-upload-list__item-thumbnail",attrs:{src:i.url,alt:""}}),t("span",{staticClass:"el-upload-list__item-actions"},[t("span",{staticClass:"el-upload-list__item-preview",on:{click:function(t){return e.handlePictureCardPreview(i)}}},[t("i",{staticClass:"el-icon-zoom-in"})]),e.disabled?e._e():t("span",{staticClass:"el-upload-list__item-delete",on:{click:function(t){return e.handleDownload(i)}}},[t("i",{staticClass:"el-icon-download"})]),"R"!==e.perm?t("span",{staticClass:"el-upload-list__item-delete",on:{click:function(t){return e.handleRemove(i)}}},[t("i",{staticClass:"el-icon-delete"})]):e._e()])])}}])},[t("i",{staticClass:"el-icon-plus",attrs:{slot:"default"},slot:"default"}),"R"!==e.perm?t("div",{staticClass:"el-upload__tip",attrs:{slot:"tip"},slot:"tip"},[e._v(" "+e._s(e.placeholder)+" "+e._s(e.sizeTip)+" ")]):e._e()])],1)},s=[],l=(i("14d9"),i("ba89")),n=i("403a"),r={mixins:[l["a"]],name:"ImageUpload",components:{},props:{value:{type:Array,default:()=>[]},placeholder:{type:String,default:"请选择图片"},maxSize:{type:Number,default:5},maxNumber:{type:Number,default:10},enableZip:{type:Boolean,default:!0},perm:{type:String,default:null}},watch:{isShow(){this.fileList=JSON.parse(JSON.stringify(this.value))}},computed:{sizeTip(){return this.maxSize>0?`| 每张图不超过${this.maxSize}MB`:""},isShow(){return this._value.length}},data(){return{disabled:!1,uploadParams:{},fileList:[],headers:{"x-token":Object(n["a"])("token")}}},created(){this.fileList=JSON.parse(JSON.stringify(this.value))},methods:{beforeUpload(e){console.log(e);const t=["image/jpeg","image/png","image/gif","image/jpg"];if(-1===t.indexOf(e.type))this.$message.warning("存在不支持的图片格式");else{if(!(this.maxSize>0&&e.size/1024/1024>this.maxSize))return!0;this.$message.warning(`单张图片最大不超过 ${this.maxSize}MB`)}return!1},handleRemove(e){let t=e.path;this.value.forEach((e,i)=>{e.path===t&&this.value.splice(i,1)})},handlePictureCardPreview(e){console.log(e)},handleDownload(e){console.log(e)},onSuccess(e){this._value.push({...e.data,url:e.data.preview}),this.$set(this,"_value",this.value)}}},o=r,u=(i("7bd5"),i("2877")),d=Object(u["a"])(o,a,s,!1,null,"150590c9",null);t["default"]=d.exports},ba89:function(e,t,i){"use strict";t["a"]={props:{mode:{type:String,default:"DESIGN"},required:{type:Boolean,default:!1}},data(){return{}},computed:{_value:{get(){return this.value},set(e){this.$emit("input",e)}}}}}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long