!52 工作流的部分实现

Merge pull request !52 from 芋道源码/feature/activiti
This commit is contained in:
芋道源码 2021-10-30 05:51:21 +00:00 committed by Gitee
commit e82c37f2c5
62 changed files with 3872 additions and 754 deletions

View File

@ -1,17 +1,17 @@
/*
Navicat Premium Data Transfer
Source Server : local-mysql001
Source Server : 127.0.0.1
Source Server Type : MySQL
Source Server Version : 50718
Source Server Version : 80026
Source Host : localhost:3306
Source Schema : ruoyi-vue-pro
Target Server Type : MySQL
Target Server Version : 50718
Target Server Version : 80026
File Encoding : 65001
Date: 03/05/2021 12:01:37
Date: 30/10/2021 13:46:03
*/
SET NAMES utf8mb4;
@ -29,7 +29,7 @@ CREATE TABLE `QRTZ_BLOB_TRIGGERS` (
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
KEY `SCHED_NAME` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of QRTZ_BLOB_TRIGGERS
@ -46,7 +46,7 @@ CREATE TABLE `QRTZ_CALENDARS` (
`CALENDAR_NAME` varchar(190) NOT NULL,
`CALENDAR` blob NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of QRTZ_CALENDARS
@ -66,12 +66,13 @@ CREATE TABLE `QRTZ_CRON_TRIGGERS` (
`TIME_ZONE_ID` varchar(80) DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of QRTZ_CRON_TRIGGERS
-- ----------------------------
BEGIN;
INSERT INTO `QRTZ_CRON_TRIGGERS` VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', '* * * * * ?', 'Asia/Shanghai');
INSERT INTO `QRTZ_CRON_TRIGGERS` VALUES ('schedulerName', 'sysUserSessionTimeoutJob', 'DEFAULT', '0 * * * * ? *', 'Asia/Shanghai');
COMMIT;
@ -85,9 +86,9 @@ CREATE TABLE `QRTZ_FIRED_TRIGGERS` (
`TRIGGER_NAME` varchar(190) NOT NULL,
`TRIGGER_GROUP` varchar(190) NOT NULL,
`INSTANCE_NAME` varchar(190) NOT NULL,
`FIRED_TIME` bigint(13) NOT NULL,
`SCHED_TIME` bigint(13) NOT NULL,
`PRIORITY` int(11) NOT NULL,
`FIRED_TIME` bigint NOT NULL,
`SCHED_TIME` bigint NOT NULL,
`PRIORITY` int NOT NULL,
`STATE` varchar(16) NOT NULL,
`JOB_NAME` varchar(190) DEFAULT NULL,
`JOB_GROUP` varchar(190) DEFAULT NULL,
@ -100,7 +101,7 @@ CREATE TABLE `QRTZ_FIRED_TRIGGERS` (
KEY `IDX_QRTZ_FT_JG` (`SCHED_NAME`,`JOB_GROUP`),
KEY `IDX_QRTZ_FT_T_G` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
KEY `IDX_QRTZ_FT_TG` (`SCHED_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of QRTZ_FIRED_TRIGGERS
@ -126,12 +127,13 @@ CREATE TABLE `QRTZ_JOB_DETAILS` (
PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
KEY `IDX_QRTZ_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`),
KEY `IDX_QRTZ_J_GRP` (`SCHED_NAME`,`JOB_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of QRTZ_JOB_DETAILS
-- ----------------------------
BEGIN;
INSERT INTO `QRTZ_JOB_DETAILS` VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000057400104A4F425F48414E444C45525F4E414D4574000C7061794E6F746966794A6F627800);
INSERT INTO `QRTZ_JOB_DETAILS` VALUES ('schedulerName', 'sysUserSessionTimeoutJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000037400104A4F425F48414E444C45525F4E414D457400187379735573657253657373696F6E54696D656F75744A6F627800);
COMMIT;
@ -143,7 +145,7 @@ CREATE TABLE `QRTZ_LOCKS` (
`SCHED_NAME` varchar(120) NOT NULL,
`LOCK_NAME` varchar(40) NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of QRTZ_LOCKS
@ -161,7 +163,7 @@ CREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (
`SCHED_NAME` varchar(120) NOT NULL,
`TRIGGER_GROUP` varchar(190) NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of QRTZ_PAUSED_TRIGGER_GRPS
@ -176,16 +178,16 @@ DROP TABLE IF EXISTS `QRTZ_SCHEDULER_STATE`;
CREATE TABLE `QRTZ_SCHEDULER_STATE` (
`SCHED_NAME` varchar(120) NOT NULL,
`INSTANCE_NAME` varchar(190) NOT NULL,
`LAST_CHECKIN_TIME` bigint(13) NOT NULL,
`CHECKIN_INTERVAL` bigint(13) NOT NULL,
`LAST_CHECKIN_TIME` bigint NOT NULL,
`CHECKIN_INTERVAL` bigint NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of QRTZ_SCHEDULER_STATE
-- ----------------------------
BEGIN;
INSERT INTO `QRTZ_SCHEDULER_STATE` VALUES ('schedulerName', 'Yunai1620010117445', 1620010210071, 15000);
INSERT INTO `QRTZ_SCHEDULER_STATE` VALUES ('schedulerName', 'Yunai.local1635571630493', 1635572537879, 15000);
COMMIT;
-- ----------------------------
@ -196,12 +198,12 @@ CREATE TABLE `QRTZ_SIMPLE_TRIGGERS` (
`SCHED_NAME` varchar(120) NOT NULL,
`TRIGGER_NAME` varchar(190) NOT NULL,
`TRIGGER_GROUP` varchar(190) NOT NULL,
`REPEAT_COUNT` bigint(7) NOT NULL,
`REPEAT_INTERVAL` bigint(12) NOT NULL,
`TIMES_TRIGGERED` bigint(10) NOT NULL,
`REPEAT_COUNT` bigint NOT NULL,
`REPEAT_INTERVAL` bigint NOT NULL,
`TIMES_TRIGGERED` bigint NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of QRTZ_SIMPLE_TRIGGERS
@ -220,17 +222,17 @@ CREATE TABLE `QRTZ_SIMPROP_TRIGGERS` (
`STR_PROP_1` varchar(512) DEFAULT NULL,
`STR_PROP_2` varchar(512) DEFAULT NULL,
`STR_PROP_3` varchar(512) DEFAULT NULL,
`INT_PROP_1` int(11) DEFAULT NULL,
`INT_PROP_2` int(11) DEFAULT NULL,
`LONG_PROP_1` bigint(20) DEFAULT NULL,
`LONG_PROP_2` bigint(20) DEFAULT NULL,
`INT_PROP_1` int DEFAULT NULL,
`INT_PROP_2` int DEFAULT NULL,
`LONG_PROP_1` bigint DEFAULT NULL,
`LONG_PROP_2` bigint DEFAULT NULL,
`DEC_PROP_1` decimal(13,4) DEFAULT NULL,
`DEC_PROP_2` decimal(13,4) DEFAULT NULL,
`BOOL_PROP_1` varchar(1) DEFAULT NULL,
`BOOL_PROP_2` varchar(1) DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of QRTZ_SIMPROP_TRIGGERS
@ -249,15 +251,15 @@ CREATE TABLE `QRTZ_TRIGGERS` (
`JOB_NAME` varchar(190) NOT NULL,
`JOB_GROUP` varchar(190) NOT NULL,
`DESCRIPTION` varchar(250) DEFAULT NULL,
`NEXT_FIRE_TIME` bigint(13) DEFAULT NULL,
`PREV_FIRE_TIME` bigint(13) DEFAULT NULL,
`PRIORITY` int(11) DEFAULT NULL,
`NEXT_FIRE_TIME` bigint DEFAULT NULL,
`PREV_FIRE_TIME` bigint DEFAULT NULL,
`PRIORITY` int DEFAULT NULL,
`TRIGGER_STATE` varchar(16) NOT NULL,
`TRIGGER_TYPE` varchar(8) NOT NULL,
`START_TIME` bigint(13) NOT NULL,
`END_TIME` bigint(13) DEFAULT NULL,
`START_TIME` bigint NOT NULL,
`END_TIME` bigint DEFAULT NULL,
`CALENDAR_NAME` varchar(190) DEFAULT NULL,
`MISFIRE_INSTR` smallint(2) DEFAULT NULL,
`MISFIRE_INSTR` smallint DEFAULT NULL,
`JOB_DATA` blob,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
KEY `IDX_QRTZ_T_J` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
@ -273,13 +275,14 @@ CREATE TABLE `QRTZ_TRIGGERS` (
KEY `IDX_QRTZ_T_NFT_ST_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_STATE`),
KEY `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),
CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of QRTZ_TRIGGERS
-- ----------------------------
BEGIN;
INSERT INTO `QRTZ_TRIGGERS` VALUES ('schedulerName', 'sysUserSessionTimeoutJob', 'DEFAULT', 'sysUserSessionTimeoutJob', 'DEFAULT', NULL, 1620010260000, 1620010200000, 5, 'WAITING', 'CRON', 1613649236000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B0200007870000007D074000F4A4F425F52455452595F434F554E547371007E0009000000037800);
INSERT INTO `QRTZ_TRIGGERS` VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', 'payNotifyJob', 'DEFAULT', NULL, 1635572540000, 1635572539000, 5, 'WAITING', 'CRON', 1635294882000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
INSERT INTO `QRTZ_TRIGGERS` VALUES ('schedulerName', 'sysUserSessionTimeoutJob', 'DEFAULT', 'sysUserSessionTimeoutJob', 'DEFAULT', NULL, 1635572580000, 1635572520000, 5, 'WAITING', 'CRON', 1613649236000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B0200007870000007D074000F4A4F425F52455452595F434F554E547371007E0009000000037800);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

File diff suppressed because one or more lines are too long

View File

@ -36,6 +36,11 @@
<artifactId>yudao-spring-boot-starter-biz-sms</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-activiti</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
@ -112,6 +117,8 @@
<artifactId>yudao-spring-boot-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>

View File

@ -0,0 +1,114 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.oa;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo.*;
import cn.iocoder.yudao.adminserver.modules.activiti.convert.oa.OaLeaveConvert;
import cn.iocoder.yudao.adminserver.modules.activiti.dal.dataobject.oa.OaLeaveDO;
import cn.iocoder.yudao.adminserver.modules.activiti.service.oa.OaLeaveService;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
// TODO @jasonOa=OA 会不会好点名词缩写哈
@Api(tags = "请假申请")
@RestController
@RequestMapping("/oa/leave")
@Validated
public class OaLeaveController {
@Resource
private OaLeaveService leaveService;
@PostMapping("/create")
@ApiOperation("创建请假申请")
@PreAuthorize("@ss.hasPermission('oa:leave:create')")
public CommonResult<Long> createLeave(@Valid @RequestBody OaLeaveCreateReqVO createReqVO) {
// TODO @芋艿processKey 自己去理解下不过得把 leave 变成枚举
createReqVO.setProcessKey("leave");
return success(leaveService.createLeave(createReqVO));
}
@PostMapping("/form-key/create")
@ApiOperation("创建外置请假申请")
public CommonResult<Long> createFormKeyLeave(@Valid @RequestBody OaLeaveCreateReqVO createReqVO) {
// TODO @芋艿processKey 自己去理解下不过得把 formkey 变成枚举
createReqVO.setProcessKey("leave-formkey");
return success(leaveService.createLeave(createReqVO));
}
@PutMapping("/update")
@ApiOperation("更新请假申请")
@PreAuthorize("@ss.hasPermission('oa:leave:update')")
public CommonResult<Boolean> updateLeave(@Valid @RequestBody OaLeaveUpdateReqVO updateReqVO) {
leaveService.updateLeave(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@ApiOperation("删除请假申请")
@ApiImplicitParam(name = "id", value = "编号", required = true)
@PreAuthorize("@ss.hasPermission('oa:leave:delete')")
public CommonResult<Boolean> deleteLeave(@RequestParam("id") Long id) {
leaveService.deleteLeave(id);
return success(true);
}
@GetMapping("/get")
@ApiOperation("获得请假申请")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('oa:leave:query')")
public CommonResult<OaLeaveRespVO> getLeave(@RequestParam("id") Long id) {
OaLeaveDO leave = leaveService.getLeave(id);
return success(OaLeaveConvert.INSTANCE.convert(leave));
}
@GetMapping("/list")
@ApiOperation("获得请假申请列表")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
@PreAuthorize("@ss.hasPermission('oa:leave:query')")
public CommonResult<List<OaLeaveRespVO>> getLeaveList(@RequestParam("ids") Collection<Long> ids) {
List<OaLeaveDO> list = leaveService.getLeaveList(ids);
return success(OaLeaveConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@ApiOperation("获得请假申请分页")
@PreAuthorize("@ss.hasPermission('oa:leave:query')")
public CommonResult<PageResult<OaLeaveRespVO>> getLeavePage(@Valid OaLeavePageReqVO pageVO) {
//值查询自己申请请假
// TODO @芋艿这里的传值到底前端搞还是后端搞
pageVO.setUserId(SecurityFrameworkUtils.getLoginUser().getUsername());
PageResult<OaLeaveDO> pageResult = leaveService.getLeavePage(pageVO);
return success(OaLeaveConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@ApiOperation("导出请假申请 Excel")
@PreAuthorize("@ss.hasPermission('oa:leave:export')")
@OperateLog(type = EXPORT)
public void exportLeaveExcel(@Valid OaLeaveExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<OaLeaveDO> list = leaveService.getLeaveList(exportReqVO);
// 导出 Excel
List<OaLeaveExcelVO> datas = OaLeaveConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "请假申请.xls", "数据", OaLeaveExcelVO.class, datas);
}
}

View File

@ -0,0 +1,48 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 请假申请 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class OaLeaveBaseVO {
@ApiModelProperty(value = "流程id")
private String processInstanceId;
@ApiModelProperty(value = "状态", required = true)
private Integer status;
@ApiModelProperty(value = "申请人id", required = true)
private String userId;
@ApiModelProperty(value = "开始时间", required = true)
@NotNull(message = "开始时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date startTime;
@ApiModelProperty(value = "结束时间", required = true)
@NotNull(message = "结束时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date endTime;
@ApiModelProperty(value = "请假类型")
private String leaveType;
@ApiModelProperty(value = "原因")
private String reason;
@ApiModelProperty(value = "申请时间", required = true)
@NotNull(message = "申请时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date applyTime;
}

View File

@ -0,0 +1,15 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
@ApiModel("请假申请创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class OaLeaveCreateReqVO extends OaLeaveBaseVO {
private String processKey;
}

View File

@ -0,0 +1,44 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import com.alibaba.excel.annotation.ExcelProperty;
/**
* 请假申请 Excel VO
*
* @author 芋艿
*/
@Data
public class OaLeaveExcelVO {
@ExcelProperty("请假表单主键")
private Long id;
@ExcelProperty("流程id")
private String processInstanceId;
@ExcelProperty("状态")
private Integer status;
@ExcelProperty("申请人id")
private String userId;
@ExcelProperty("开始时间")
private Date startTime;
@ExcelProperty("结束时间")
private Date endTime;
@ExcelProperty("请假类型")
private String leaveType;
@ExcelProperty("原因")
private String reason;
@ExcelProperty("申请时间")
private Date applyTime;
}

View File

@ -0,0 +1,54 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel(value = "请假申请 Excel 导出 Request VO", description = "参数和 OaLeavePageReqVO 是一致的")
@Data
public class OaLeaveExportReqVO {
@ApiModelProperty(value = "流程id")
private String processInstanceId;
@ApiModelProperty(value = "状态")
private Integer status;
@ApiModelProperty(value = "申请人id")
private String userId;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "开始开始时间")
private Date beginStartTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "结束开始时间")
private Date endStartTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "开始结束时间")
private Date beginEndTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "结束结束时间")
private Date endEndTime;
@ApiModelProperty(value = "请假类型")
private String leaveType;
@ApiModelProperty(value = "原因")
private String reason;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "开始申请时间")
private Date beginApplyTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "结束申请时间")
private Date endApplyTime;
}

View File

@ -0,0 +1,56 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("请假申请分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class OaLeavePageReqVO extends PageParam {
@ApiModelProperty(value = "流程id")
private String processInstanceId;
@ApiModelProperty(value = "状态")
private Integer status;
@ApiModelProperty(value = "申请人id")
private String userId;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "开始开始时间")
private Date beginStartTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "结束开始时间")
private Date endStartTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "开始结束时间")
private Date beginEndTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "结束结束时间")
private Date endEndTime;
@ApiModelProperty(value = "请假类型")
private String leaveType;
@ApiModelProperty(value = "原因")
private String reason;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "开始申请时间")
private Date beginApplyTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "结束申请时间")
private Date endApplyTime;
}

View File

@ -0,0 +1,16 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
@ApiModel("请假申请 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class OaLeaveRespVO extends OaLeaveBaseVO {
@ApiModelProperty(value = "请假表单主键", required = true)
private Long id;
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
import java.util.Map;
@ApiModel("请假申请更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class OaLeaveUpdateReqVO extends OaLeaveBaseVO {
@ApiModelProperty(value = "请假表单主键", required = true)
@NotNull(message = "请假表单主键不能为空")
private Long id;
// TODO @jsonswagger validator 的注解要加哈
private String taskId;
private String comment;
private Map<String,Object> variables;
// TODO @芋艿variables 的作用是啥
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.engine.RepositoryService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
// TODO @jsonswagger validation 的注解后续要补全下哈可以等 workflow 基本写的差不多之后
@RestController
@RequestMapping("/workflow/process/definition")
public class ProcessDefinitionController {
@Resource
private RepositoryService repositoryService;
@Resource
private ProcessRuntime processRuntime;
@GetMapping(value = "/getStartForm")
public CommonResult<String> getStartForm(@RequestParam("processKey") String processKey){
//这样查似乎有问题 暂时写死
// final ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().
// processDefinitionKey(processKey).latestVersion().singleResult();
// processRuntime.processDefinition(processDefinition.getId()).getFormKey();
return CommonResult.success("/flow/leave/apply");
}
}

View File

@ -0,0 +1,60 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow.vo.*;
import cn.iocoder.yudao.adminserver.modules.activiti.service.workflow.TaskService;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
// TODO @jsonswagger validation 的注解后续要补全下哈可以等 workflow 基本写的差不多之后
@Api(tags = "工作流待办任务")
@RestController
@RequestMapping("/workflow/task")
public class TaskController {
@Resource
private TaskService taskService;
@GetMapping("/todo/page")
@ApiOperation("获取待办任务分页")
public CommonResult<PageResult<TodoTaskRespVO>> getTodoTaskPage(@Valid TodoTaskPageReqVO pageVO) {
return success(taskService.getTodoTaskPage(pageVO));
}
@GetMapping("/claim")
@ApiOperation("签收任务")
public CommonResult<Boolean> claimTask(@RequestParam("id") String taskId) {
taskService.claimTask(taskId);
return success(true);
}
@PostMapping("/task-steps")
public CommonResult<TaskHandleVO> getTaskSteps(@RequestBody TaskQueryReqVO taskQuery) {
return success(taskService.getTaskSteps(taskQuery));
}
@PostMapping("/formKey")
public CommonResult<TodoTaskRespVO> getTaskFormKey(@RequestBody TaskQueryReqVO taskQuery) {
return success(taskService.getTaskFormKey(taskQuery));
}
@PostMapping("/complete")
public CommonResult<Boolean> complete(@RequestBody TaskReqVO taskReq) {
taskService.completeTask(taskReq);
return success(true);
}
@GetMapping("/process/history-steps")
public CommonResult<List<TaskStepVO>> getHistorySteps(@RequestParam("id") String processInstanceId) {
return success(taskService.getHistorySteps(processInstanceId));
}
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow.vo;
import lombok.Data;
import lombok.ToString;
import java.util.List;
@Data
@ToString
public class TaskHandleVO {
private Object formObject;
private List<TaskStepVO> historyTask;
private String taskVariable;
}

View File

@ -0,0 +1,15 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow.vo;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class TaskQueryReqVO {
private String processKey;
private String taskId;
private String businessKey;
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow.vo;
import lombok.Data;
import lombok.ToString;
import java.util.Map;
@Data
@ToString
public class TaskReqVO {
private String taskId;
private Map<String,Object> variables;
private String comment;
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow.vo;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
@Data
@ToString
public class TaskStepVO {
private String stepName;
private Date startTime;
private Date endTime;
private String assignee;
private String comment;
private Integer status;
}

View File

@ -0,0 +1,16 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ApiModel("待办任务申请分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TodoTaskPageReqVO extends PageParam {
private String assignee;
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow.vo;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ApiModel("待办任务 Response VO")
@Data
@ToString
public class TodoTaskRespVO {
private String id;
/**
* 1:未签收
* 2:已签收
*/
private Integer status;
private String processName;
private String processKey;
private String businessKey;
private String formKey;
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.adminserver.modules.activiti.convert.oa;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo.*;
import cn.iocoder.yudao.adminserver.modules.activiti.dal.dataobject.oa.OaLeaveDO;
/**
* 请假申请 Convert
*
* @author 芋艿
*/
@Mapper
public interface OaLeaveConvert {
OaLeaveConvert INSTANCE = Mappers.getMapper(OaLeaveConvert.class);
OaLeaveDO convert(OaLeaveCreateReqVO bean);
OaLeaveDO convert(OaLeaveUpdateReqVO bean);
OaLeaveRespVO convert(OaLeaveDO bean);
List<OaLeaveRespVO> convertList(List<OaLeaveDO> list);
PageResult<OaLeaveRespVO> convertPage(PageResult<OaLeaveDO> page);
List<OaLeaveExcelVO> convertList02(List<OaLeaveDO> list);
}

View File

@ -0,0 +1,9 @@
package cn.iocoder.yudao.adminserver.modules.activiti.convert.workflow;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface TodoTaskConvert {
TodoTaskConvert INSTANCE = Mappers.getMapper(TodoTaskConvert.class);
}

View File

@ -0,0 +1,60 @@
package cn.iocoder.yudao.adminserver.modules.activiti.dal.dataobject.oa;
import lombok.*;
import java.util.*;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 请假申请 DO
*
* @author 芋艿
*/
@TableName("oa_leave")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OaLeaveDO extends BaseDO {
/**
* 请假表单主键
*/
@TableId
private Long id;
/**
* 流程id
*/
private String processInstanceId;
/**
* 状态
*/
private Integer status;
/**
* 申请人id
*/
private String userId;
/**
* 开始时间
*/
private Date startTime;
/**
* 结束时间
*/
private Date endTime;
/**
* 请假类型
*/
private String leaveType;
/**
* 原因
*/
private String reason;
/**
* 申请时间
*/
private Date applyTime;
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.adminserver.modules.activiti.dal.dataobject.process;
/**
* 流程模型实体类 映射 activiti ProcessDefinition接口
*
* @author ZJQ
* @date 2021/9/7 23:23
*/
public class ProcessDefinitionDO {
private String id;
private String category;
private String key;
private String name;
private String version;
private String resourceName;
private String deploymentId;
private String diagramResourceName;
private boolean suspended;
}

View File

@ -0,0 +1,46 @@
package cn.iocoder.yudao.adminserver.modules.activiti.dal.mysql.oa;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.adminserver.modules.activiti.dal.dataobject.oa.OaLeaveDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo.*;
/**
* 请假申请 Mapper
*
* @author 芋艿
*/
@Mapper
public interface OaLeaveMapper extends BaseMapperX<OaLeaveDO> {
default PageResult<OaLeaveDO> selectPage(OaLeavePageReqVO reqVO) {
return selectPage(reqVO, new QueryWrapperX<OaLeaveDO>()
.eqIfPresent("process_instance_id", reqVO.getProcessInstanceId())
.eqIfPresent("status", reqVO.getStatus())
.eqIfPresent("user_id", reqVO.getUserId())
.betweenIfPresent("start_time", reqVO.getBeginStartTime(), reqVO.getEndStartTime())
.betweenIfPresent("end_time", reqVO.getBeginEndTime(), reqVO.getEndEndTime())
.eqIfPresent("leave_type", reqVO.getLeaveType())
.eqIfPresent("reason", reqVO.getReason())
.betweenIfPresent("apply_time", reqVO.getBeginApplyTime(), reqVO.getEndApplyTime())
.orderByDesc("id") );
}
default List<OaLeaveDO> selectList(OaLeaveExportReqVO reqVO) {
return selectList(new QueryWrapperX<OaLeaveDO>()
.eqIfPresent("process_instance_id", reqVO.getProcessInstanceId())
.eqIfPresent("status", reqVO.getStatus())
.eqIfPresent("user_id", reqVO.getUserId())
.betweenIfPresent("start_time", reqVO.getBeginStartTime(), reqVO.getEndStartTime())
.betweenIfPresent("end_time", reqVO.getBeginEndTime(), reqVO.getEndEndTime())
.eqIfPresent("leave_type", reqVO.getLeaveType())
.eqIfPresent("reason", reqVO.getReason())
.betweenIfPresent("apply_time", reqVO.getBeginApplyTime(), reqVO.getEndApplyTime())
.orderByDesc("id") );
}
}

View File

@ -0,0 +1,13 @@
package cn.iocoder.yudao.adminserver.modules.activiti.enums;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
/**
* activiti 系统 错误码枚举类
*
* 003 activiti
* 001 oa
* activiti 系统使用 1-003-000-000
*/
public interface OaErrorCodeConstants {
ErrorCode LEAVE_NOT_EXISTS = new ErrorCode(1003001001, "请假申请不存在");
}

View File

@ -0,0 +1,2 @@
// TODO @芋艿思考下 activitioa 的定位边界模块的拆分
package cn.iocoder.yudao.adminserver.modules.activiti;

View File

@ -0,0 +1,62 @@
package cn.iocoder.yudao.adminserver.modules.activiti.service.config;
import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysPostService;
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService;
import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import org.activiti.api.runtime.shared.identity.UserGroupManager;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.Collections.singleton;
@Service
public class UserGroupManagerService implements UserGroupManager {
@Resource
private UserDetailsService userDetailsService;
@Resource
private SysUserService userService;
@Resource
private SysPostService sysPostService;
/**
* 暂时使用岗位来代替
* @param userId
* @return
*/
@Override
public List<String> getUserGroups(String userId) {
// final LoginUser loginUser = (LoginUser) userDetailsService.loadUserByUsername(userId);
// final Long id = loginUser.getId();
final SysUserDO user = userService.getUserByUsername(userId);
return sysPostService.getPosts(user.getPostIds()).stream().map(post -> post.getCode()).collect(Collectors.toList());
}
@Override
public List<String> getUserRoles(String userId) {
return Arrays.asList("ROLE_ACTIVITI_USER");
}
@Override
public List<String> getGroups() {
throw new UnsupportedOperationException("getGroups is now un supported");
}
@Override
public List<String> getUsers() {
throw new UnsupportedOperationException("getGroups is now un supported");
}
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.adminserver.modules.activiti.service.config;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import org.activiti.api.runtime.shared.security.PrincipalGroupsProvider;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import java.security.Principal;
import java.util.Collections;
import java.util.List;
@Service
public class UserGroupsProvider implements PrincipalGroupsProvider {
@Override
public List<String> getGroups(Principal principal) {
if(principal instanceof Authentication){
Authentication authentication = (Authentication) principal;
final Object user = authentication.getPrincipal();
if( user instanceof LoginUser){
return ((LoginUser) user).getGroups();
}else{
return Collections.emptyList();
}
}else{
return Collections.emptyList();
}
}
}

View File

@ -0,0 +1,76 @@
package cn.iocoder.yudao.adminserver.modules.activiti.service.oa;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo.OaLeaveCreateReqVO;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo.OaLeaveExportReqVO;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo.OaLeavePageReqVO;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo.OaLeaveUpdateReqVO;
import cn.iocoder.yudao.adminserver.modules.activiti.dal.dataobject.oa.OaLeaveDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
/**
* 请假申请 Service 接口
*
* @author 芋艿
*/
public interface OaLeaveService {
/**
* 创建请假申请
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createLeave(@Valid OaLeaveCreateReqVO createReqVO);
/**
* 更新请假申请
*
* @param updateReqVO 更新信息
*/
void updateLeave(@Valid OaLeaveUpdateReqVO updateReqVO);
/**
* 删除请假申请
*
* @param id 编号
*/
void deleteLeave(Long id);
/**
* 获得请假申请
*
* @param id 编号
* @return 请假申请
*/
OaLeaveDO getLeave(Long id);
/**
* 获得请假申请列表
*
* @param ids 编号
* @return 请假申请列表
*/
List<OaLeaveDO> getLeaveList(Collection<Long> ids);
/**
* 获得请假申请分页
*
* @param pageReqVO 分页查询
* @return 请假申请分页
*/
PageResult<OaLeaveDO> getLeavePage(OaLeavePageReqVO pageReqVO);
/**
* 获得请假申请列表, 用于 Excel 导出
*
* @param exportReqVO 查询条件
* @return 请假申请列表
*/
List<OaLeaveDO> getLeaveList(OaLeaveExportReqVO exportReqVO);
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.adminserver.modules.activiti.service.oa;
import cn.iocoder.yudao.adminserver.modules.activiti.dal.dataobject.oa.OaLeaveDO;
import cn.iocoder.yudao.adminserver.modules.activiti.dal.mysql.oa.OaLeaveMapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Component
public class ReportBackEndProcessor implements ExecutionListener {
@Resource
private OaLeaveMapper leaveMapper;
// @Override
// @Transactional(rollbackFor = Exception.class)
// public void notify(DelegateTask delegateTask) {
// final String businessKey = delegateTask.getExecution().getProcessInstanceBusinessKey();
// UpdateWrapper<OaLeaveDO> updateWrapper = new UpdateWrapper<>();
// updateWrapper.eq("id", Long.valueOf(businessKey));
// OaLeaveDO updateDo = new OaLeaveDO();
// updateDo.setStatus(2);
// leaveMapper.update(updateDo, updateWrapper);
// }
@Override
@Transactional(rollbackFor = Exception.class)
public void notify(DelegateExecution delegateExecution) {
final String businessKey = delegateExecution.getProcessInstanceBusinessKey();
// TODO @jsonservice 不要出现 dao 的元素例如说 UpdateWrapper这里我们可以调用 updateById 方法
UpdateWrapper<OaLeaveDO> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", Long.valueOf(businessKey));
OaLeaveDO updateDo = new OaLeaveDO();
updateDo.setStatus(2); // TODO @jsonstatus 要枚举起来不要出现 magic number
leaveMapper.update(updateDo, updateWrapper);
}
}

View File

@ -0,0 +1,141 @@
package cn.iocoder.yudao.adminserver.modules.activiti.service.oa.impl;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo.OaLeaveCreateReqVO;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo.OaLeaveExportReqVO;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo.OaLeavePageReqVO;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.oa.vo.OaLeaveUpdateReqVO;
import cn.iocoder.yudao.adminserver.modules.activiti.convert.oa.OaLeaveConvert;
import cn.iocoder.yudao.adminserver.modules.activiti.dal.dataobject.oa.OaLeaveDO;
import cn.iocoder.yudao.adminserver.modules.activiti.dal.mysql.oa.OaLeaveMapper;
import cn.iocoder.yudao.adminserver.modules.activiti.service.oa.OaLeaveService;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.runtime.TaskRuntime;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.adminserver.modules.activiti.enums.OaErrorCodeConstants.LEAVE_NOT_EXISTS;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* 请假申请 Service 实现类
*
* @author 芋艿
*/
@Service
@Validated
public class OaLeaveServiceImpl implements OaLeaveService {
@Resource
private OaLeaveMapper leaveMapper;
@Resource
private RuntimeService runtimeService;
@Resource
private org.activiti.engine.TaskService activitiTaskService;
@Resource
private TaskRuntime taskRuntime;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createLeave(OaLeaveCreateReqVO createReqVO) {
// 插入 OA 请假单
OaLeaveDO leave = OaLeaveConvert.INSTANCE.convert(createReqVO);
leave.setStatus(1);
leave.setUserId(SecurityFrameworkUtils.getLoginUser().getUsername());
leaveMapper.insert(leave);
// 创建工作流
Map<String, Object> variables = new HashMap<>();
// 如何得到部门领导人暂时写死
variables.put("deptLeader", "admin"); // TODO @芋艿需要部门的负责人
Long id = leave.getId();
String businessKey = String.valueOf(id);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(createReqVO.getProcessKey(), businessKey, variables);
String processInstanceId = processInstance.getProcessInstanceId();
// TODO @jsonservice 不要出现 dao 的元素例如说 UpdateWrapper这里我们可以调用 updateById 方法
// 将工作流的编号更新到 OA 请假单中
UpdateWrapper<OaLeaveDO> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", id);
OaLeaveDO updateDo = new OaLeaveDO();
updateDo.setProcessInstanceId(processInstanceId);
leaveMapper.update(updateDo, updateWrapper);
return id;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateLeave(OaLeaveUpdateReqVO updateReqVO) {
// 校验存在
this.validateLeaveExists(updateReqVO.getId());
final Task task = taskRuntime.task(updateReqVO.getTaskId());
activitiTaskService.addComment(task.getId(), task.getProcessInstanceId(), updateReqVO.getComment());
Map<String, Object> variables = updateReqVO.getVariables();
//如何得到部门领导人 暂时写死
variables.put("deptLeader", "admin");
taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId())
.withVariables(variables)
.build());
// TODO @jason不需要加 final 虽然是不变但是代码比较少这么去写
final Object reApply = variables.get("reApply");
// TODO @jason直接使用 Objects.equals(reApply, true) 就可以
if((reApply instanceof Boolean) && (Boolean)reApply){
// 更新 表单
OaLeaveDO updateObj = OaLeaveConvert.INSTANCE.convert(updateReqVO);
leaveMapper.updateById(updateObj);
}
}
@Override
public void deleteLeave(Long id) {
// 校验存在
this.validateLeaveExists(id);
// 删除
leaveMapper.deleteById(id);
// TODO @jason需要调用 runtimeService delete 方法删除
}
private void validateLeaveExists(Long id) {
if (leaveMapper.selectById(id) == null) {
throw exception(LEAVE_NOT_EXISTS);
}
}
@Override
public OaLeaveDO getLeave(Long id) {
return leaveMapper.selectById(id);
}
@Override
public List<OaLeaveDO> getLeaveList(Collection<Long> ids) {
return leaveMapper.selectBatchIds(ids);
}
@Override
public PageResult<OaLeaveDO> getLeavePage(OaLeavePageReqVO pageReqVO) {
return leaveMapper.selectPage(pageReqVO);
}
@Override
public List<OaLeaveDO> getLeaveList(OaLeaveExportReqVO exportReqVO) {
return leaveMapper.selectList(exportReqVO);
}
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.adminserver.modules.activiti.service.process;
import org.springframework.web.multipart.MultipartFile;
/**
* 流程基础管理
*
* @author ZJQ
* @date 2021/9/5 21:00
*/
public interface ProcessService {
/**
* 上传流程文件进行流程模型部署
* @param multipartFile 上传文件
*/
void deployProcess(MultipartFile multipartFile);
/**
* 激活或者挂起流程模型实体
* @param processDefinitionId 流程模型实体id
* @param type 类型
* @return 状态
*/
String setActivOrHang(String processDefinitionId,String type);
}

View File

@ -0,0 +1,109 @@
package cn.iocoder.yudao.adminserver.modules.activiti.service.process.impl;
import cn.iocoder.yudao.adminserver.modules.activiti.service.process.ProcessService;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.zip.ZipInputStream;
import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.FILE_UPLOAD_FAILED;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* 流程基础管理
*
* @author ZJQ
* @date 2021/9/5 21:04
*/
@Service
@Slf4j
public class ProcessServiceImpl implements ProcessService {
private static final String BPMN20_XML = "bpmn20.xml";
@Resource
private RepositoryService repositoryService;
/**
* 上传流程文件进行流程部署
* @param multipartFile 上传文件
*/
@Override
public void deployProcess(MultipartFile multipartFile) {
String fileName = multipartFile.getOriginalFilename();
try (InputStream inputStream = multipartFile.getInputStream()){
Deployment deployment = getDeplymentByType(inputStream,fileName);
//获取部署成功的流程模型
List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
processDefinitions.forEach((processDefinition)->{
//设置线上部署流程模型名字
String proDefId = processDefinition.getId();
repositoryService.setProcessDefinitionCategory(proDefId,fileName);
log.info("流程文件部署成功流程ID="+proDefId);
});
} catch (IOException e) {
log.error("流程部署出现异常"+e);
}
}
/**
* 激活或者挂起流程模型实体
* @param processDefinitionId 流程模型实体id
* @param type 类型
* @return 提示
*/
@Override
public String setActivOrHang(String processDefinitionId, String type) {
String result = "无操作";
switch (type){
case "active":
repositoryService.activateProcessDefinitionById(processDefinitionId,true,null);
result = "已激活ID为【"+processDefinitionId+"】的流程模型实例";
break;
case "suspend":
repositoryService.suspendProcessDefinitionById(processDefinitionId,true,null);
result = "已挂起ID为【"+processDefinitionId+"】的流程模型实例";
break;
default:
break;
}
return result;
}
/**
* 根据上传文件类型对应实现不同方式的流程部署
* @param inputStream 文件输入流
* @param fileName 文件名
* @return 文件部署流程
*/
public Deployment getDeplymentByType(InputStream inputStream,String fileName){
Deployment deployment;
String type = FilenameUtils.getExtension(fileName);
switch (type){
case "bpmn":
String baseName = FilenameUtils.getBaseName(fileName);
deployment = repositoryService.createDeployment().addInputStream(baseName+"."+BPMN20_XML,inputStream).deploy();
break;
case "png":
deployment = repositoryService.createDeployment().addInputStream(fileName,inputStream).deploy();
break;
case "zip":
case "bar":
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
deployment = repositoryService.createDeployment().addZipInputStream(zipInputStream).deploy();
break;
default:
throw exception(FILE_UPLOAD_FAILED);
}
return deployment;
}
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.adminserver.modules.activiti.service.workflow;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow.vo.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import java.util.List;
// TODO @芋艿前缀注释
public interface TaskService {
PageResult<TodoTaskRespVO> getTodoTaskPage(TodoTaskPageReqVO pageReqVO);
void claimTask(String taskId);
void getTaskHistory(String taskId);
void completeTask(TaskReqVO taskReq);
// void flowImage(String taskId, HttpServletResponse response);
TaskHandleVO getTaskSteps(TaskQueryReqVO taskQuery);
List<TaskStepVO> getHistorySteps(String processInstanceId);
TodoTaskRespVO getTaskFormKey(TaskQueryReqVO taskQuery);
}

View File

@ -0,0 +1,266 @@
package cn.iocoder.yudao.adminserver.modules.activiti.service.workflow.impl;
import cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow.vo.*;
import cn.iocoder.yudao.adminserver.modules.activiti.dal.mysql.oa.OaLeaveMapper;
import cn.iocoder.yudao.adminserver.modules.activiti.service.workflow.TaskService;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import com.google.common.collect.ImmutableMap;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.ClaimTaskPayloadBuilder;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.runtime.TaskRuntime;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.task.Comment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
public class TaskServiceImpl implements TaskService {
@Resource
private TaskRuntime taskRuntime;
@Resource
private org.activiti.engine.TaskService activitiTaskService;
@Resource
private HistoryService historyService;
@Resource
private RepositoryService repositoryService;
@Resource
private OaLeaveMapper leaveMapper;
private static Map<String,String> taskVariable = ImmutableMap.<String,String>builder()
.put("deptLeaderVerify","deptLeaderApproved")
.put("hrVerify","hrApproved")
.build();
public TaskServiceImpl() {
}
@Override
public PageResult<TodoTaskRespVO> getTodoTaskPage(TodoTaskPageReqVO pageReqVO) {
final LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
// TODO @jason封装一个方法用于转换成 activiti 的分页对象
final Pageable pageable = Pageable.of((pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize(), pageReqVO.getPageSize());
Page<Task> pageTasks = taskRuntime.tasks(pageable);
// TODO @jasonconvert 里转换
List<Task> tasks = pageTasks.getContent();
int totalItems = pageTasks.getTotalItems();
final List<TodoTaskRespVO> respVOList = tasks.stream().map(task -> {
TodoTaskRespVO respVO = new TodoTaskRespVO();
respVO.setId(task.getId());
final ProcessDefinition definition = repositoryService.getProcessDefinition(task.getProcessDefinitionId());
respVO.setProcessName(definition.getName());
respVO.setProcessKey(definition.getKey());
respVO.setBusinessKey(task.getBusinessKey());
respVO.setStatus(task.getAssignee() == null ? 1 : 2);
return respVO;
}).collect(Collectors.toList());
// TODO @jason要注意泛型哈
return new PageResult(respVOList, Long.valueOf(totalItems)); // TODO @jason(long) 转换即可
}
@Override
public void claimTask(String taskId) {
taskRuntime.claim(new ClaimTaskPayloadBuilder()
.withTaskId(taskId)
.withAssignee(SecurityFrameworkUtils.getLoginUser().getUsername())
.build());
}
@Override
public void getTaskHistory(String taskId) {
final List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().
processInstanceId("8e2801fc-1a38-11ec-98ce-74867a13730f").list();
}
// TODO @jason一个方法里会有多个方法的调用最好写下对应的注释这样容易理解
@Override
@Transactional
public void completeTask(TaskReqVO taskReq) {
final Task task = taskRuntime.task(taskReq.getTaskId());
final Map<String, Object> variables = taskReq.getVariables();
activitiTaskService.addComment(taskReq.getTaskId(), task.getProcessInstanceId(), taskReq.getComment());
taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(taskReq.getTaskId())
.withVariables(taskReq.getVariables())
.build());
// if(variables.containsValue(Boolean.FALSE)){
// final String businessKey = task.getBusinessKey();
// UpdateWrapper<OaLeaveDO> updateWrapper = new UpdateWrapper<>();
// updateWrapper.eq("id", Long.valueOf(businessKey));
// OaLeaveDO updateDo = new OaLeaveDO();
// updateDo.setStatus(2);
// leaveMapper.update(updateDo, updateWrapper);
// }
}
// @Override
// public void flowImage(String taskId, HttpServletResponse response) {
//
// final Task task = taskRuntime.task(taskId);
// BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
// final Process process = bpmnModel.getMainProcess();
// ProcessDefinitionEntity processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
// List<String> activeActivityIds = runtimeService.getActiveActivityIds(executionId);
// List<String> highLightedFlows = getHighLightedFlows(processDefinition, processInstance.getId());
// ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
// InputStream imageStream =diagramGenerator.generateDiagram(bpmnModel, "png", activeActivityIds, highLightedFlows);
//
// // 输出资源内容到相应对象
// byte[] b = new byte[1024];
// int len;
// while ((len = imageStream.read(b, 0, 1024)) != -1) {
// response.getOutputStream().write(b, 0, len);
// }
// }
@Override
public TaskHandleVO getTaskSteps(TaskQueryReqVO taskQuery) {
TaskHandleVO handleVO = new TaskHandleVO();
// String processKey = taskQuery.getProcessKey();
// if ("leave".equals(processKey)) {
// String businessKey = taskQuery.getBusinessKey();
// final OaLeaveDO leave = leaveMapper.selectById(Long.valueOf(businessKey));
// handleVO.setFormObject( OaLeaveConvert.INSTANCE.convert(leave));
// }
//
// final String taskDefKey = task.getTaskDefinitionKey();
// final String variableName = Optional.ofNullable(taskVariable.get(taskDefKey)).orElse("");
// handleVO.setTaskVariable(variableName);
final Task task = taskRuntime.task(taskQuery.getTaskId());
List<TaskStepVO> steps = getTaskSteps(task.getProcessInstanceId());
handleVO.setHistoryTask(steps);
return handleVO;
}
private List<TaskStepVO> getTaskSteps(String processInstanceId) {
// 获得已完成的活动
List<HistoricActivityInstance> finished = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processInstanceId)
.activityType("userTask")
.finished()
.orderByHistoricActivityInstanceStartTime().asc().list();
// 获得对应的步骤
List<TaskStepVO> steps = new ArrayList<>();
finished.forEach(instance -> {
// TODO @jason放到 convert
TaskStepVO step = new TaskStepVO();
step.setStepName(instance.getActivityName());
step.setStartTime(instance.getStartTime());
step.setEndTime(instance.getEndTime());
step.setAssignee(instance.getAssignee());
step.setStatus(1);
// TODO @jason一般判数组为空使用 CollUtil.isEmpty 会好点哈另外null 时候不用填写 "" 的哈
List<Comment> comments = activitiTaskService.getTaskComments(instance.getTaskId());
if (comments.size() > 0) {
step.setComment(comments.get(0).getFullMessage());
} else {
step.setComment("");
}
steps.add(step);
});
// 获得未完成的活动
List<HistoricActivityInstance> unfinished = historyService
.createHistoricActivityInstanceQuery()
.processInstanceId(processInstanceId)
.activityType("userTask")
.unfinished().list();
// 获得对应的步骤
// TODO @json其实已完成和未完成它们的 convert 的逻辑是一致的
for (HistoricActivityInstance instance : unfinished) {
TaskStepVO step = new TaskStepVO();
step.setStepName(instance.getActivityName());
step.setStartTime(instance.getStartTime());
step.setEndTime(instance.getEndTime());
step.setAssignee(Optional.ofNullable(instance.getAssignee()).orElse(""));
step.setComment("");
step.setStatus(0);
steps.add(step);
}
return steps;
}
@Override
public List<TaskStepVO> getHistorySteps(String processInstanceId) {
return getTaskSteps(processInstanceId);
}
@Override
public TodoTaskRespVO getTaskFormKey(TaskQueryReqVO taskQuery) {
final Task task = taskRuntime.task(taskQuery.getTaskId());
// 转换结果
TodoTaskRespVO respVO = new TodoTaskRespVO();
respVO.setFormKey(task.getFormKey());
respVO.setBusinessKey(task.getBusinessKey());
respVO.setId(task.getId());
return respVO;
}
// private List<String> getHighLightedFlows(ProcessDefinitionEntity processDefinition, String processInstanceId) {
//
// List<String> highLightedFlows = new ArrayList<String>();
// List<HistoricActivityInstance> historicActivityInstances = historyService
// .createHistoricActivityInstanceQuery()
// .processInstanceId(processInstanceId)
// .orderByHistoricActivityInstanceStartTime().asc().list();
//
// List<String> historicActivityInstanceList = new ArrayList<String>();
// for (HistoricActivityInstance hai : historicActivityInstances) {
// historicActivityInstanceList.add(hai.getActivityId());
// }
// // add current activities to list
// List<String> highLightedActivities = runtimeService.getActiveActivityIds(processInstanceId);
// historicActivityInstanceList.addAll(highLightedActivities);
// activities and their sequence-flows
// for (ActivityImpl activity : processDefinition.getActivities()) {
// int index = historicActivityInstanceList.indexOf(activity.getId());
//
// if (index >= 0 && index + 1 < historicActivityInstanceList.size()) {
// List<PvmTransition> pvmTransitionList = activity
// .getOutgoingTransitions();
// for (PvmTransition pvmTransition : pvmTransitionList) {
// String destinationFlowId = pvmTransition.getDestination().getId();
// if (destinationFlowId.equals(historicActivityInstanceList.get(index + 1))) {
// highLightedFlows.add(pvmTransition.getId());
// }
// }
// }
// }
// return highLightedFlows;
// }
}

View File

@ -1,5 +1,11 @@
package cn.iocoder.yudao.adminserver.modules.system.service.auth.impl;
import cn.iocoder.yudao.adminserver.modules.system.service.auth.SysUserSessionService;
import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysPostService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthLoginReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthSocialBindReqVO;
@ -40,11 +46,15 @@ import org.springframework.util.Assert;
import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.*;
import static java.util.Collections.EMPTY_LIST;
import static java.util.Collections.singleton;
/**
@ -73,6 +83,7 @@ public class SysAuthServiceImpl implements SysAuthService {
@Resource
private SysUserSessionCoreService userSessionCoreService;
@Resource
private SysPostService sysPostService;
private SysSocialService socialService;
// TODO @timfruit静态枚举类需要都大写例如说 USER_TYPE_ENUM静态变量放在普通变量前面这个实践不错哈
@ -86,7 +97,9 @@ public class SysAuthServiceImpl implements SysAuthService {
throw new UsernameNotFoundException(username);
}
// 创建 LoginUser 对象
return SysAuthConvert.INSTANCE.convert(user);
LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
loginUser.setPostIds(user.getPostIds());
return loginUser;
}
@Override
@ -112,11 +125,18 @@ public class SysAuthServiceImpl implements SysAuthService {
// 使用账号密码进行登录
LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword());
loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
// 缓存登用户到 Redis 返回 sessionId 编号
loginUser.setGroups(this.getUserPosts(loginUser.getPostIds()));
// 缓存登用户到 Redis 返回 sessionId 编号
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
}
private List<String> getUserPosts(Set<Long> postIds) {
return Optional.ofNullable(postIds).map(ids->
sysPostService.getPosts(ids).stream().map(post -> post.getCode()).collect(Collectors.toList())
).orElse(EMPTY_LIST);
}
private void verifyCaptcha(String username, String captchaUUID, String captchaCode) {
final SysLoginLogTypeEnum logTypeEnum = SysLoginLogTypeEnum.LOGIN_USERNAME;
String code = captchaService.getCaptchaCode(captchaUUID);
@ -144,6 +164,7 @@ public class SysAuthServiceImpl implements SysAuthService {
// 调用 Spring Security AuthenticationManager#authenticate(...) 方法使用账号密码进行认证
// 在其内部会调用到 loadUserByUsername 方法获取 User 信息
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
// org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
} catch (BadCredentialsException badCredentialsException) {
this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS);
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);

View File

@ -47,13 +47,27 @@ spring:
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
password: root
slave: # 模拟从库,可根据自己需要修改
name: ruoyi-vue-pro
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
password: root
activiti:
#1.false:默认值activiti启动时对比数据库表中保存的版本如果不匹配。将抛出异常
#2.true:启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表
#3.create_drop:启动时自动创建表,关闭时自动删除表
#4.drop_create:启动时,删除旧表,再创建新表
database-schema-update: true
#activiti7默认不生成历史信息表需手动设置开启
db-history-used: true
check-process-definitions: true
#full保存历史数据的最高级别可保存全部流程相关细节包括流程流转各节点参数
history-level: full
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis:

View File

@ -22,15 +22,28 @@ spring:
# MyBatis Plus 的配置项
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志
# 在 mybatis-config/mybatis-config.xml 中设置
# configuration:
# map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志
global-config:
db-config:
id-type: AUTO # 自增 ID
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
mapper-locations: classpath*:mapper/*.xml
config-location: classpath:mybatis-config/mybatis-config.xml
configuration-properties:
prefix: ""
wildcardEscapeClause: ""
limitBefore: ""
limitAfter: "LIMIT #{maxResults} OFFSET #{firstResult}"
limitBetween: ""
limitOuterJoinBetween: ""
limitBeforeNativeQuery: ""
orderBy: "order by ${orderByColumns}"
blobType: "BLOB"
boolValue: "TRUE"
type-aliases-package: ${yudao.info.base-package}.modules.*.dal.dataobject, ${yudao.core-service.base-package}.modules.*.dal.dataobject
--- #################### 芋道相关配置 ####################

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="lazyLoadingEnabled" value="false" />
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<typeAlias type="org.activiti.engine.impl.persistence.ByteArrayRefTypeHandler" alias="ByteArrayRefTypeHandler"/>
<typeAlias type="org.activiti.engine.impl.db.IbatisVariableTypeHandler" alias="IbatisVariableTypeHandler"/>
</typeAliases>
<typeHandlers>
<typeHandler handler="ByteArrayRefTypeHandler"
javaType="org.activiti.engine.impl.persistence.entity.ByteArrayRef"
jdbcType="VARCHAR"/>
<typeHandler handler="IbatisVariableTypeHandler"
javaType="org.activiti.engine.impl.variable.VariableType"
jdbcType="VARCHAR"/>
</typeHandlers>
<mappers>
<mapper resource="org/activiti/db/mapping/common.xml" />
<mapper resource="org/activiti/db/mapping/entity/Attachment.xml" />
<mapper resource="org/activiti/db/mapping/entity/ByteArray.xml" />
<mapper resource="org/activiti/db/mapping/entity/Comment.xml" />
<mapper resource="org/activiti/db/mapping/entity/DeadLetterJob.xml" />
<mapper resource="org/activiti/db/mapping/entity/Deployment.xml" />
<mapper resource="org/activiti/db/mapping/entity/Execution.xml" />
<mapper resource="org/activiti/db/mapping/entity/HistoricActivityInstance.xml" />
<mapper resource="org/activiti/db/mapping/entity/HistoricDetail.xml" />
<mapper resource="org/activiti/db/mapping/entity/HistoricProcessInstance.xml" />
<mapper resource="org/activiti/db/mapping/entity/HistoricVariableInstance.xml" />
<mapper resource="org/activiti/db/mapping/entity/HistoricTaskInstance.xml" />
<mapper resource="org/activiti/db/mapping/entity/HistoricIdentityLink.xml" />
<mapper resource="org/activiti/db/mapping/entity/IdentityLink.xml" />
<mapper resource="org/activiti/db/mapping/entity/Job.xml" />
<mapper resource="org/activiti/db/mapping/entity/Model.xml" />
<mapper resource="org/activiti/db/mapping/entity/ProcessDefinition.xml" />
<mapper resource="org/activiti/db/mapping/entity/ProcessDefinitionInfo.xml" />
<mapper resource="org/activiti/db/mapping/entity/Property.xml" />
<mapper resource="org/activiti/db/mapping/entity/Resource.xml" />
<mapper resource="org/activiti/db/mapping/entity/SuspendedJob.xml" />
<mapper resource="org/activiti/db/mapping/entity/TableData.xml" />
<mapper resource="org/activiti/db/mapping/entity/Task.xml" />
<mapper resource="org/activiti/db/mapping/entity/TimerJob.xml" />
<mapper resource="org/activiti/db/mapping/entity/VariableInstance.xml" />
<mapper resource="org/activiti/db/mapping/entity/EventSubscription.xml" />
<mapper resource="org/activiti/db/mapping/entity/EventLogEntry.xml" />
<mapper resource="org/activiti/db/mapping/entity/IntegrationContext.xml" />
</mappers>
</configuration>

View File

@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://bpmn.io/schema/bpmn">
<process id="leave-formkey" name="请假流程-外置表单" isExecutable="true">
<documentation>外置表单</documentation>
<startEvent id="startevent1" name="Start" activiti:initiator="applyUserId" activiti:formKey="/flow/leave/apply"></startEvent>
<userTask id="deptLeaderVerify" name="部门经理审批" activiti:assignee="${deptLeader}" activiti:formKey="/flow/leave/approve-leader"></userTask>
<exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
<userTask id="hrVerify" name="人事经理审批" activiti:candidateGroups="hr" activiti:formKey="/flow/leave/approve-hr"></userTask>
<exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway"></exclusiveGateway>
<userTask id="reportBack" name="申请人确认" activiti:assignee="${applyUserId}" activiti:formKey="/flow/leave/confirm">
</userTask>
<endEvent id="endevent1" name="End"></endEvent>
<userTask id="modifyApply" name="调整申请内容" activiti:assignee="${applyUserId}" activiti:formKey="/flow/leave/modify"></userTask>
<exclusiveGateway id="exclusivegateway3" name="Exclusive Gateway"></exclusiveGateway>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="deptLeaderVerify"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="deptLeaderVerify" targetRef="exclusivegateway1"></sequenceFlow>
<sequenceFlow id="flow3" name="同意" sourceRef="exclusivegateway1" targetRef="hrVerify">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptLeaderApproved == 'true'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow4" sourceRef="hrVerify" targetRef="exclusivegateway2"></sequenceFlow>
<sequenceFlow id="flow5" name="同意" sourceRef="exclusivegateway2" targetRef="reportBack">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${hrApproved == 'true'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow6" sourceRef="reportBack" targetRef="endevent1">
<extensionElements>
<activiti:executionListener event="take" delegateExpression="${reportBackEndProcessor}"></activiti:executionListener>
</extensionElements>
</sequenceFlow>
<sequenceFlow id="flow7" name="不同意" sourceRef="exclusivegateway2" targetRef="modifyApply">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${hrApproved == 'false'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow8" name="不同意" sourceRef="exclusivegateway1" targetRef="modifyApply">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptLeaderApproved == 'false'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow9" sourceRef="modifyApply" targetRef="exclusivegateway3"></sequenceFlow>
<sequenceFlow id="flow10" name="调整后继续申请" sourceRef="exclusivegateway3" targetRef="deptLeaderVerify">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${reApply == 'true'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow11" name="取消申请,并设置取消标志" sourceRef="exclusivegateway3" targetRef="endevent1">
<extensionElements>
<activiti:executionListener event="take" delegateExpression="${reportBackEndProcessor}"></activiti:executionListener>
</extensionElements>
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${reApply == 'false'}]]></conditionExpression>
</sequenceFlow>
<textAnnotation id="textannotation1" textFormat="text/plain">
<text>请求被驳回后员工可以选择继续申请,或者取消本次申请</text>
</textAnnotation>
<association id="association1" sourceRef="modifyApply" targetRef="textannotation1"></association>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave-formkey">
<bpmndi:BPMNPlane bpmnElement="leave-formkey" id="BPMNPlane_leave-formkey">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="10.0" y="50.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="deptLeaderVerify" id="BPMNShape_deptLeaderVerify">
<omgdc:Bounds height="55.0" width="105.0" x="90.0" y="40.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
<omgdc:Bounds height="40.0" width="40.0" x="230.0" y="47.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="hrVerify" id="BPMNShape_hrVerify">
<omgdc:Bounds height="55.0" width="105.0" x="310.0" y="40.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2">
<omgdc:Bounds height="40.0" width="40.0" x="470.0" y="47.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="reportBack" id="BPMNShape_reportBack">
<omgdc:Bounds height="55.0" width="105.0" x="580.0" y="40.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="615.0" y="213.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="modifyApply" id="BPMNShape_modifyApply">
<omgdc:Bounds height="55.0" width="105.0" x="198.0" y="120.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="exclusivegateway3" id="BPMNShape_exclusivegateway3">
<omgdc:Bounds height="40.0" width="40.0" x="230.0" y="210.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="textannotation1" id="BPMNShape_textannotation1">
<omgdc:Bounds height="57.0" width="112.0" x="340.0" y="168.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="45.0" y="67.0"></omgdi:waypoint>
<omgdi:waypoint x="90.0" y="67.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="195.0" y="67.0"></omgdi:waypoint>
<omgdi:waypoint x="230.0" y="67.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="270.0" y="67.0"></omgdi:waypoint>
<omgdi:waypoint x="310.0" y="67.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="11.0" width="22.0" x="269.0" y="50.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="415.0" y="67.0"></omgdi:waypoint>
<omgdi:waypoint x="470.0" y="67.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
<omgdi:waypoint x="510.0" y="67.0"></omgdi:waypoint>
<omgdi:waypoint x="580.0" y="67.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="11.0" width="22.0" x="529.0" y="50.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
<omgdi:waypoint x="632.0" y="95.0"></omgdi:waypoint>
<omgdi:waypoint x="632.0" y="213.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
<omgdi:waypoint x="490.0" y="87.0"></omgdi:waypoint>
<omgdi:waypoint x="490.0" y="147.0"></omgdi:waypoint>
<omgdi:waypoint x="303.0" y="147.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="11.0" width="33.0" x="438.0" y="119.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
<omgdi:waypoint x="250.0" y="87.0"></omgdi:waypoint>
<omgdi:waypoint x="250.0" y="120.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="11.0" width="33.0" x="260.0" y="87.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
<omgdi:waypoint x="250.0" y="175.0"></omgdi:waypoint>
<omgdi:waypoint x="250.0" y="210.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10">
<omgdi:waypoint x="230.0" y="230.0"></omgdi:waypoint>
<omgdi:waypoint x="142.0" y="230.0"></omgdi:waypoint>
<omgdi:waypoint x="142.0" y="95.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="11.0" width="77.0" x="159.0" y="210.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11">
<omgdi:waypoint x="270.0" y="230.0"></omgdi:waypoint>
<omgdi:waypoint x="615.0" y="230.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="33.0" width="100.0" x="58.0" y="-37.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="association1" id="BPMNEdge_association1">
<omgdi:waypoint x="303.0" y="147.0"></omgdi:waypoint>
<omgdi:waypoint x="396.0" y="168.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>

View File

@ -0,0 +1,9 @@
import request from '@/utils/request'
export function getStartForm(processKey) {
return request({
url: '/workflow/process/definition/getStartForm?processKey='+processKey,
method: 'get'
})
}

View File

@ -0,0 +1,62 @@
import request from '@/utils/request'
// 创建请假申请
export function createLeave(data) {
return request({
url: '/oa/leave/create',
method: 'post',
data: data
})
}
// 更新请假申请
export function updateLeave(data) {
return request({
url: '/oa/leave/update',
method: 'put',
data: data
})
}
// 删除请假申请
export function deleteLeave(id) {
return request({
url: '/oa/leave/delete?id=' + id,
method: 'delete'
})
}
// 获得请假申请
export function getLeave(id) {
return request({
url: '/oa/leave/get?id=' + id,
method: 'get'
})
}
// 获得请假申请分页
export function getLeavePage(query) {
return request({
url: '/oa/leave/page',
method: 'get',
params: query
})
}
export function createFormKeyLeave(data) {
return request({
url: '/oa/leave/form-key/create',
method: 'post',
data: data
})
}
// 导出请假申请 Excel
export function exportLeaveExcel(query) {
return request({
url: '/oa/leave/export-excel',
method: 'get',
params: query,
responseType: 'blob'
})
}

View File

@ -0,0 +1,83 @@
import request from '@/utils/request'
// 创建请假申请
export function createLeave(data) {
return request({
url: '/oa/leave/create',
method: 'post',
data: data
})
}
// 更新请假申请
export function updateLeave(data) {
return request({
url: '/oa/leave/update',
method: 'put',
data: data
})
}
// 删除请假申请
export function deleteLeave(id) {
return request({
url: '/oa/leave/delete?id=' + id,
method: 'delete'
})
}
// 获得请假申请
export function getLeave(id) {
return request({
url: '/oa/leave/get?id=' + id,
method: 'get'
})
}
// 获得待办任务分页
export function getTodoTaskPage(query) {
return request({
url: '/workflow/task/todo/page',
method: 'get',
params: query
})
}
// 签收任务
export function claimTask(id) {
return request({
url: '/workflow/task/claim?id=' + id,
method: 'get'
})
}
export function completeTask(data) {
return request({
url: '/workflow/task/complete',
method: 'post',
data: data
})
}
export function taskSteps(data) {
return request({
url: '/workflow/task/task-steps',
method: 'post',
data: data
})
}
export function getTaskFormKey(data) {
return request({
url: '/workflow/task/formKey',
method: 'post',
data: data
})
}
export function processHistorySteps(id) {
return request({
url: '/workflow/task/process/history-steps?id='+id,
method: 'get'
})
}

View File

@ -123,6 +123,76 @@ export const constantRoutes = [
meta: { title: '修改生成配置' }
}
]
},
{
path: '/flow',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'leave/apply',
component: (resolve) => require(['@/views/oa/leave/apply/index'], resolve),
name: '请假表单',
meta: { title: '请假表单', icon: 'form' }
}
]
},
{
path: '/flow',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'leave/approve-leader',
component: (resolve) => require(['@/views/oa/leave/approve-leader/index'], resolve),
name: '请假表单-部门领导审批',
meta: { title: '请假表单-部门领导审批', icon: 'form' }
}
]
},
{
path: '/flow',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'leave/approve-hr',
component: (resolve) => require(['@/views/oa/leave/approve-hr/index'], resolve),
name: '请假表单-人事审批',
meta: { title: '请假表单-人事审批', icon: 'form' }
}
]
},
{
path: '/flow',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'leave/confirm',
component: (resolve) => require(['@/views/oa/leave/confirm/index'], resolve),
name: '请假表单-确认',
meta: { title: '请假表单-确认', icon: 'form' }
}
]
},
{
path: '/flow',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'leave/modify',
component: (resolve) => require(['@/views/oa/leave/modify/index'], resolve),
name: '请假表单-修改',
meta: { title: '请假表单-修改', icon: 'form' }
}
]
}
]

View File

@ -30,6 +30,9 @@ export const DICT_TYPE = {
INF_API_ERROR_LOG_PROCESS_STATUS: 'inf_api_error_log_process_status',
TOOL_CODEGEN_TEMPLATE_TYPE: 'tool_codegen_template_type',
OA_LEAVE_STATUS: 'oa_leave_status',
OA_LEAVE_TYPE: 'oa_leave_type'
}
/**

View File

@ -0,0 +1,36 @@
<template>
<div class="app-container">
<el-row>
<el-button type="primary" v-on:click="leave">请假申请</el-button>
</el-row>
</div>
</template>
<script>
import { getStartForm } from "@/api/oa/flow";
export default {
name: "Flow",
components: {
},
data() {
return {
//
form: {},
rules: {
}
};
},
created() {
},
methods: {
leave() {
getStartForm('leave-formkey').then(response => {
const route = response.data;
this.$router.replace(route);
});
}
}
};
</script>

View File

@ -0,0 +1,93 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="开始时间" prop="startTime">
<el-date-picker clearable size="small" v-model="form.startTime" type="date" value-format="timestamp" placeholder="选择开始时间" />
</el-form-item>
<el-form-item label="结束时间" prop="endTime">
<el-date-picker clearable size="small" v-model="form.endTime" type="date" value-format="timestamp" placeholder="选择结束时间" />
</el-form-item>
<el-form-item label="请假类型" prop="leaveType">
<el-select v-model="form.leaveType" placeholder="请选择">
<el-option
v-for="dict in leaveTypeDictData"
:key="parseInt(dict.value)"
:label="dict.label"
:value="parseInt(dict.value)"
/>
</el-select>
</el-form-item>
<el-form-item label="原因" prop="reason">
<el-col :span="10">
<el-input
type="textarea"
:rows="3"
v-model="form.reason"
placeholder="请输入原因" />
</el-col>
</el-form-item>
<el-form-item label="申请时间" prop="applyTime">
<el-date-picker clearable size="small" v-model="form.applyTime" type="date" value-format="timestamp" placeholder="选择申请时间" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm"> </el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { createFormKeyLeave } from "@/api/oa/leave"
import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
export default {
name: "ApplyLeave",
components: {
},
data() {
return {
//
form: {},
//
rules: {
startTime: [{ required: true, message: "开始时间不能为空", trigger: "blur" }],
endTime: [{ required: true, message: "结束时间不能为空", trigger: "blur" }],
applyTime: [{ required: true, message: "申请时间不能为空", trigger: "blur" }],
},
statusFormat(row, column) {
return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
},
leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
};
},
created() {
},
methods: {
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
//
// if (this.form.id != null) {
// updateLeave(this.form).then(response => {
// this.msgSuccess("");
// });
// return;
// }
//
createFormKeyLeave(this.form).then(response => {
this.msgSuccess("新增成功");
this.$store.dispatch('tagsView/delView', this.$route).then(({ visitedViews }) => {
//if (this.isActive(this.$route)) {
this.$router.push({path: '/oa/todo'})
//}
})
});
});
}
}
};
</script>

View File

@ -0,0 +1,190 @@
<template>
<div class="app-container">
<el-tabs type="border-card">
<el-tab-pane label="任务处理">
<el-form ref="form" :model="form" label-width="80px">
<el-row :gutter="20">
<el-col :span="6"><el-form-item label="申请人" >{{form.userId}}</el-form-item></el-col>
<el-col :span="6">
<el-form-item label="请假类型" prop="leaveType">
{{ getDictDataLabel(DICT_TYPE.OA_LEAVE_TYPE, form.leaveType) }}
</el-form-item>
</el-col>
<el-col :span="6"><el-form-item label="原因" prop="reason">{{form.reason}}</el-form-item></el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="6"><el-form-item label="开始时间" >{{ parseTime(form.startTime) }}</el-form-item></el-col>
<el-col :span="6"><el-form-item label="结束时间" prop="endTime">{{ parseTime(form.endTime) }}</el-form-item></el-col>
<el-form-item label="申请时间" prop="applyTime">{{ parseTime(form.applyTime) }}</el-form-item>
</el-row>
</el-form>
<el-divider></el-divider>
<el-form ref="taskForm" :model="leaveApprove" :rules="rules" label-width="80px">
<el-form-item label="是否同意" prop="approved">
<el-select v-model="leaveApprove.approved" placeholder="是否同意" v-on:change="approveChange">
<el-option
v-for="dict in approvedData"
:key="parseInt(dict.value)"
:label="dict.label"
:value="parseInt(dict.value)"
/>
</el-select>
</el-form-item>
<el-form-item label="处理意见" prop="comment">
<el-col :span="11">
<el-input
type="textarea"
:rows="3"
v-model="leaveApprove.comment">
</el-input>
</el-col>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm"> </el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="历史跟踪">
<el-steps :active="stepActive" simple finish-status="success">
<el-step :title="stepTitle(item)" icon="el-icon-edit" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
<br/>
<el-steps direction="vertical" :active="stepActive" space="65px">
<el-step :title="stepTitle(item)" :description="stepDes(item)" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
</el-tab-pane>
<el-tab-pane label="流程图">流程图-TODO</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import { getLeave } from "@/api/oa/leave"
import { completeTask,taskSteps } from "@/api/oa/todo";
import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
export default {
name: "HrApproveLeave",
components: {
},
data() {
return {
//
form: {},
//
rules: {
comment: [{ required: true, message: "意见不能为空", trigger: "blur" }]
},
handleTask: {
historyTask:[]
},
leaveApprove: {
approved : 1,
variables: {},
taskId: "",
comment: "同意"
},
approvedData: [
{
value: 1,
label: '同意'
},
{
value: 0,
label: '不同意'
}
],
statusFormat(row, column) {
return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
},
leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
};
},
computed:{
stepActive: function () {
let idx = 0;
for(let i=0; i<this.handleTask.historyTask.length; i++){
if(this.handleTask.historyTask[i].status === 1){
idx= idx+1;
}else{
break;
}
}
return idx;
},
stepTitle() {
return function (item) {
let name = item.stepName;
if (item.status === 1) {
name += '(已完成)'
}
if (item.status === 0) {
name += '(进行中)'
}
return name;
}
},
stepDes(){
return function (item) {
let desc = "";
if (item.status === 1) {
desc+="审批人:["+ item.assignee +"] 审批意见: [" + item.comment + "] 审批时间: " + this.parseTime(item.endTime);
}
return desc;
}
}
},
created() {
const businessKey = this.$route.query.businessKey;
const taskId = this.$route.query.taskId;
this.leaveApprove.taskId = taskId;
this.getForm(businessKey);
},
methods: {
/** 提交按钮 */
submitForm() {
this.$refs["taskForm"].validate(valid => {
if (!valid) {
return;
}
if (this.leaveApprove.approved == 1) {
this.leaveApprove.variables["hrApproved"] = true;
}
if (this.leaveApprove.approved == 0) {
this.leaveApprove.variables["hrApproved"] = false;
}
completeTask(this.leaveApprove).then(response => {
this.msgSuccess("执行任务成功");
this.$store.dispatch('tagsView/delView', this.$route).then(({ visitedViews }) => {
//if (this.isActive(this.$route)) {
this.$router.push({path: '/oa/todo'})
//}
})
})
});
},
getForm(id){
getLeave(id).then(response => {
this.form = response.data;
});
const data = {
taskId : this.leaveApprove.taskId,
businessKey: id,
}
taskSteps(data).then(response => {
this.handleTask = response.data;
});
},
approveChange(){
if (this.leaveApprove.approved === 1) {
this.leaveApprove.comment = "同意"
}
if(this.leaveApprove.approved === 0){
this.leaveApprove.comment = "不同意"
}
}
}
};
</script>

View File

@ -0,0 +1,190 @@
<template>
<div class="app-container">
<el-tabs type="border-card">
<el-tab-pane label="任务处理">
<el-form ref="form" :model="form" label-width="80px">
<el-row :gutter="20">
<el-col :span="6"><el-form-item label="申请人" >{{form.userId}}</el-form-item></el-col>
<el-col :span="6">
<el-form-item label="请假类型" prop="leaveType">
{{ getDictDataLabel(DICT_TYPE.OA_LEAVE_TYPE, form.leaveType) }}
</el-form-item>
</el-col>
<el-col :span="6"><el-form-item label="原因" prop="reason">{{form.reason}}</el-form-item></el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="6"><el-form-item label="开始时间" >{{ parseTime(form.startTime) }}</el-form-item></el-col>
<el-col :span="6"><el-form-item label="结束时间" prop="endTime">{{ parseTime(form.endTime) }}</el-form-item></el-col>
<el-form-item label="申请时间" prop="applyTime">{{ parseTime(form.applyTime) }}</el-form-item>
</el-row>
</el-form>
<el-divider></el-divider>
<el-form ref="taskForm" :model="leaveApprove" :rules="rules" label-width="80px">
<el-form-item label="是否同意" prop="approved">
<el-select v-model="leaveApprove.approved" placeholder="是否同意" v-on:change="approveChange">
<el-option
v-for="dict in approvedData"
:key="parseInt(dict.value)"
:label="dict.label"
:value="parseInt(dict.value)"
/>
</el-select>
</el-form-item>
<el-form-item label="处理意见" prop="comment">
<el-col :span="11">
<el-input
type="textarea"
:rows="3"
v-model="leaveApprove.comment">
</el-input>
</el-col>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm"> </el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="历史跟踪">
<el-steps :active="stepActive" simple finish-status="success">
<el-step :title="stepTitle(item)" icon="el-icon-edit" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
<br/>
<el-steps direction="vertical" :active="stepActive" space="65px">
<el-step :title="stepTitle(item)" :description="stepDes(item)" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
</el-tab-pane>
<el-tab-pane label="流程图">流程图-TODO</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import { getLeave } from "@/api/oa/leave"
import { completeTask,taskSteps } from "@/api/oa/todo";
import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
export default {
name: "ApproveLeaderLeave",
components: {
},
data() {
return {
//
form: {},
//
rules: {
comment: [{ required: true, message: "意见不能为空", trigger: "blur" }]
},
handleTask: {
historyTask:[]
},
leaveApprove: {
approved : 1,
variables: {},
taskId: "",
comment: "同意"
},
approvedData: [
{
value: 1,
label: '同意'
},
{
value: 0,
label: '不同意'
}
],
statusFormat(row, column) {
return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
},
leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
};
},
created() {
const businessKey = this.$route.query.businessKey;
const taskId = this.$route.query.taskId;
this.leaveApprove.taskId = taskId;
this.getForm(businessKey);
},
computed:{
stepActive: function () {
let idx = 0;
for(let i=0; i<this.handleTask.historyTask.length; i++){
if(this.handleTask.historyTask[i].status === 1){
idx= idx+1;
}else{
break;
}
}
return idx;
},
stepTitle() {
return function (item) {
let name = item.stepName;
if (item.status === 1) {
name += '(已完成)'
}
if (item.status === 0) {
name += '(进行中)'
}
return name;
}
},
stepDes(){
return function (item) {
let desc = "";
if (item.status === 1) {
desc+="审批人:["+ item.assignee +"] 审批意见: [" + item.comment + "] 审批时间: " + this.parseTime(item.endTime);
}
return desc;
}
}
},
methods: {
/** 提交按钮 */
submitForm() {
this.$refs["taskForm"].validate(valid => {
if (!valid) {
return;
}
if (this.leaveApprove.approved == 1) {
this.leaveApprove.variables["deptLeaderApproved"] = true;
}
if (this.leaveApprove.approved == 0) {
this.leaveApprove.variables["deptLeaderApproved"] = false;
}
completeTask(this.leaveApprove).then(response => {
this.msgSuccess("执行任务成功");
this.$store.dispatch('tagsView/delView', this.$route).then(({ visitedViews }) => {
//if (this.isActive(this.$route)) {
this.$router.push({path: '/oa/todo'})
//}
})
});
});
},
getForm(id){
getLeave(id).then(response => {
this.form = response.data;
});
const data = {
taskId : this.leaveApprove.taskId,
businessKey: id,
}
taskSteps(data).then(response => {
this.handleTask = response.data;
});
},
approveChange(){
if (this.leaveApprove.approved === 1) {
this.leaveApprove.comment = "同意"
}
if(this.leaveApprove.approved === 0){
this.leaveApprove.comment = "不同意"
}
}
}
};
</script>

View File

@ -0,0 +1,137 @@
<template>
<div class="app-container">
<el-tabs type="border-card">
<el-tab-pane label="任务处理">
<el-form ref="form" :model="form" label-width="80px">
<el-row :gutter="20">
<el-col :span="6"><el-form-item label="申请人" >{{form.userId}}</el-form-item></el-col>
<el-col :span="6">
<el-form-item label="请假类型" prop="leaveType">
{{ getDictDataLabel(DICT_TYPE.OA_LEAVE_TYPE, form.leaveType) }}
</el-form-item>
</el-col>
<el-col :span="6"><el-form-item label="原因" prop="reason">{{form.reason}}</el-form-item></el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="6"><el-form-item label="开始时间" >{{ parseTime(form.startTime) }}</el-form-item></el-col>
<el-col :span="6"><el-form-item label="结束时间" prop="endTime">{{ parseTime(form.endTime) }}</el-form-item></el-col>
<el-form-item label="申请时间" prop="applyTime">{{ parseTime(form.applyTime) }}</el-form-item>
</el-row>
<el-row :gutter="20">
<el-col :span="6"><el-button type="primary" @click="submitForm"> </el-button></el-col>
</el-row>
</el-form>
</el-tab-pane>
<el-tab-pane label="历史跟踪">
<el-steps :active="stepActive" simple finish-status="success">
<el-step :title="stepTitle(item)" icon="el-icon-edit" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
<br/>
<el-steps direction="vertical" :active="stepActive" space="65px">
<el-step :title="stepTitle(item)" :description="stepDes(item)" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
</el-tab-pane>
<el-tab-pane label="流程图">流程图-TODO</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import { getLeave } from "@/api/oa/leave"
import { completeTask,taskSteps } from "@/api/oa/todo";
import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
export default {
name: "ConfirmLeave",
components: {
},
data() {
return {
//
form: {},
//
rules: {
},
handleTask: {
historyTask:[]
},
leaveApprove: {
variables: {},
taskId: "",
comment: ""
},
statusFormat(row, column) {
return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
},
leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
};
},
created() {
const businessKey = this.$route.query.businessKey;
const taskId = this.$route.query.taskId;
this.leaveApprove.taskId = taskId;
this.getForm(businessKey);
},
computed:{
stepActive: function () {
let idx = 0;
for(let i=0; i<this.handleTask.historyTask.length; i++){
if(this.handleTask.historyTask[i].status === 1){
idx= idx+1;
}else{
break;
}
}
return idx;
},
stepTitle() {
return function (item) {
let name = item.stepName;
if (item.status === 1) {
name += '(已完成)'
}
if (item.status === 0) {
name += '(进行中)'
}
return name;
}
},
stepDes(){
return function (item) {
let desc = "";
if (item.status === 1) {
desc+="审批人:["+ item.assignee +"] 审批意见: [" + item.comment + "] 审批时间: " + this.parseTime(item.endTime);
}
return desc;
}
}
},
methods: {
/** 提交按钮 */
submitForm() {
completeTask(this.leaveApprove).then(response => {
this.msgSuccess("执行任务成功");
this.$store.dispatch('tagsView/delView', this.$route).then(({ visitedViews }) => {
//if (this.isActive(this.$route)) {
this.$router.push({path: '/oa/todo'})
//}
})
})
},
getForm(id){
getLeave(id).then(response => {
this.form = response.data;
});
const data = {
taskId : this.leaveApprove.taskId,
businessKey: id,
}
taskSteps(data).then(response => {
this.handleTask = response.data;
});
}
}
};
</script>

View File

@ -0,0 +1,377 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="流程id" prop="processInstanceId">
<el-input v-model="queryParams.processInstanceId" placeholder="请输入流程id" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态">
<el-option
v-for="dict in leaveStatusData"
:key="parseInt(dict.value)"
:label="dict.label"
:value="parseInt(dict.value)"
/>
</el-select>
</el-form-item>
<el-form-item label="开始时间">
<el-date-picker v-model="dateRangeStartTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item label="结束时间">
<el-date-picker v-model="dateRangeEndTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item label="请假类型" prop="leaveType">
<el-select v-model="queryParams.leaveType" placeholder="请选择请假类型">
<el-option
v-for="dict in leaveTypeDictData"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="原因" prop="reason">
<el-input v-model="queryParams.reason" placeholder="请输入原因" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="申请时间">
<el-date-picker v-model="dateRangeApplyTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="请假表单主键" align="center" prop="id" />
<el-table-column label="状态" align="center" prop="status" :formatter="statusFormat" />
<el-table-column label="申请人id" align="center" prop="userId" />
<el-table-column label="开始时间" align="center" prop="startTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.startTime) }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="endTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="请假类型" align="center" prop="leaveType" :formatter="leaveTypeFormat" />
<el-table-column label="原因" align="center" prop="reason" />
<el-table-column label="申请时间" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleStep(scope.row)">审批进度</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="开始时间" prop="startTime">
<el-date-picker clearable size="small" v-model="form.startTime" type="date" value-format="timestamp" placeholder="选择开始时间" />
</el-form-item>
<el-form-item label="结束时间" prop="endTime">
<el-date-picker clearable size="small" v-model="form.endTime" type="date" value-format="timestamp" placeholder="选择结束时间" />
</el-form-item>
<el-form-item label="请假类型" prop="leaveType">
<el-select v-model="form.leaveType" placeholder="请选择">
<el-option
v-for="dict in leaveTypeDictData"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="原因" prop="reason">
<el-input v-model="form.reason" placeholder="请输入原因" />
</el-form-item>
<el-form-item label="申请时间" prop="applyTime">
<el-date-picker clearable size="small" v-model="form.applyTime" type="date" value-format="timestamp" placeholder="选择申请时间" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<el-dialog :title="title" :visible.sync="dialogDetailVisible" width="500px" append-to-body>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="状态" >
{{ getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, form.status) }}
</el-form-item>
<el-form-item label="申请人id" >{{form.userId}}</el-form-item>
<el-form-item label="开始时间" >{{ parseTime(form.startTime) }}</el-form-item>
<el-form-item label="结束时间" prop="endTime">{{ parseTime(form.endTime) }}</el-form-item>
<el-form-item label="请假类型" prop="leaveType">
{{ getDictDataLabel(DICT_TYPE.OA_LEAVE_TYPE, form.leaveType) }}
</el-form-item>
<el-form-item label="原因" prop="reason">{{form.reason}}</el-form-item>
<el-form-item label="申请时间" prop="applyTime">{{ parseTime(form.applyTime) }}</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogDetailVisible = false"> </el-button>
<el-button @click="dialogDetailVisible = false"> </el-button>
</div>
</el-dialog>
<el-dialog :title="title" :visible.sync="dialogStepsVisible" width="600px" append-to-body>
<el-steps :active="stepActive" finish-status="success" >
<el-step :title="stepTitle(item)" :description="' 办理人:' + item.assignee " icon="el-icon-edit" v-for="(item) in handleTask.historyTask"></el-step>
</el-steps>
<br/>
<el-steps direction="vertical" :active="stepActive">
<el-step :title="stepTitle(item)" :description="stepDes(item)" v-for="(item) in handleTask.historyTask"></el-step>
</el-steps>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogStepsVisible = false"> </el-button>
<el-button @click="dialogStepsVisible = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { createLeave, updateLeave, deleteLeave, getLeave, getLeavePage, exportLeaveExcel } from "@/api/oa/leave"
import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
import { processHistorySteps } from '@/api/oa/todo'
export default {
name: "Leave",
components: {
},
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
title: "",
//
open: false,
//
dialogDetailVisible: false,
//
dialogStepsVisible: false,
dateRangeStartTime: [],
dateRangeEndTime: [],
dateRangeApplyTime: [],
//
queryParams: {
pageNo: 1,
pageSize: 10,
processInstanceId: null,
status: null,
userId: null,
leaveType: null,
reason: null,
},
//
form: {},
handleTask: {
historyTask:[],
taskVariable: "",
formObject: {}
},
steps:[{
stepName:"步骤一"
}],
//
rules: {
startTime: [{ required: true, message: "开始时间不能为空", trigger: "blur" }],
endTime: [{ required: true, message: "结束时间不能为空", trigger: "blur" }],
applyTime: [{ required: true, message: "申请时间不能为空", trigger: "blur" }],
},
statusFormat(row, column) {
return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
},
leaveTypeFormat(row, column) {
return getDictDataLabel(DICT_TYPE.OA_LEAVE_TYPE, row.leaveType)
},
leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
};
},
created() {
this.getList();
},
computed: {
stepActive: function () {
let idx = 0;
for (let i = 0; i < this.handleTask.historyTask.length; i++) {
if (this.handleTask.historyTask[i].status === 1) {
idx = idx + 1;
} else {
break;
}
}
return idx;
},
stepTitle() {
return function (item) {
let name = item.stepName;
if (item.status === 1) {
name += '(已完成)'
}
if (item.status === 0) {
name += '(进行中)'
}
return name;
}
},
stepDes() {
return function (item) {
let desc = "";
if (item.status === 1) {
desc += "审批人:[" + item.assignee + "] 审批意见: [" + item.comment + "] 审批时间: " + this.parseTime(item.endTime);
}
return desc;
}
}
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeStartTime, 'startTime');
this.addBeginAndEndTime(params, this.dateRangeEndTime, 'endTime');
this.addBeginAndEndTime(params, this.dateRangeApplyTime, 'applyTime');
//
getLeavePage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
processInstanceId: undefined,
status: undefined,
userId: undefined,
startTime: undefined,
endTime: undefined,
leaveType: undefined,
reason: undefined,
applyTime: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeStartTime = [];
this.dateRangeEndTime = [];
this.dateRangeApplyTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加请假申请";
},
/** 详情按钮操作 */
handleDetail(row) {
this.reset();
const id = row.id;
getLeave(id).then(response => {
this.form = response.data;
this.dialogDetailVisible = true
this.title = "请假详情";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
//
if (this.form.id != null) {
updateLeave(this.form).then(response => {
this.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
//
createLeave(this.form).then(response => {
this.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 审批进度 */
handleStep(row) {
const id = row.processInstanceId;
processHistorySteps(id).then(response => {
this.handleTask.historyTask = response.data;
this.dialogStepsVisible = true
this.title = "审批进度";
});
},
/** 导出按钮操作 */
handleExport() {
//
let params = {...this.queryParams};
params.pageNo = undefined;
params.pageSize = undefined;
this.addBeginAndEndTime(params, this.dateRangeStartTime, 'startTime');
this.addBeginAndEndTime(params, this.dateRangeEndTime, 'endTime');
this.addBeginAndEndTime(params, this.dateRangeApplyTime, 'applyTime');
//
this.$confirm('是否确认导出所有请假申请数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return exportLeaveExcel(params);
}).then(response => {
this.downloadExcel(response, '请假申请.xls');
})
}
}
};
</script>

View File

@ -0,0 +1,211 @@
<template>
<div class="app-container">
<el-tabs type="border-card">
<el-tab-pane label="任务处理">
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-row>
<el-col :span="15">
<el-form-item label="是否调整申请" prop="reApply">
<el-select v-model="reApplySelect" placeholder="是否调整申请" v-on:change="reApplyChange">
<el-option
v-for="dict in reApplyData"
:key="parseInt(dict.value)"
:label="dict.label"
:value="parseInt(dict.value)"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" v-show="modifyShow">
<el-col :span="8"><el-form-item label="申请人" >{{form.userId}}</el-form-item></el-col>
<el-col :span="8">
<el-form-item label="申请时间" prop="applyTime">{{ parseTime(form.applyTime) }}</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" v-show="modifyShow">
<el-col :span="8">
<el-form-item label="开始时间" prop="startTime">
<el-date-picker clearable size="small" v-model="form.startTime" type="date" value-format="timestamp" placeholder="选择开始时间" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="结束时间" prop="endTime">
<el-date-picker clearable size="small" v-model="form.endTime" type="date" value-format="timestamp" placeholder="选择结束时间" />
</el-form-item>
</el-col>
</el-row>
<el-row v-show="modifyShow" >
<el-col :span="8">
<el-form-item label="请假类型" prop="leaveType">
<el-select v-model="form.leaveType" placeholder="请选择">
<el-option
v-for="dict in leaveTypeDictData"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row v-show="modifyShow">
<el-col :span="15">
<el-form-item label="原因" prop="reason">
<el-input
type="textarea"
:rows="3"
v-model="form.reason"
placeholder="请输入原因" />
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" @click="submitForm"> </el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="历史跟踪">
<el-steps :active="stepActive" simple finish-status="success">
<el-step :title="stepTitle(item)" icon="el-icon-edit" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
<br/>
<el-steps direction="vertical" :active="stepActive" space="65px">
<el-step :title="stepTitle(item)" :description="stepDes(item)" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
</el-tab-pane>
<el-tab-pane label="流程图">流程图-TODO</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import { getLeave,updateLeave } from "@/api/oa/leave"
import { taskSteps } from "@/api/oa/todo"
import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
export default {
name: "HrApproveLeave",
components: {
},
data() {
return {
//
form: {},
//
rules: {
},
handleTask: {
historyTask:[]
},
modifyShow: true,
reApplySelect: 1,
reApplyData: [
{
value: 0,
label: '取消申请'
},
{
value: 1,
label: '继续申请'
}
],
statusFormat(row, column) {
return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
},
leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
};
},
mounted() {
const businessKey = this.$route.query.businessKey;
const taskId = this.$route.query.taskId;
this.getForm(businessKey,taskId);
},
computed:{
stepActive: function () {
let idx = 0;
for(let i=0; i<this.handleTask.historyTask.length; i++){
if(this.handleTask.historyTask[i].status === 1){
idx= idx+1;
}else{
break;
}
}
return idx;
},
stepTitle() {
return function (item) {
let name = item.stepName;
if (item.status === 1) {
name += '(已完成)'
}
if (item.status === 0) {
name += '(进行中)'
}
return name;
}
},
stepDes(){
return function (item) {
let desc = "";
if (item.status === 1) {
desc+="审批人:["+ item.assignee +"] 审批意见: [" + item.comment + "] 审批时间: " + this.parseTime(item.endTime);
}
return desc;
}
}
},
methods: {
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
if (this.reApplySelect === 1) {
this.form.variables["reApply"] = true;
this.form.comment = '调整请假申请';
}
if (this.reApplySelect === 0) {
this.form.variables["reApply"] = false;
this.form.comment = '取消请假申请';
}
updateLeave(this.form).then(response => {
this.msgSuccess("修改成功");
this.$store.dispatch('tagsView/delView', this.$route).then(({ visitedViews }) => {
//if (this.isActive(this.$route)) {
this.$router.push({path: '/oa/todo'})
//}
})
});
});
},
getForm(id, taskId){
getLeave(id).then(response => {
this.form = response.data;
this.form.taskId = taskId;
this.form.variables = {};
});
const data = {
taskId : taskId,
businessKey: id,
}
taskSteps(data).then(response => {
this.handleTask = response.data;
});
},
reApplyChange(){
if (this.reApplySelect === 1) {
this.modifyShow = true;
}
if (this.reApplySelect === 0) {
this.modifyShow = false;
}
}
}
};
</script>

View File

@ -0,0 +1,303 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态">
<el-option
v-for="dict in leaveStatusData"
:key="parseInt(dict.value)"
:label="dict.label"
:value="parseInt(dict.value)"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="任务Id" align="center" prop="id" />
<el-table-column label="流程名称" align="center" prop="processName" />
<el-table-column label="任务状态" align="center" :formatter="statusFormat" prop="status" />
<!-- <el-table-column label="申请人id" align="center" prop="userId" />-->
<!-- <el-table-column label="开始时间" align="center" prop="startTime" width="180">-->
<!-- <template slot-scope="scope">-->
<!-- <span>{{ parseTime(scope.row.startTime) }}</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column label="结束时间" align="center" prop="endTime" width="180">-->
<!-- <template slot-scope="scope">-->
<!-- <span>{{ parseTime(scope.row.endTime) }}</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column label="请假类型" align="center" prop="leaveType" />-->
<!-- <el-table-column label="原因" align="center" prop="reason" />-->
<!-- <el-table-column label="申请时间" align="center" prop="applyTime" width="180">-->
<!-- <template slot-scope="scope">-->
<!-- <span>{{ parseTime(scope.row.applyTime) }}</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" v-if="scope.row.status == 1" @click="handleClaim(scope.row)">签收</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" v-if="scope.row.status == 2" @click="getTaskFormKey(scope.row)">办理</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
<el-tabs tab-position="left" style="height: 500px;">
<el-tab-pane label="详情">
<el-form ref="form" :model="handleTask.formObject" label-width="80px">
<el-form-item label="状态" >
{{ getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, handleTask.formObject.status) }}
</el-form-item>
<el-form-item label="申请人id" >{{handleTask.formObject.userId}}</el-form-item>
<el-form-item label="开始时间" >{{ parseTime(handleTask.formObject.startTime) }}</el-form-item>
<el-form-item label="结束时间" prop="endTime">{{ parseTime(handleTask.formObject.endTime) }}</el-form-item>
<el-form-item label="请假类型" prop="leaveType">
{{ getDictDataLabel(DICT_TYPE.OA_LEAVE_TYPE, handleTask.formObject.leaveType) }}
</el-form-item>
<el-form-item label="原因" prop="reason">{{handleTask.formObject.reason}}</el-form-item>
<el-form-item label="申请时间" prop="applyTime">{{ parseTime(handleTask.formObject.applyTime) }}</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="任务处理">
<el-steps :active="handleTask.historyTask.length-1" simple finish-status="success">
<el-step :title="item.stepName" icon="el-icon-edit" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
<br/>
<el-steps direction="vertical" :active="handleTask.historyTask.length-1" finish-status="success" space="60px">
<el-step :title="item.stepName" :description="item.comment" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
<br/>
<el-form ref="taskForm" :model="task" label-width="80px" v-show="handleTask.taskVariable !=''">
<el-form-item label="处理意见" prop="approved">
<el-select v-model="task.approved" placeholder="处理意见">
<el-option
v-for="dict in approvedData"
:key="parseInt(dict.value)"
:label="dict.label"
:value="parseInt(dict.value)"
/>
</el-select>
</el-form-item>
<el-input
type="textarea"
:rows="2"
v-model="task.comment">
</el-input>
</el-form>
<br/>
<el-button type="primary" @click="submitTask">提交</el-button>
</el-tab-pane>
</el-tabs>
</el-dialog>
</div>
</template>
<script>
import { completeTask, taskSteps, getTaskFormKey,deleteLeave, getLeave, getTodoTaskPage, claimTask } from "@/api/oa/todo";
import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
export default {
name: "Todo",
components: {
},
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNo: 1,
pageSize: 10
},
//
form: {},
handleTask: {
historyTask:[{
stepName:"步骤一"
}
],
taskVariable: "",
formObject: {}
},
steps:[{
stepName:"步骤一"
}],
task: {
approved : 1,
variables: {},
taskId: undefined,
comment: ""
},
rules: {
},
leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS),
approvedData: [
{
value: 1,
label: '同意'
},
{
value: 0,
label: '不同意'
}
]
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
let params = {...this.queryParams};
//
getTodoTaskPage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
processInstanceId: undefined,
status: undefined,
userId: undefined,
startTime: undefined,
endTime: undefined,
leaveType: undefined,
reason: undefined,
applyTime: undefined,
};
this.resetForm("form");
},
statusFormat(row, column) {
return row.status == 1 ? "未签收" : "已签收";
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeStartTime = [];
this.dateRangeEndTime = [];
this.dateRangeApplyTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
getTaskFormKey(row) {
const taskId = row.id;
const data = {
taskId : taskId
}
getTaskFormKey(data).then(response => {
const resp = response.data;
const path = resp.formKey;
const taskId = resp.id;
const businessKey = resp.businessKey;
const route = {
path: path,
query: {
businessKey: businessKey,
taskId:taskId
}
}
this.$router.replace(route);
});
},
handleLeaveApprove(row) {
this.reset();
const businessKey = row.businessKey;
const taskId = row.id;
const processKey = row.processKey;
const data = {
taskId : taskId,
businessKey: businessKey,
processKey: processKey
}
taskSteps(data).then(response => {
this.form = {};
this.handleTask = response.data;
this.task.taskId = taskId;
this.open = true;
this.title = "任务办理";
});
},
/** 任务签收操作 */
handleClaim(row) {
this.reset();
const id = row.id;
claimTask(id).then(() => {
this.getList();
this.msgSuccess("签收成功");
});
},
/** 提交任务 */
submitTask() {
const taskVariableName = this.handleTask.taskVariable;
if (taskVariableName != "") {
if (this.task.approved == 1) {
this.task.variables[taskVariableName] = true;
}
if (this.task.approved == 0) {
this.task.variables[taskVariableName] = false;
}
}
completeTask(this.task).then(response => {
this.msgSuccess("执行任务成功");
this.open = false;
this.getList();
})
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$confirm('是否确认删除请假申请编号为"' + id + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return deleteLeave(id);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}
}
};
</script>

View File

@ -438,6 +438,15 @@
<version>${justauth.version}</version>
</dependency>
<!-- activiti begin -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-activiti</artifactId>
<version>${revision}</version>
</dependency>
<!-- activiti end -->
</dependencies>
</dependencyManagement>

View File

@ -28,6 +28,7 @@
<module>yudao-spring-boot-starter-biz-operatelog</module>
<module>yudao-spring-boot-starter-biz-dict</module>
<module>yudao-spring-boot-starter-biz-sms</module>
<module>yudao-spring-boot-starter-activiti</module>
<module>yudao-spring-boot-starter-biz-pay</module>
<module>yudao-spring-boot-starter-biz-weixin</module>
<module>yudao-spring-boot-starter-extension</module>

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-framework</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-spring-boot-starter-activiti</artifactId>
<packaging>jar</packaging>
<name>${artifactId}</name>
<description>Activiti 拓展</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<activiti.version>7.1.0.M6</activiti.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.activiti.dependencies</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>${activiti.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-common</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<optional>true</optional>
</dependency>
<!--使用mybatis plus需排除掉mybatis-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>${activiti.version}</version>
<exclusions>
<exclusion>
<groupId>de.odysseus.juel</groupId>
<artifactId>juel-api</artifactId>
</exclusion>
<exclusion>
<groupId>de.odysseus.juel</groupId>
<artifactId>juel-spi</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<artifactId>el-api</artifactId>
<groupId>javax.el</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.framework.activiti.config;
import org.activiti.api.runtime.shared.identity.UserGroupManager;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Configuration
public class YudaoActivitiConfiguration {
@Component
public static class SqlSessionFactoryProcessEngineConfigurationConfigurer
implements ProcessEngineConfigurationConfigurer {
private final SqlSessionFactory sqlSessionFactory;
public SqlSessionFactoryProcessEngineConfigurationConfigurer(SqlSessionFactory sessionFactory) {
this.sqlSessionFactory = sessionFactory;
}
@Override
public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
springProcessEngineConfiguration.setSqlSessionFactory(sqlSessionFactory);
}
}
}

View File

@ -0,0 +1 @@
package cn.iocoder.yudao.framework.activiti;

View File

@ -44,6 +44,19 @@
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- TODO 芋艿: -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>7.1.0.M6</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -5,12 +5,10 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
/**
* 登录用户信息
@ -56,6 +54,18 @@ public class LoginUser implements UserDetails {
*/
private Integer status;
/**
* 所属岗位
*/
private Set<Long> postIds;
/**
* group 目前指岗位代替
*/
private List<String> groups;
// TODO @芋艿怎么去掉 deptId
@Override
@ -65,7 +75,6 @@ public class LoginUser implements UserDetails {
}
@Override
@JsonIgnore
public String getUsername() {
return username;
}
@ -79,7 +88,9 @@ public class LoginUser implements UserDetails {
@Override
@JsonIgnore// 避免序列化
public Collection<? extends GrantedAuthority> getAuthorities() {
return new HashSet<>();
List<GrantedAuthority> list = new ArrayList<>(1);
list.add(new SimpleGrantedAuthority("ROLE_ACTIVITI_USER"));
return list;
}
@Override

View File

@ -90,14 +90,17 @@ public class SecurityFrameworkUtils {
public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
// 创建 UsernamePasswordAuthenticationToken 对象
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
loginUser, null, null);
loginUser, null, loginUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 设置到上下文
//何时调用 SecurityContextHolder.clearContext. spring security filter 应该会调用 clearContext
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
// 额外设置到 request 用于 ApiAccessLogFilter 可以获取到用户编号
// 原因是Spring Security Filter ApiAccessLogFilter 后面在它记录访问日志时线上上下文已经没有用户编号等信息
WebFrameworkUtils.setLoginUserId(request, loginUser.getId());
WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType());
// TODO @jason使用 userId 会不会更合适哈
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(loginUser.getUsername());
}
}

View File

@ -6,6 +6,17 @@
## [v1.3.0] 待定
* 工作流
* 修改表单为外置表单
* 新增菜单流程申请
* 请假流程如下
1. 请假人 在流程申请, 点击请假申请,填写表单提交
2. 部门领导目前写死admin) 登陆,待办任务中 点击办理, 进行审批
3. hr 登陆用户名hradmin 密码:123456) 待办任务中 点击办理, 进行审批
4. 请假人登陆, 待办任务中, 确认
5. 流程结束
* 请假查询中,可以查询本人的请假申请, 和进度
* 流程跟踪图 待实现
## [v1.2.0] 进行中