فهرست منبع

!65 完成流程图的高亮、OA 请假的接入
Merge pull request !65 from 芋道源码/feature/activiti

芋道源码 3 سال پیش
والد
کامیت
9c32ba400c
100فایلهای تغییر یافته به همراه2106 افزوده شده و 1260 حذف شده
  1. 59 27
      README.md
  2. 1 1
      sql/activiti.sql
  3. 8 8
      sql/ruoyi-vue-pro.sql
  4. 1 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/BpmFormController.java
  5. 3 2
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/BpmProcessDefinitionController.java
  6. 14 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/BpmTaskAssignRuleController.java
  7. 1 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/BpmUserGroupController.java
  8. 1 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/form/BpmFormRespVO.java
  9. 1 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/group/BpmUserGroupSimpleRespVO.java
  10. 0 3
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/model/BpmModelUpdateReqVO.java
  11. 17 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/process/BpmProcessDefinitionRespVO.java
  12. 11 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/rule/BpmTaskAssignRuleBaseVO.java
  13. 11 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/rule/BpmTaskAssignRuleCreateReqVO.java
  14. 12 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/rule/BpmTaskAssignRuleRespVO.java
  15. 10 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/rule/BpmTaskAssignRuleUpdateReqVO.java
  16. 12 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/BpmOALeaveController.http
  17. 63 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/BpmOALeaveController.java
  18. 0 112
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/OALeaveController.java
  19. 6 21
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/BpmOALeaveBaseVO.java
  20. 21 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/BpmOALeaveCreateReqVO.java
  21. 34 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/BpmOALeavePageReqVO.java
  22. 32 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/BpmOALeaveRespVO.java
  23. 0 25
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveApplyMembersVO.java
  24. 0 23
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveCreateReqVO.java
  25. 0 44
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveExcelVO.java
  26. 0 54
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveExportReqVO.java
  27. 0 56
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeavePageReqVO.java
  28. 0 15
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveRespVO.java
  29. 0 32
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveUpdateReqVO.java
  30. 55 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmActivityController.java
  31. 2 2
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmProcessInstanceController.http
  32. 15 7
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmProcessInstanceController.java
  33. 5 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmTaskController.http
  34. 22 21
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmTaskController.java
  35. 26 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/activity/BpmActivityRespVO.java
  36. 97 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/instance/BpmProcessInstanceRespVO.java
  37. 5 44
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/BpmTaskDonePageItemRespVO.java
  38. 41 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/BpmTaskRespVO.java
  39. 25 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/BpmTaskUpdateAssigneeReqVO.java
  40. 0 24
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/FileResp.java
  41. 0 19
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/TaskHandleVO.java
  42. 0 15
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/TaskQueryReqVO.java
  43. 0 24
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/TaskStepVO.java
  44. 9 4
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/definition/BpmModelConvert.java
  45. 11 10
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/definition/BpmProcessDefinitionConvert.java
  46. 57 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/message/BpmMessageConvert.java
  47. 30 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/oa/BpmOALeaveConvert.java
  48. 0 37
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/oa/OALeaveConvert.java
  49. 33 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmActivityConvert.java
  50. 66 7
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmProcessInstanceConvert.java
  51. 69 46
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmTaskConvert.java
  52. 0 55
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/definition/BpmFormDataDO.java
  53. 40 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/definition/BpmProcessDefinitionExtDO.java
  54. 74 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/leave/BpmOALeaveDO.java
  55. 0 60
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/leave/OALeaveDO.java
  56. 0 4
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/leave/package-info.java
  57. 15 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/task/BpmProcessInstanceExtDO.java
  58. 6 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/task/BpmTaskExtDO.java
  59. 7 2
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/definition/BpmProcessDefinitionExtMapper.java
  60. 29 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/oa/BpmOALeaveMapper.java
  61. 0 47
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/oa/OALeaveMapper.java
  62. 4 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmProcessInstanceExtMapper.java
  63. 3 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmTaskExtMapper.java
  64. 4 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java
  65. 5 3
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/definition/BpmTaskRuleScriptEnum.java
  66. 28 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/message/BpmMessageEnum.java
  67. 19 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/task/BpmProcessInstanceDeleteReasonEnum.java
  68. 12 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/config/BpmActivitiConfiguration.java
  69. 6 8
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehavior.java
  70. 2 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java
  71. 62 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java
  72. 27 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java
  73. 27 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java
  74. 30 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignStartUserScript.java
  75. 44 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/event/BpmProcessInstanceResultEvent.java
  76. 34 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/event/BpmProcessInstanceResultEventListener.java
  77. 24 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/event/BpmProcessInstanceResultEventPublisher.java
  78. 6 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/event/package-info.java
  79. 37 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/identity/EmptyUserGroupManager.java
  80. 3 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/listener/BpmProcessInstanceEventListener.java
  81. 6 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/listener/BpmTaskEventListener.java
  82. 28 2
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/BpmProcessDefinitionService.java
  83. 9 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/BpmTaskAssignRuleService.java
  84. 33 2
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java
  85. 50 26
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmModelServiceImpl.java
  86. 66 24
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmProcessDefinitionServiceImpl.java
  87. 29 4
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmTaskAssignRuleServiceImpl.java
  88. 40 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/message/BpmMessageService.java
  89. 27 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java
  90. 33 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java
  91. 46 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java
  92. 68 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/message/impl/BpmMessageServiceImpl.java
  93. 52 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/BpmOALeaveService.java
  94. 0 40
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/LeaveApplyEndProcessor.java
  95. 0 80
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/OALeaveService.java
  96. 86 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/impl/BpmOALeaveServiceImpl.java
  97. 0 195
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/impl/OALeaveServiceImpl.java
  98. 32 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/listener/BpmOALeaveResultListener.java
  99. 37 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmActivityService.java
  100. 30 15
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmProcessInstanceService.java

+ 59 - 27
README.md

@@ -4,22 +4,36 @@
 
 > 有任何问题,或者想要的功能,可以在 _Issues_ 中提给艿艿。
 
-* 前端采用 [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)。
-* 后端采用 Spring Boot、MySQL、Redis。
-* 权限认证使用 Spring Security & Token,支持多终端认证系统。
-* 支持加载动态权限菜单,多方式轻松权限控制。
-* 高效率开发,使用代码生成器可以一键生成前后端代码。
+* 前端采用 [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) ,正在支持 Vue 3 + ElementUI Plus 最新方案。
+* 后端采用 Spring Boot、MySQL + MyBatis Plus、Redis + Redisson。
+* 权限认证使用 Spring Security & Token & Redis,支持多终端、多种用户的认证系统。
+* 支持加载动态权限菜单,多方式轻松权限控制,本地缓存提升性能。
+* 工作流使用 Activiti ,支持动态表单、在线设计流程、多种任务分配方式。
+* 高效率开发,使用代码生成器可以一键生成前后端代码 + 单元测试 + Swagger 接口文档。
+
+## 在线体验
+
+演示地址:<http://dashboard.yudao.iocoder.cn>
+* 账号密码:admin/admin123
+
+文档地址:<http://www.iocoder.cn/categories/Yudao/>
+* [《如何搭建环境》](http://www.iocoder.cn/categories/Yudao/?yudao)
+
+> 未来会补充文档和视频,方便胖友冲冲冲!
 
 ## 内置功能
 
-分成三种内置功能:
+分成种内置功能:
 * 系统功能
+* 工作流程
+* 支付系统
 * 基础设施
 * 研发工具
 
 > 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。
-> 
-> 额外新增的功能,我们使用 🚀 标记。
+>
+> * 额外新增的功能,我们使用 🚀 标记。
+> * 重新实现的功能,我们使用 ⭐️ 标记。
 
 🙂 所有功能,都通过 **单元测试** 保证高质量。
 
@@ -28,29 +42,46 @@
 |  | 功能 | 描述 |
 | --- | --- | --- |
 |  | 用户管理 | 用户是系统操作者,该功能主要完成系统用户配置 |
-|  | 在线用户 | 当前系统中活跃用户状态监控,支持手动踢下线 |
+| ⭐️ | 在线用户 | 当前系统中活跃用户状态监控,支持手动踢下线 |
 |  | 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 |
-|  | 菜单管理 | 配置系统菜单,操作权限,按钮权限标识等 |
+|  | 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等,本地缓存提供性能 |
 |  | 部门管理 | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限 |
 |  | 岗位管理 | 配置系统用户所属担任职务 |
-|  | 租户管理 | 配置系统租户,支持 SaaS 场景下的多租户功能 |
+| 🚀 | 租户管理 | 配置系统租户,支持 SaaS 场景下的多租户功能 |
 |  | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 |
 | 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、云片等主流短信平台 |
 | 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
-|  | 登录日志 | 系统登录日志记录查询,包含登录异常 |
+| ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 |
 | 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 |
 |  | 通知公告 | 系统通知公告信息发布维护 |
 
+### 工作流程
+
+|  | 功能 | 描述 |
+| --- | --- | --- |
+| 🚀 | 流程模型 | 配置工作流的流程模型,支持文件导入与在线设计流程图,提供 7 种任务分配规则 |
+| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 |
+| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 |
+| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 |
+| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转发、委派、退回等操作 |
+| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,未来会支持回退操作 |
+| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用案例,只需创建请求对应的工作流程,即可进行审批 |
+
+### 支付系统
+
+正在测试中,核心功能已经开发完毕。
+
 ### 基础设施
 
 |  | 功能 | 描述 |
 | --- | --- | --- |
 | 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 |
-| | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 |
+| ⭐️ | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 |
 | 🚀 | 文件服务 | 支持本地文件存储,同时支持兼容 Amazon S3 协议的云服务、开源组件 | 
 | 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
 |  | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 |
-|  | Redis 监控 |监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
+|  | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
+| 🚀 | 消息队列 | 基于 Redis 实现消息队列,Stream 提供集群消费,Pub/Sub 提供广播消费 |
 | 🚀 |Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
 | 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 |
 | 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 |
@@ -67,17 +98,7 @@
 | 🚀 | 代码生成 |前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载 |
 | 🚀 | 系统接口 | 基于 Swagger 自动生成相关的 RESTful API 接口文档 |
 | 🚀 | 数据库文档 | 基于 Screw 自动生成数据库文档,支持导出 Word、HTML、MD 格式 |
-| | 表单构建 | 拖动表单元素生成相应的 HTML 代码 |
-
-## 在线体验
-
-演示地址:<http://dashboard.yudao.iocoder.cn>
-* 账号密码:admin/admin123  
-
-文档地址:<http://www.iocoder.cn/categories/Yudao/>
-* [《如何搭建环境》](http://www.iocoder.cn/categories/Yudao/?yudao)
-
-> 未来会补充文档和视频,方便胖友冲冲冲!
+|  | 表单构建 | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件  |
 
 ## 技术栈
 
@@ -107,6 +128,7 @@
 | [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架  | 5.3.13 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
 | [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 5.4.9 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
 | [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.1.7 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
+| [Activiti](https://github.com/Activiti/Activiti) | 工作流引擎 | 7.1.0.M6 | [文档](TODO)  |
 | [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
 | [Knife4j](https://gitee.com/xiaoym/knife4j) | Swagger 增强 UI 实现 | 3.0.2 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
 | [Resilience4j](https://github.com/resilience4j/resilience4j) | 服务保障组件 | 1.7.0 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) |
@@ -132,7 +154,7 @@
 | 模块 | biu |  biu | biu |
 | --- | --- | --- | --- |
 | 登录 & 首页 | ![登录](https://static.iocoder.cn/images/ruoyi-vue-pro/登录.jpg) | ![首页](https://static.iocoder.cn/images/ruoyi-vue-pro/首页.jpg) | ![个人中心](https://static.iocoder.cn/images/ruoyi-vue-pro/个人中心.jpg) |
-| 用户 | ![用户管理](https://static.iocoder.cn/images/ruoyi-vue-pro/用户管理.jpg) | ![在线用户](https://static.iocoder.cn/images/ruoyi-vue-pro/在线用户.jpg) | - |
+| 用户 & 租户 | ![用户管理](https://static.iocoder.cn/images/ruoyi-vue-pro/用户管理.jpg) | ![在线用户](https://static.iocoder.cn/images/ruoyi-vue-pro/在线用户.jpg) | ![用户管理](https://static.iocoder.cn/images/ruoyi-vue-pro/租户管理.jpg) |
 | 部门 & 岗位 | ![部门管理](https://static.iocoder.cn/images/ruoyi-vue-pro/部门管理.jpg) | ![岗位管理](https://static.iocoder.cn/images/ruoyi-vue-pro/岗位管理.jpg) | - |
 | 菜单 & 角色 | ![菜单管理](https://static.iocoder.cn/images/ruoyi-vue-pro/菜单管理.jpg) | ![角色管理](https://static.iocoder.cn/images/ruoyi-vue-pro/角色管理.jpg) | - |
 | 审计日志 | ![操作日志](https://static.iocoder.cn/images/ruoyi-vue-pro/操作日志.jpg) | ![登录日志](https://static.iocoder.cn/images/ruoyi-vue-pro/登录日志.jpg) | - |
@@ -140,6 +162,16 @@
 | 字典 | ![字典类型](https://static.iocoder.cn/images/ruoyi-vue-pro/字典类型.jpg) | ![字典数据](https://static.iocoder.cn/images/ruoyi-vue-pro/字典数据.jpg) | - |
 | 错误码 & 通知 | ![错误码管理](https://static.iocoder.cn/images/ruoyi-vue-pro/错误码管理.jpg) | ![通知公告](https://static.iocoder.cn/images/ruoyi-vue-pro/通知公告.jpg) | - |
 
+### 工作流程
+
+| 模块 | biu |  biu | biu |
+| --- | --- | --- | --- |
+| 流程模型 | ![流程模型-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-列表.jpg) | ![流程模型-设计](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-设计.jpg) | ![流程模型-定义](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-定义.jpg) |
+| 表单 & 分组 | ![流程表单](https://static.iocoder.cn/images/ruoyi-vue-pro/流程表单.jpg) | ![流程分组](https://static.iocoder.cn/images/ruoyi-vue-pro/流程分组.jpg) | - |
+| 我的流程 | ![我的流程-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-列表.jpg) | ![我的流程-发起](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-发起.jpg) | ![我的流程-详情](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-详情.jpg) |
+| 待办 & 已办 | ![任务列表-审批](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-审批.jpg) | ![任务列表-待办](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-待办.jpg) | ![任务列表-已办](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-已办.jpg) |
+| OA 请假 | ![OA审批-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/OA审批-列表.jpg) | ![OA审批-发起](https://static.iocoder.cn/images/ruoyi-vue-pro/OA审批-发起.jpg) | ![OA审批-详情](https://static.iocoder.cn/images/ruoyi-vue-pro/OA审批-详情.jpg) |
+
 ### 基础设施
 
 | 模块 | biu |  biu | biu |
@@ -154,5 +186,5 @@
 
 | 模块 | biu |  biu | biu |
 | --- | --- | --- | --- |
-| 代码生成 | ![代码生成](https://static.iocoder.cn/images/ruoyi-vue-pro/代码生成.jpg) | ![生成效果](https://static.iocoder.cn/images/ruoyi-vue-pro/生成效果.jpg) | ![表单构建](https://static.iocoder.cn/images/ruoyi-vue-pro/表单构建.jpg) |
+| 代码生成 | ![代码生成](https://static.iocoder.cn/images/ruoyi-vue-pro/代码生成.jpg) | ![生成效果](https://static.iocoder.cn/images/ruoyi-vue-pro/生成效果.jpg) | - |
 | 文档 | ![系统接口](https://static.iocoder.cn/images/ruoyi-vue-pro/系统接口.jpg) | ![数据库文档](https://static.iocoder.cn/images/ruoyi-vue-pro/数据库文档.jpg) | - |

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
sql/activiti.sql


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 8 - 8
sql/ruoyi-vue-pro.sql


+ 1 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/BpmFormController.java

@@ -45,7 +45,7 @@ public class BpmFormController {
 
     @DeleteMapping("/delete")
     @ApiOperation("删除动态表单")
-    @ApiImplicitParam(name = "id", value = "编号", required = true)
+    @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
     @PreAuthorize("@ss.hasPermission('bpm:form:delete')")
     public CommonResult<Boolean> deleteForm(@RequestParam("id") Long id) {
         formService.deleteForm(id);

+ 3 - 2
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/BpmProcessDefinitionController.java

@@ -34,7 +34,7 @@ public class BpmProcessDefinitionController {
 
     @GetMapping ("/page")
     @ApiOperation(value = "获得流程定义分页")
-    @PreAuthorize("@ss.hasPermission('bpm:model:query')") // 暂时使用 model 的权限标识
+    @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
     public CommonResult<PageResult<BpmProcessDefinitionPageItemRespVO>> getProcessDefinitionPage(
             BpmProcessDefinitionPageReqVO pageReqVO) {
         return success(bpmDefinitionService.getProcessDefinitionPage(pageReqVO));
@@ -42,6 +42,7 @@ public class BpmProcessDefinitionController {
 
     @GetMapping ("/list")
     @ApiOperation(value = "获得流程定义列表")
+    @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
     public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionList(
             BpmProcessDefinitionListReqVO listReqVO) {
         return success(bpmDefinitionService.getProcessDefinitionList(listReqVO));
@@ -50,7 +51,7 @@ public class BpmProcessDefinitionController {
     @GetMapping ("/get-bpmn-xml")
     @ApiOperation(value = "获得流程定义的 BPMN XML")
     @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = String.class)
-    @PreAuthorize("@ss.hasPermission('bpm:model:query')") // 暂时使用 model 的权限标识
+    @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
     public CommonResult<String> getProcessDefinitionBpmnXML(@RequestParam("id") String id) {
         String bpmnXML = bpmDefinitionService.getProcessDefinitionBpmnXML(id);
         return success(bpmnXML);

+ 14 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/BpmTaskAssignRuleController.java

@@ -6,6 +6,10 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.rule.Bp
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskAssignRuleService;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
@@ -25,6 +29,12 @@ public class BpmTaskAssignRuleController {
     private BpmTaskAssignRuleService taskAssignRuleService;
 
     @GetMapping("/list")
+    @ApiOperation(value = "获得任务分配规则列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "modelId", value = "模型编号", example = "1024", dataTypeClass = String.class),
+            @ApiImplicitParam(name = "processDefinitionId", value = "刘晨定义的编号", example = "2048", dataTypeClass = String.class)
+    })
+    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:query')")
     public CommonResult<List<BpmTaskAssignRuleRespVO>> getTaskAssignRuleList(
             @RequestParam(value = "modelId", required = false) String modelId,
             @RequestParam(value = "processDefinitionId", required = false) String processDefinitionId) {
@@ -32,11 +42,15 @@ public class BpmTaskAssignRuleController {
     }
 
     @PostMapping("/create")
+    @ApiOperation(value = "创建任务分配规则")
+    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:create')")
     public CommonResult<Long> createTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleCreateReqVO reqVO) {
         return success(taskAssignRuleService.createTaskAssignRule(reqVO));
     }
 
     @PutMapping("/update")
+    @ApiOperation(value = "更新任务分配规则")
+    @PreAuthorize("@ss.hasPermission('bpm:task-assign-rule:update')")
     public CommonResult<Boolean> updateTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleUpdateReqVO reqVO) {
         taskAssignRuleService.updateTaskAssignRule(reqVO);
         return success(true);

+ 1 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/BpmUserGroupController.java

@@ -53,7 +53,7 @@ public class BpmUserGroupController {
 
     @DeleteMapping("/delete")
     @ApiOperation("删除用户组")
-    @ApiImplicitParam(name = "id", value = "编号", required = true)
+    @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
     @PreAuthorize("@ss.hasPermission('bpm:user-group:delete')")
     public CommonResult<Boolean> deleteUserGroup(@RequestParam("id") Long id) {
         userGroupService.deleteUserGroup(id);

+ 1 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/form/BpmFormRespVO.java

@@ -18,6 +18,7 @@ public class BpmFormRespVO extends BpmFormBaseVO {
 
     @ApiModelProperty(value = "表单编号", required = true, example = "1024")
     private Long id;
+
     @ApiModelProperty(value = "表单的配置", required = true, notes = "JSON 字符串")
     @NotNull(message = "表单的配置不能为空")
     private String conf;

+ 1 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/rule/BpmUserGroupSimpleRespVO.java → yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/group/BpmUserGroupSimpleRespVO.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.rule;
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.group;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;

+ 0 - 3
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/model/BpmModelUpdateReqVO.java

@@ -1,11 +1,8 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.model;
 
-import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmModelFormTypeEnum;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
 
 import javax.validation.constraints.NotEmpty;
 

+ 17 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/process/BpmProcessDefinitionRespVO.java

@@ -5,6 +5,8 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.List;
 
 @ApiModel("流程定义 Response VO")
 @Data
@@ -27,8 +29,22 @@ public class BpmProcessDefinitionRespVO {
     @NotEmpty(message = "流程分类不能为空")
     private String category;
 
-    @ApiModelProperty(value = "表单编号", example = "1024")
+    @ApiModelProperty(value = "表单类型", notes = "参见 bpm_model_form_type 数据字典", example = "1")
+    private Integer formType;
+    @ApiModelProperty(value = "表单编号", example = "1024", notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空")
     private Long formId;
+    @ApiModelProperty(value = "表单的配置", required = true,
+            notes = "JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空")
+    private String formConf;
+    @ApiModelProperty(value = "表单项的数组", required = true,
+            notes = "JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空")
+    private List<String> formFields;
+    @ApiModelProperty(value = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create",
+            notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空")
+    private String formCustomCreatePath;
+    @ApiModelProperty(value = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view",
+            notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空")
+    private String formCustomViewPath;
 
     @ApiModelProperty(value = "中断状态", required = true, example = "1", notes = "参见 SuspensionState 枚举")
     private Integer suspensionState;

+ 11 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/rule/BpmTaskAssignRuleBaseVO.java

@@ -1,14 +1,25 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.rule;
 
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
 import java.util.Set;
 
+/**
+ * 流程任务分配规则 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
 @Data
 public class BpmTaskAssignRuleBaseVO {
 
+    @ApiModelProperty(value = "规则类型", required = true, example = "bpm_task_assign_rule_type")
+    @NotNull(message = "规则类型不能为空")
     private Integer type;
 
+    @ApiModelProperty(value = "规则值数组", required = true, example = "1,2,3")
+    @NotNull(message = "规则值数组不能为空")
     private Set<Long> options;
 
 }

+ 11 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/rule/BpmTaskAssignRuleCreateReqVO.java

@@ -1,14 +1,25 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.rule;
 
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import lombok.ToString;
 
+import javax.validation.constraints.NotEmpty;
+
+@ApiModel("流程任务分配规则的创建 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
 public class BpmTaskAssignRuleCreateReqVO extends BpmTaskAssignRuleBaseVO {
 
+    @ApiModelProperty(value = "流程模型的编号", required = true, example = "1024")
+    @NotEmpty(message = "流程模型的编号不能为空")
     private String modelId;
 
+    @ApiModelProperty(value = "流程任务定义的编号", required = true, example = "2048")
+    @NotEmpty(message = "流程任务定义的编号不能为空")
     private String taskDefinitionKey;
 
 }

+ 12 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/rule/BpmTaskAssignRuleRespVO.java

@@ -1,17 +1,29 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.rule;
 
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
 
+@ApiModel("流程任务分配规则的 Response VO")
 @Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
 public class BpmTaskAssignRuleRespVO extends BpmTaskAssignRuleBaseVO {
 
+    @ApiModelProperty(value = "任务分配规则的编号", required = true, example = "1024")
     private Long id;
 
+    @ApiModelProperty(value = "流程模型的编号", required = true, example = "2048")
     private String modelId;
 
+    @ApiModelProperty(value = "流程定义的编号", required = true, example = "4096")
     private String processDefinitionId;
 
+    @ApiModelProperty(value = "流程任务定义的编号", required = true, example = "2048")
     private String taskDefinitionKey;
+    @ApiModelProperty(value = "流程任务定义的名字", required = true, example = "关注芋道")
     private String taskDefinitionName;
 
 }

+ 10 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/rule/BpmTaskAssignRuleUpdateReqVO.java

@@ -1,12 +1,22 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.rule;
 
+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.Set;
 
+@ApiModel("流程任务分配规则的更新 Request VO")
 @Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
 public class BpmTaskAssignRuleUpdateReqVO extends BpmTaskAssignRuleBaseVO {
 
+    @ApiModelProperty(value = "任务分配规则的编号", required = true, example = "1024")
+    @NotNull(message = "任务分配规则的编号不能为空")
     private Long id;
 
 }

+ 12 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/BpmOALeaveController.http

@@ -0,0 +1,12 @@
+### 请求 /bpm/oa/leave/create 接口 => 成功
+POST {{baseUrl}}/bpm/oa/leave/create
+Content-Type: application/json
+tenant-id: 1
+Authorization: Bearer {{token}}
+
+{
+  "startTime": "2022-03-01",
+  "endTime": "2022-03-05",
+  "type": 1,
+  "reason": "我要请假啦啦啦!"
+}

+ 63 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/BpmOALeaveController.java

@@ -0,0 +1,63 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.oa;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.BpmOALeaveCreateReqVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.BpmOALeavePageReqVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.BpmOALeaveRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.convert.oa.BpmOALeaveConvert;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.BpmOALeaveDO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.oa.BpmOALeaveService;
+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.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.validation.Valid;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+/**
+ * OA 请假申请 Controller,用于演示自己存储数据,接入工作流的例子
+ *
+ * @author jason
+ * @author 芋道源码
+ */
+@Api(tags = "OA 请假申请")
+@RestController
+@RequestMapping("/bpm/oa/leave")
+@Validated
+public class BpmOALeaveController {
+
+    @Resource
+    private BpmOALeaveService leaveService;
+
+    @PostMapping("/create")
+    @PreAuthorize("@ss.hasPermission('bpm:oa-leave:create')")
+    @ApiOperation("创建请求申请")
+    public CommonResult<Long> createLeave(@Valid @RequestBody BpmOALeaveCreateReqVO createReqVO) {
+        return success(leaveService.createLeave(getLoginUserId(), createReqVO));
+    }
+
+    @GetMapping("/get")
+    @PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')")
+    @ApiOperation("获得请假申请")
+    @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
+    public CommonResult<BpmOALeaveRespVO> getLeave(@RequestParam("id") Long id) {
+        BpmOALeaveDO leave = leaveService.getLeave(id);
+        return success(BpmOALeaveConvert.INSTANCE.convert(leave));
+    }
+
+    @GetMapping("/page")
+    @PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')")
+    @ApiOperation("获得请假申请分页")
+    public CommonResult<PageResult<BpmOALeaveRespVO>> getLeavePage(@Valid BpmOALeavePageReqVO pageVO) {
+        PageResult<BpmOALeaveDO> pageResult = leaveService.getLeavePage(getLoginUserId(), pageVO);
+        return success(BpmOALeaveConvert.INSTANCE.convertPage(pageResult));
+    }
+
+}

+ 0 - 112
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/OALeaveController.java

@@ -1,112 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.controller.oa;
-
-import cn.iocoder.yudao.adminserver.modules.bpm.convert.oa.OALeaveConvert;
-import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.OALeaveDO;
-import cn.iocoder.yudao.adminserver.modules.bpm.service.oa.OALeaveService;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.*;
-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;
-
-
-@Api(tags = "请假申请")
-@RestController
-@RequestMapping("/oa/leave")
-@Validated
-public class OALeaveController {
-
-    @Resource
-    private OALeaveService leaveService;
-
-
-
-    @PostMapping("/form-key/create")
-    @ApiOperation("创建外置请假申请")
-    public CommonResult<Long> createFormKeyLeave(@Valid @RequestBody OALeaveCreateReqVO createReqVO) {
-        // processKey 前台传入
-        return success(leaveService.createLeave(createReqVO));
-    }
-
-    @GetMapping("/getLeaveApplyMembers")
-    @ApiOperation("获取本人请假申请流程中审批人员,可先检查这些人员是否存在")
-    public CommonResult<OALeaveApplyMembersVO> getLeaveApplyMembers() {
-        return success(leaveService.getLeaveApplyMembers());
-    }
-
-    @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);
-    }
-
-}

+ 6 - 21
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveBaseVO.java → yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/BpmOALeaveBaseVO.java

@@ -13,36 +13,21 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
 */
 @Data
-public class OALeaveBaseVO {
+public class BpmOALeaveBaseVO {
 
-    @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)
+    @ApiModelProperty(value = "请假的开始时间", required = true)
     @NotNull(message = "开始时间不能为空")
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
     private Date startTime;
-
-    @ApiModelProperty(value = "结束时间", required = true)
+    @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 = "请假类型", required = true, example = "1", notes = "参见 bpm_oa_type 枚举")
+    private Integer type;
 
-    @ApiModelProperty(value = "原因")
+    @ApiModelProperty(value = "原因", required = true, example = "阅读芋道源码")
     private String reason;
 
-    @ApiModelProperty(value = "申请时间", required = true)
-    @NotNull(message = "申请时间不能为空")
-    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
-    private Date applyTime;
-
 }

+ 21 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/BpmOALeaveCreateReqVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.AssertTrue;
+
+@ApiModel("请假申请创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class BpmOALeaveCreateReqVO extends BpmOALeaveBaseVO {
+
+    @AssertTrue(message = "结束时间,需要在开始时间之后")
+    public boolean isEndTimeValid() {
+        return !getEndTime().before(getStartTime());
+    }
+
+}

+ 34 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/BpmOALeavePageReqVO.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.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 BpmOALeavePageReqVO extends PageParam {
+
+    @ApiModelProperty(value = "状态", example = "1", notes = "参见 bpm_process_instance_result 枚举")
+    private Integer result;
+
+    @ApiModelProperty(value = "请假类型", example = "1", notes = "参见 bpm_oa_type")
+    private Integer type;
+
+    @ApiModelProperty(value = "原因", example = "阅读芋道源码", notes = "模糊匹配")
+    private String reason;
+
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    @ApiModelProperty(value = "开始申请时间")
+    private Date beginCreateTime;
+
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    @ApiModelProperty(value = "结束申请时间")
+    private Date endCreateTime;
+
+}

+ 32 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/BpmOALeaveRespVO.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo;
+
+import lombok.*;
+import io.swagger.annotations.*;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@ApiModel("请假申请 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class BpmOALeaveRespVO extends BpmOALeaveBaseVO {
+
+    @ApiModelProperty(value = "请假表单主键", required = true, example = "1024")
+    private Long id;
+
+    @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 bpm_process_instance_result 枚举")
+    private Integer result;
+
+    @ApiModelProperty(value = "申请时间", required = true)
+    @NotNull(message = "申请时间不能为空")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private Date createTime;
+
+    @ApiModelProperty(value = "流程id")
+    private String processInstanceId;
+
+}

+ 0 - 25
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveApplyMembersVO.java

@@ -1,25 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Builder;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-
-@ApiModel("请假申请审批人员 Response VO")
-@Data
-@Builder
-@EqualsAndHashCode
-@ToString
-public class OALeaveApplyMembersVO {
-
-    @ApiModelProperty(value = "部门的hr")
-    private String hr;
-
-    @ApiModelProperty(value = "部门的项目经理")
-    private String pm;
-
-    @ApiModelProperty(value = "部门的部门经理")
-    private String bm;
-}

+ 0 - 23
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveCreateReqVO.java

@@ -1,23 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo;
-
-import lombok.*;
-import io.swagger.annotations.*;
-
-import java.util.Map;
-
-@ApiModel("请假申请创建 Request VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class OALeaveCreateReqVO extends OALeaveBaseVO {
-
-    /**
-     * 对应 bpmn 文件 <process> 的 id
-     */
-    @ApiModelProperty(value = "流程key")
-    private String processKey;
-
-
-    @ApiModelProperty(value = "流程用户任务的变量")
-    private Map<String,Object> taskVariables;
-}

+ 0 - 44
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveExcelVO.java

@@ -1,44 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.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;
-
-}

+ 0 - 54
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveExportReqVO.java

@@ -1,54 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.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;
-
-}

+ 0 - 56
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeavePageReqVO.java

@@ -1,56 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.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;
-
-}

+ 0 - 15
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveRespVO.java

@@ -1,15 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo;
-
-import lombok.*;
-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;
-
-}

+ 0 - 32
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/oa/vo/OALeaveUpdateReqVO.java

@@ -1,32 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.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 @json:swagger 和 validator 的注解要加哈。
-
-    private String taskId;
-
-    private String comment;
-
-    private Map<String,Object> variables;
-
-    // TODO @芋艿:variables 的作用是啥。
-
-}

+ 55 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmActivityController.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.task;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity.BpmActivityRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmActivityService;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
+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.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;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Api(tags = "流程活动实例")
+@RestController
+@RequestMapping("/bpm/activity")
+@Validated
+public class BpmActivityController {
+
+    @Resource
+    private BpmActivityService activityService;
+
+    @GetMapping("/list")
+    @ApiOperation(value = "生成指定流程实例的高亮流程图",
+            notes = "只高亮进行中的任务。不过要注意,该接口暂时没用,通过前端的 ProcessViewer.vue 界面的 highlightDiagram 方法生成")
+    @ApiImplicitParam(name = "id", value = "流程实例的编号", required = true, dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:task:query')")
+    public CommonResult<List<BpmActivityRespVO>> getActivityList(
+            @RequestParam("processInstanceId") String processInstanceId) {
+        return success(activityService.getActivityListByProcessInstanceId(processInstanceId));
+    }
+
+    @GetMapping("/generate-highlight-diagram")
+    @ApiOperation(value = "生成指定流程实例的高亮流程图",
+            notes = "只高亮进行中的任务。不过要注意,该接口暂时没用,通过前端的 ProcessViewer.vue 界面的 highlightDiagram 方法生成")
+    @ApiImplicitParam(name = "id", value = "流程实例的编号", required = true, dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:task:query')")
+    public void generateHighlightDiagram(@RequestParam("processInstanceId") String processInstanceId,
+                                         HttpServletResponse response) throws IOException {
+        byte[] bytes = activityService.generateHighlightDiagram(processInstanceId);
+        ServletUtils.writeAttachment(response, StrUtil.format("流程图-{}.svg", processInstanceId), bytes);
+    }
+
+}

+ 2 - 2
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmProcessInstanceController.http

@@ -23,7 +23,7 @@ Authorization: Bearer {{token}}
   "reason": "我就取消"
 }
 
-### 请求 /bpm/process-instance/my-page 接口 => 成功
-GET {{baseUrl}}/bpm/process-instance/my-page
+### 请求 /bpm/process-instance/get 接口 => 成功
+GET {{baseUrl}}/bpm/process-instance/get?id=537cceb3-768c-11ec-afcd-a2380e71991a
 tenant-id: 1
 Authorization: Bearer {{token}}

+ 15 - 7
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmProcessInstanceController.java

@@ -1,14 +1,13 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.controller.task;
 
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCancelReqVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCreateReqVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceMyPageReqVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstancePageItemRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.*;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
 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.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.*;
 
@@ -26,17 +25,17 @@ public class BpmProcessInstanceController {
 
     @Resource
     private BpmProcessInstanceService processInstanceService;
-
-    // TODO 芋艿:权限
-
+    
     @PostMapping("/create")
     @ApiOperation("新建流程实例")
+    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
     public CommonResult<String> createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) {
         return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO));
     }
 
     @DeleteMapping("/cancel")
     @ApiOperation(value = "取消流程实例", notes = "撤回发起的流程")
+    @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')")
     public CommonResult<Boolean> cancelProcessInstance(@Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) {
         processInstanceService.cancelProcessInstance(getLoginUserId(), cancelReqVO);
         return success(true);
@@ -44,9 +43,18 @@ public class BpmProcessInstanceController {
 
     @GetMapping("/my-page")
     @ApiOperation(value = "获得我的实例分页列表", notes = "在【我的流程】菜单中,进行调用")
+    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
     public CommonResult<PageResult<BpmProcessInstancePageItemRespVO>> getMyProcessInstancePage(
             @Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
         return success(processInstanceService.getMyProcessInstancePage(getLoginUserId(), pageReqVO));
     }
 
+    @GetMapping("/get")
+    @ApiOperation(value = "获得指定流程实例", notes = "在【流程详细】界面中,进行调用")
+    @ApiImplicitParam(name = "id", value = "流程实例的编号", required = true, dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
+    public CommonResult<BpmProcessInstanceRespVO> getProcessInstance(@RequestParam("id") String id) {
+        return success(processInstanceService.getProcessInstanceVO(id));
+    }
+
 }

+ 5 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmTaskController.http

@@ -7,3 +7,8 @@ Authorization: Bearer {{token}}
 GET {{baseUrl}}/bpm/task/done-page?pageSize=100
 tenant-id: 1
 Authorization: Bearer {{token}}
+
+### 请求 /bpm/task/list-by-process-instance-id 接口 => 成功
+GET {{baseUrl}}/bpm/task/list-by-process-instance-id?processInstanceId=537cceb3-768c-11ec-afcd-a2380e71991a
+tenant-id: 1
+Authorization: Bearer {{token}}

+ 22 - 21
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmTaskController.java

@@ -6,7 +6,9 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
 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.*;
 
@@ -19,7 +21,7 @@ import java.util.List;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
 
-@Api(tags = "流程任务")
+@Api(tags = "流程任务实例")
 @RestController
 @RequestMapping("/bpm/task")
 @Validated
@@ -28,52 +30,51 @@ public class BpmTaskController {
     @Resource
     private BpmTaskService taskService;
 
-    // TODO 芋艿:权限、validation;
-
     @GetMapping("todo-page")
     @ApiOperation("获取 Todo 待办任务分页")
+    @PreAuthorize("@ss.hasPermission('bpm:task:query')")
     public CommonResult<PageResult<BpmTaskTodoPageItemRespVO>> getTodoTaskPage(@Valid BpmTaskTodoPageReqVO pageVO) {
         return success(taskService.getTodoTaskPage(getLoginUserId(), pageVO));
     }
 
     @GetMapping("done-page")
     @ApiOperation("获取 Done 已办任务分页")
+    @PreAuthorize("@ss.hasPermission('bpm:task:query')")
     public CommonResult<PageResult<BpmTaskDonePageItemRespVO>> getTodoTaskPage(@Valid BpmTaskDonePageReqVO pageVO) {
         return success(taskService.getDoneTaskPage(getLoginUserId(), pageVO));
     }
 
     @PutMapping("/approve")
     @ApiOperation("通过任务")
+    @PreAuthorize("@ss.hasPermission('bpm:task:update')")
     public CommonResult<Boolean> approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) {
-        taskService.approveTask(reqVO);
+        taskService.approveTask(getLoginUserId(), reqVO);
         return success(true);
     }
 
     @PutMapping("/reject")
     @ApiOperation("不通过任务")
+    @PreAuthorize("@ss.hasPermission('bpm:task:update')")
     public CommonResult<Boolean> rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) {
-        taskService.rejectTask(reqVO);
+        taskService.rejectTask(getLoginUserId(), reqVO);
         return success(true);
     }
 
-    @PostMapping("/task-steps")
-    public CommonResult<TaskHandleVO> getTaskSteps(@RequestBody TaskQueryReqVO taskQuery) {
-        return success(taskService.getTaskSteps(taskQuery));
-    }
-
-    @GetMapping("/process/history-steps")
-    public CommonResult<List<TaskStepVO>> getHistorySteps(@RequestParam("id") String processInstanceId) {
-        return success(taskService.getHistorySteps(processInstanceId));
+    @PutMapping("/update-assignee")
+    @ApiOperation(value = "更新任务的负责人", notes = "用于【流程详情】的【转派】按钮")
+    @PreAuthorize("@ss.hasPermission('bpm:task:update')")
+    public CommonResult<Boolean> updateTaskAssignee(@Valid @RequestBody BpmTaskUpdateAssigneeReqVO reqVO) {
+        taskService.updateTaskAssignee(getLoginUserId(), reqVO);
+        return success(true);
     }
 
-    /**
-     * 返回高亮的流转图SVG
-     * @param processInstanceId 流程Id
-     */
-    @GetMapping("/process/highlight-img")
-    public void getHighlightImg(@RequestParam String processInstanceId, HttpServletResponse response) throws IOException {
-        FileResp fileResp = taskService.getHighlightImg(processInstanceId);
-        ServletUtils.writeAttachment(response, fileResp.getFileName(), fileResp.getFileByte());
+    @GetMapping("/list-by-process-instance-id")
+    @ApiOperation(value = "获得指定流程实例的任务列表", notes = "包括完成的、未完成的")
+    @ApiImplicitParam(name = "processInstanceId", value = "流程实例的编号", required = true, dataTypeClass = String.class)
+    @PreAuthorize("@ss.hasPermission('bpm:task:query')")
+    public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
+            @RequestParam("processInstanceId") String processInstanceId) {
+        return success(taskService.getTaskListByProcessInstanceId(processInstanceId));
     }
 
 }

+ 26 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/activity/BpmActivityRespVO.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+@ApiModel("流程活动的 Response VO")
+@Data
+public class BpmActivityRespVO {
+
+    @ApiModelProperty(value = "流程活动的标识", required = true, example = "1024")
+    private String key;
+    @ApiModelProperty(value = "流程活动的类型", required = true, example = "StartEvent")
+    private String type;
+
+    @ApiModelProperty(value = "流程活动的开始时间", required = true)
+    private Date startTime;
+    @ApiModelProperty(value = "流程活动的结束时间", required = true)
+    private Date endTime;
+
+    @ApiModelProperty(value = "关联的流程任务的编号", example = "2048", notes = "关联的流程任务,只有 UserTask 等类型才有")
+    private String taskId;
+
+}

+ 97 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/instance/BpmProcessInstanceRespVO.java

@@ -0,0 +1,97 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+@ApiModel("流程实例的 Response VO")
+@Data
+public class BpmProcessInstanceRespVO {
+
+    @ApiModelProperty(value = "流程实例的编号", required = true, example = "1024")
+    private String id;
+
+    @ApiModelProperty(value = "流程名称", required = true, example = "芋道")
+    private String name;
+
+    @ApiModelProperty(value = "流程分类", required = true, notes = "参见 bpm_model_category 数据字典", example = "1")
+    private String category;
+
+    @ApiModelProperty(value = "流程实例的状态", required = true, notes = "参见 bpm_process_instance_status", example = "1")
+    private Integer status;
+
+    @ApiModelProperty(value = "流程实例的结果", required = true, notes = "参见 bpm_process_instance_result", example = "2")
+    private Integer result;
+
+    @ApiModelProperty(value = "提交时间", required = true)
+    private Date createTime;
+
+    @ApiModelProperty(value = "结束时间", required = true)
+    private Date endTime;
+
+    @ApiModelProperty(value = "提交的表单值", required = true)
+    private Map<String, Object> formVariables;
+
+    @ApiModelProperty(value = "业务的唯一标识", example = "1", notes = "例如说,请假申请的编号")
+    private String businessKey;
+
+    /**
+     * 发起流程的用户
+     */
+    private User startUser;
+
+    /**
+     * 流程定义
+     */
+    private ProcessDefinition processDefinition;
+
+    @ApiModel("用户信息")
+    @Data
+    public static class User {
+
+        @ApiModelProperty(value = "用户编号", required = true, example = "1")
+        private Long id;
+        @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿")
+        private String nickname;
+
+        @ApiModelProperty(value = "部门编号", required = true, example = "1")
+        private Long deptId;
+        @ApiModelProperty(value = "部门名称", required = true, example = "研发部")
+        private String deptName;
+
+    }
+
+    @ApiModel("流程定义信息")
+    @Data
+    public static class ProcessDefinition {
+
+        @ApiModelProperty(value = "编号", required = true, example = "1024")
+        private String id;
+
+        @ApiModelProperty(value = "表单类型", notes = "参见 bpm_model_form_type 数据字典", example = "1")
+        private Integer formType;
+        @ApiModelProperty(value = "表单编号", example = "1024", notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空")
+        private Long formId;
+        @ApiModelProperty(value = "表单的配置", required = true,
+                notes = "JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空")
+        private String formConf;
+        @ApiModelProperty(value = "表单项的数组", required = true,
+                notes = "JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空")
+        private List<String> formFields;
+        @ApiModelProperty(value = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create",
+                notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空")
+        private String formCustomCreatePath;
+        @ApiModelProperty(value = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view",
+                notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空")
+        private String formCustomViewPath;
+
+        @ApiModelProperty(value = "BPMN XML", required = true)
+        private String bpmnXml;
+
+    }
+
+}

+ 5 - 44
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/BpmTaskDonePageItemRespVO.java

@@ -3,24 +3,17 @@ package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
 
 import java.util.Date;
 
 @ApiModel("流程任务的 Done 已完成的分页项 Response VO")
 @Data
-public class BpmTaskDonePageItemRespVO {
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class BpmTaskDonePageItemRespVO extends BpmTaskTodoPageItemRespVO {
 
-    @ApiModelProperty(value = "任务编号", required = true, example = "1024")
-    private String id;
-
-    @ApiModelProperty(value = "任务名字", required = true, example = "芋道")
-    private String name;
-
-    @ApiModelProperty(value = "接收时间", required = true)
-    private Date claimTime;
-
-    @ApiModelProperty(value = "创建时间", required = true)
-    private Date createTime;
     @ApiModelProperty(value = "结束时间", required = true)
     private Date endTime;
     @ApiModelProperty(value = "持续时间", required = true, example = "1000")
@@ -31,36 +24,4 @@ public class BpmTaskDonePageItemRespVO {
     @ApiModelProperty(value = "审批建议", required = true, example = "不请假了!")
     private String comment;
 
-    /**
-     * 所属流程实例
-     */
-    private ProcessInstance processInstance;
-
-    @Data
-    @ApiModel("流程实例")
-    public static class ProcessInstance {
-
-        @ApiModelProperty(value = "流程实例编号", required = true, example = "1024")
-        private String id;
-
-        @ApiModelProperty(value = "流程实例名称", required = true, example = "芋道")
-        private String name;
-
-        @ApiModelProperty(value = "发起人的用户编号", required = true, example = "1024")
-        private Long startUserId;
-
-        @ApiModelProperty(value = "发起人的用户昵称", required = true, example = "芋艿")
-        private String startUserNickname;
-
-        @ApiModelProperty(value = "流程定义的编号", required = true, example = "2048")
-        private String processDefinitionId;
-
-    }
-
-
-    // 任务编号、流程名称、任务节点、流程发起人、接收时间、审批时间、耗时【名称、开始时间】「流程记录、撤回」
-    // 任务编号、任务名称、所属流程、委托代办人、流程发起人、优先级、审批操作、审批意见、耗时、创建时间【名称、开始时间】「申请详情」
-
-    // 任务编号、任务名称、流程名称、流程发起人、接收时间、审批时间、耗时【名称、接收时间】「详情」TODO 撤回
-
 }

+ 41 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/BpmTaskRespVO.java

@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.List;
+
+@ApiModel("流程任务的 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class BpmTaskRespVO extends BpmTaskDonePageItemRespVO {
+
+    @ApiModelProperty(value = "任务定义的标识", required = true, example = "user-001")
+    private String definitionKey;
+
+    /**
+     * 审核的用户信息
+     */
+    private User assigneeUser;
+
+    @ApiModel("用户信息")
+    @Data
+    public static class User {
+
+        @ApiModelProperty(value = "用户编号", required = true, example = "1")
+        private Long id;
+        @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿")
+        private String nickname;
+
+        @ApiModelProperty(value = "部门编号", required = true, example = "1")
+        private Long deptId;
+        @ApiModelProperty(value = "部门名称", required = true, example = "研发部")
+        private String deptName;
+
+    }
+
+}

+ 25 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/BpmTaskUpdateAssigneeReqVO.java

@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import net.bytebuddy.implementation.bind.annotation.Empty;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@ApiModel("流程任务的更新负责人的 Request VO")
+@Data
+public class BpmTaskUpdateAssigneeReqVO {
+
+    @ApiModelProperty(value = "任务编号", required = true, example = "1024")
+    @NotEmpty(message = "任务编号不能为空")
+    private String id;
+
+    @ApiModelProperty(value = "新审批人的用户编号", required = true, example = "2048")
+    @NotNull(message = "新审批人的用户编号不能为空")
+    private Long assigneeUserId;
+
+}

+ 0 - 24
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/FileResp.java

@@ -1,24 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
-
-import lombok.Data;
-
-// TODO @Li:1)改成 HighlightImgRespVO 吧。2)swagger 注解要补充;3)fileByte => fileContent
-/**
- * 文件输出类
- *
- * @author yunlongn
- */
-@Data
-public class FileResp {
-
-    /**
-     * 文件名字
-     */
-    private String fileName;
-
-    /**
-     * 文件输出流
-     */
-    private byte[] fileByte;
-
-}

+ 0 - 19
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/TaskHandleVO.java

@@ -1,19 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
-
-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;
-}

+ 0 - 15
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/TaskQueryReqVO.java

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

+ 0 - 24
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/task/TaskStepVO.java

@@ -1,24 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
-
-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;
-
-}

+ 9 - 4
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/definition/BpmModelConvert.java

@@ -3,7 +3,7 @@ package cn.iocoder.yudao.adminserver.modules.bpm.convert.definition;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.model.*;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmFormDO;
-import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmDefinitionCreateReqDTO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
@@ -83,8 +83,8 @@ public interface BpmModelConvert {
 
     void copyTo(BpmModelMetaInfoRespDTO from, @MappingTarget BpmModelBaseVO to);
 
-    default BpmDefinitionCreateReqDTO convert2(Model model) {
-        BpmDefinitionCreateReqDTO createReqDTO = new BpmDefinitionCreateReqDTO();
+    default BpmProcessDefinitionCreateReqDTO convert2(Model model, BpmFormDO form) {
+        BpmProcessDefinitionCreateReqDTO createReqDTO = new BpmProcessDefinitionCreateReqDTO();
         createReqDTO.setModelId(model.getId());
         createReqDTO.setName(model.getName());
         createReqDTO.setKey(model.getKey());
@@ -92,10 +92,15 @@ public interface BpmModelConvert {
         BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
         // metaInfo
         copyTo(metaInfo, createReqDTO);
+        // form
+        if (form != null) {
+            createReqDTO.setFormConf(form.getConf());
+            createReqDTO.setFormFields(form.getFields());
+        }
         return createReqDTO;
     }
 
-    void copyTo(BpmModelMetaInfoRespDTO from, @MappingTarget BpmDefinitionCreateReqDTO to);
+    void copyTo(BpmModelMetaInfoRespDTO from, @MappingTarget BpmProcessDefinitionCreateReqDTO to);
 
     default void copy(Model model, BpmModelCreateReqVO bean) {
         model.setName(bean.getName());

+ 11 - 10
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/definition/BpmProcessDefinitionConvert.java

@@ -4,13 +4,14 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.process
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.process.BpmProcessDefinitionRespVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmFormDO;
-import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmDefinitionCreateReqDTO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import org.activiti.engine.impl.persistence.entity.SuspensionState;
 import org.activiti.engine.repository.Deployment;
 import org.activiti.engine.repository.ProcessDefinition;
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
 import org.mapstruct.Named;
 import org.mapstruct.factory.Mappers;
 
@@ -38,34 +39,31 @@ public interface BpmProcessDefinitionConvert {
     }
 
     default BpmProcessDefinitionPageItemRespVO convert(ProcessDefinition bean, Deployment deployment,
-                                                       BpmProcessDefinitionExtDO processDefinitionDO, BpmFormDO form) {
+                                                       BpmProcessDefinitionExtDO processDefinitionExtDO, BpmFormDO form) {
         BpmProcessDefinitionPageItemRespVO respVO = convert(bean);
         respVO.setSuspensionState(bean.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
         if (deployment != null) {
             respVO.setDeploymentTime(deployment.getDeploymentTime());
         }
         if (form != null) {
-            respVO.setFormId(form.getId());
             respVO.setFormName(form.getName());
         }
-        if (processDefinitionDO != null) {
-            respVO.setDescription(processDefinitionDO.getDescription());
-        }
+        // 复制通用属性
+        copyTo(processDefinitionExtDO, respVO);
         return respVO;
     }
 
     BpmProcessDefinitionPageItemRespVO convert(ProcessDefinition bean);
 
-    BpmProcessDefinitionExtDO convert2(BpmDefinitionCreateReqDTO bean);
+    BpmProcessDefinitionExtDO convert2(BpmProcessDefinitionCreateReqDTO bean);
 
     default List<BpmProcessDefinitionRespVO> convertList3(List<ProcessDefinition> list,
                                                           Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap) {
         return CollectionUtils.convertList(list, processDefinition -> {
             BpmProcessDefinitionRespVO respVO = convert3(processDefinition);
             BpmProcessDefinitionExtDO processDefinitionExtDO = processDefinitionDOMap.get(processDefinition.getId());
-            if (processDefinitionExtDO != null) {
-                respVO.setFormId(processDefinitionExtDO.getFormId());
-            }
+            // 复制通用属性
+            copyTo(processDefinitionExtDO, respVO);
             return respVO;
         });
     }
@@ -79,4 +77,7 @@ public interface BpmProcessDefinitionConvert {
                 SuspensionState.ACTIVE.getStateCode();
     }
 
+    @Mapping(source = "from.id", target = "to.id", ignore = true)
+    void copyTo(BpmProcessDefinitionExtDO from, @MappingTarget BpmProcessDefinitionRespVO to);
+
 }

+ 57 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/message/BpmMessageConvert.java

@@ -0,0 +1,57 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.convert.message;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
+import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
+import org.activiti.api.task.model.Task;
+import org.activiti.engine.runtime.ProcessInstance;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.Mappings;
+import org.mapstruct.factory.Mappers;
+
+@Mapper
+public interface BpmMessageConvert {
+
+    BpmMessageConvert INSTANCE = Mappers.getMapper(BpmMessageConvert.class);
+
+    default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, SysUserDO startUser, Task task) {
+        BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();
+        copyTo(processInstance, reqDTO);
+        copyTo(startUser, reqDTO);
+        copyTo(task, reqDTO);
+        return reqDTO;
+    }
+    @Mapping(source = "name", target = "processInstanceName")
+    void copyTo(ProcessInstance from, @MappingTarget BpmMessageSendWhenTaskCreatedReqDTO to);
+    @Mappings({
+            @Mapping(source = "id", target = "startUserId"),
+            @Mapping(source = "nickname", target = "startUserNickname")
+    })
+    void copyTo(SysUserDO from, @MappingTarget BpmMessageSendWhenTaskCreatedReqDTO to);
+    @Mappings({
+            @Mapping(source = "id", target = "taskId"),
+            @Mapping(source = "name", target = "taskName"),
+            @Mapping(source = "assignee", target = "assigneeUserId")
+    })
+    void copyTo(Task task, @MappingTarget BpmMessageSendWhenTaskCreatedReqDTO to);
+
+    default BpmMessageSendWhenProcessInstanceRejectReqDTO convert(ProcessInstance processInstance, String comment) {
+        BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO = new BpmMessageSendWhenProcessInstanceRejectReqDTO();
+        copyTo(processInstance, reqDTO);
+        reqDTO.setComment(comment);
+        return reqDTO;
+    }
+    @Mapping(source = "name", target = "processInstanceName")
+    void copyTo(ProcessInstance from, @MappingTarget BpmMessageSendWhenProcessInstanceRejectReqDTO to);
+
+    @Mappings({
+            @Mapping(source = "id", target = "processInstanceId"),
+            @Mapping(source = "name", target = "processInstanceName"),
+            @Mapping(source = "initiator", target = "startUserId")
+    })
+    BpmMessageSendWhenProcessInstanceApproveReqDTO convert(org.activiti.api.process.model.ProcessInstance processInstance);
+
+}

+ 30 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/oa/BpmOALeaveConvert.java

@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.convert.oa;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.BpmOALeaveCreateReqVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.BpmOALeaveRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.BpmOALeaveDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * 请假申请 Convert
+ *
+ * @author 芋艿
+ */
+@Mapper
+public interface BpmOALeaveConvert {
+
+    BpmOALeaveConvert INSTANCE = Mappers.getMapper(BpmOALeaveConvert.class);
+
+    BpmOALeaveDO convert(BpmOALeaveCreateReqVO bean);
+
+    BpmOALeaveRespVO convert(BpmOALeaveDO bean);
+
+    List<BpmOALeaveRespVO> convertList(List<BpmOALeaveDO> list);
+
+    PageResult<BpmOALeaveRespVO> convertPage(PageResult<BpmOALeaveDO> page);
+
+}

+ 0 - 37
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/oa/OALeaveConvert.java

@@ -1,37 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.convert.oa;
-
-import java.util.*;
-
-import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.OALeaveDO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.OALeaveCreateReqVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.OALeaveExcelVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.OALeaveRespVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.OALeaveUpdateReqVO;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-/**
- * 请假申请 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);
-
-}

+ 33 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmActivityConvert.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.convert.task;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity.BpmActivityRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import org.activiti.engine.history.HistoricActivityInstance;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * BPM 活动 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface BpmActivityConvert {
+
+    BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class);
+
+    List<BpmActivityRespVO> convertList(List<HistoricActivityInstance> list);
+
+    @Mappings({
+            @Mapping(source = "activityId", target = "key"),
+            @Mapping(source = "activityType", target = "type")
+    })
+    BpmActivityRespVO convert(HistoricActivityInstance bean);
+
+}

+ 66 - 7
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmProcessInstanceConvert.java

@@ -1,13 +1,21 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.convert.task;
 
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstancePageItemRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.BpmTaskRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
+import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.event.BpmProcessInstanceResultEvent;
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysDeptDO;
+import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import org.activiti.engine.history.HistoricProcessInstance;
 import org.activiti.engine.repository.ProcessDefinition;
 import org.activiti.engine.runtime.ProcessInstance;
 import org.activiti.engine.task.Task;
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
 import org.mapstruct.Mappings;
 import org.mapstruct.factory.Mappers;
 
@@ -26,15 +34,20 @@ public interface BpmProcessInstanceConvert {
 
     BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class);
 
+    default BpmProcessInstanceExtDO convert3(ProcessInstance instance, ProcessDefinition definition) {
+        BpmProcessInstanceExtDO ext = new BpmProcessInstanceExtDO();
+        copyTo(instance, ext);
+        copyTo(definition, ext);
+        return ext;
+    }
+
     @Mappings({
-            @Mapping(source = "instance.startUserId", target = "startUserId"),
-            @Mapping(source = "instance.id", target = "processInstanceId"),
-            @Mapping(source = "instance.startTime", target = "createTime"),
-            @Mapping(source = "definition.id", target = "processDefinitionId"),
-            @Mapping(source = "definition.name", target = "name"),
-            @Mapping(source = "definition.category", target = "category")
+            @Mapping(source = "from.id", target = "id", ignore = true),
+            @Mapping(source = "from.startTime", target = "createTime"),
     })
-    BpmProcessInstanceExtDO convert(ProcessInstance instance, ProcessDefinition definition);
+    void copyTo(ProcessInstance from, @MappingTarget BpmProcessInstanceExtDO to);
+    @Mapping(source = "from.id", target = "id", ignore = true)
+    void copyTo(ProcessDefinition from, @MappingTarget BpmProcessInstanceExtDO to);
 
     default PageResult<BpmProcessInstancePageItemRespVO> convertPage(PageResult<BpmProcessInstanceExtDO> page,
                                                                      Map<String, List<Task>> taskMap) {
@@ -52,10 +65,56 @@ public interface BpmProcessInstanceConvert {
 
     @Mappings({
             @Mapping(source = "id", target = "processInstanceId"),
+            @Mapping(source = "id", target = "id", ignore = true),
             @Mapping(source = "startDate", target = "createTime"),
             @Mapping(source = "initiator", target = "startUserId"),
             @Mapping(source = "status", target = "status", ignore = true)
     })
     BpmProcessInstanceExtDO convert(org.activiti.api.process.model.ProcessInstance bean);
 
+    default BpmProcessInstanceRespVO convert2(HistoricProcessInstance processInstance, BpmProcessInstanceExtDO processInstanceExt,
+                                              ProcessDefinition processDefinition, BpmProcessDefinitionExtDO processDefinitionExt,
+                                              String bpmnXml, SysUserDO startUser, SysDeptDO dept) {
+        BpmProcessInstanceRespVO respVO = convert2(processInstance);
+        copyTo(processInstanceExt, respVO);
+        // definition
+        respVO.setProcessDefinition(convert2(processDefinition));
+        copyTo(processDefinitionExt, respVO.getProcessDefinition());
+        respVO.getProcessDefinition().setBpmnXml(bpmnXml);
+        // user
+        if (startUser != null) {
+            respVO.setStartUser(convert2(startUser));
+            if (dept != null) {
+                respVO.getStartUser().setDeptName(dept.getName());
+            }
+        }
+        return respVO;
+    }
+
+    BpmProcessInstanceRespVO convert2(HistoricProcessInstance bean);
+    @Mapping(source = "from.id", target = "to.id", ignore = true)
+    void copyTo(BpmProcessInstanceExtDO from, @MappingTarget BpmProcessInstanceRespVO to);
+    BpmProcessInstanceRespVO.ProcessDefinition convert2(ProcessDefinition bean);
+    @Mapping(source = "from.id", target = "to.id", ignore = true)
+    void copyTo(BpmProcessDefinitionExtDO from, @MappingTarget BpmProcessInstanceRespVO.ProcessDefinition to);
+    BpmProcessInstanceRespVO.User convert2(SysUserDO bean);
+
+    default BpmProcessInstanceResultEvent convert(Object source, ProcessInstance instance, Integer result) {
+        BpmProcessInstanceResultEvent event = new BpmProcessInstanceResultEvent(source);
+        event.setId(instance.getId());
+        event.setProcessDefinitionKey(instance.getProcessDefinitionKey());
+        event.setBusinessKey(instance.getBusinessKey());
+        event.setResult(result);
+        return event;
+    }
+
+    default BpmProcessInstanceResultEvent convert(Object source, HistoricProcessInstance instance, Integer result) {
+        BpmProcessInstanceResultEvent event = new BpmProcessInstanceResultEvent(source);
+        event.setId(instance.getId());
+        event.setProcessDefinitionKey(instance.getProcessDefinitionKey());
+        event.setBusinessKey(instance.getBusinessKey());
+        event.setResult(result);
+        return event;
+    }
+
 }

+ 69 - 46
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmTaskConvert.java

@@ -1,21 +1,19 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.convert.task;
 
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.BpmTaskDonePageItemRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.BpmTaskRespVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.BpmTaskTodoPageItemRespVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.TaskStepVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysDeptDO;
 import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-import org.activiti.engine.history.HistoricActivityInstance;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import org.activiti.engine.history.HistoricProcessInstance;
 import org.activiti.engine.history.HistoricTaskInstance;
 import org.activiti.engine.impl.persistence.entity.SuspensionState;
 import org.activiti.engine.runtime.ProcessInstance;
 import org.activiti.engine.task.Task;
-import org.mapstruct.Mapper;
-import org.mapstruct.Mapping;
-import org.mapstruct.Mappings;
-import org.mapstruct.Named;
+import org.mapstruct.*;
 import org.mapstruct.factory.Mappers;
 
 import java.util.List;
@@ -31,33 +29,21 @@ public interface BpmTaskConvert {
 
     BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class);
 
-    @Mappings(value = {
-            @Mapping(source = "activityName", target = "stepName"),
-            @Mapping(source = "assignee", target = "assignee")
-    })
-    TaskStepVO convert(HistoricActivityInstance instance);
-
-    default List<BpmTaskTodoPageItemRespVO> convertList(List<Task> tasks, Map<String, ProcessInstance> processInstanceMap,
-                                                        Map<Long, SysUserDO> userMap) {
+    default List<BpmTaskTodoPageItemRespVO> convertList1(List<Task> tasks, Map<String, ProcessInstance> processInstanceMap,
+                                                         Map<Long, SysUserDO> userMap) {
         return CollectionUtils.convertList(tasks, task -> {
+            BpmTaskTodoPageItemRespVO respVO = convert1(task);
             ProcessInstance processInstance = processInstanceMap.get(task.getProcessInstanceId());
-            return convert(task, processInstance, userMap.get(Long.valueOf(processInstance.getStartUserId())));
+            if (processInstance != null) {
+                SysUserDO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
+                respVO.setProcessInstance(convert(processInstance, startUser));
+            }
+            return respVO;
         });
     }
 
-    @Mappings({
-            @Mapping(source = "task.id", target = "id"),
-            @Mapping(source = "task.name", target = "name"),
-            @Mapping(source = "task.claimTime", target = "claimTime"),
-            @Mapping(source = "task.createTime", target = "createTime"),
-            @Mapping(source = "task.suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState"),
-            @Mapping(source = "processInstance.id", target = "processInstance.id"),
-            @Mapping(source = "processInstance.name", target = "processInstance.name"),
-            @Mapping(source = "processInstance.startUserId", target = "processInstance.startUserId"),
-            @Mapping(source = "processInstance.processDefinitionId", target = "processInstance.processDefinitionId"),
-            @Mapping(source = "user.nickname", target = "processInstance.startUserNickname")
-    })
-    BpmTaskTodoPageItemRespVO convert(Task task, ProcessInstance processInstance, SysUserDO user);
+    @Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState")
+    BpmTaskTodoPageItemRespVO convert1(Task bean);
 
     @Named("convertSuspendedToSuspensionState")
     default Integer convertSuspendedToSuspensionState(boolean suspended) {
@@ -69,29 +55,19 @@ public interface BpmTaskConvert {
                                                          Map<String, HistoricProcessInstance> historicProcessInstanceMap,
                                                          Map<Long, SysUserDO> userMap) {
         return CollectionUtils.convertList(tasks, task -> {
+            BpmTaskDonePageItemRespVO respVO = convert2(task);
             BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
+            copyTo(taskExtDO, respVO);
             HistoricProcessInstance processInstance = historicProcessInstanceMap.get(task.getProcessInstanceId());
-            SysUserDO userDO = userMap.get(Long.valueOf(processInstance.getStartUserId()));
-            return convert(task, taskExtDO, processInstance, userDO);
+            if (processInstance != null) {
+                SysUserDO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
+                respVO.setProcessInstance(convert(processInstance, startUser));
+            }
+            return respVO;
         });
     }
 
-    @Mappings({
-            @Mapping(source = "task.id", target = "id"),
-            @Mapping(source = "task.name", target = "name"),
-            @Mapping(source = "task.claimTime", target = "claimTime"),
-            @Mapping(source = "task.createTime", target = "createTime"),
-            @Mapping(source = "task.endTime", target = "endTime"),
-            @Mapping(source = "task.durationInMillis", target = "durationInMillis"),
-            @Mapping(source = "taskExtDO.result", target = "result"),
-            @Mapping(source = "taskExtDO.comment", target = "comment"),
-            @Mapping(source = "processInstance.id", target = "processInstance.id"),
-            @Mapping(source = "processInstance.name", target = "processInstance.name"),
-            @Mapping(source = "processInstance.startUserId", target = "processInstance.startUserId"),
-            @Mapping(source = "processInstance.processDefinitionId", target = "processInstance.processDefinitionId"),
-            @Mapping(source = "user.nickname", target = "processInstance.startUserNickname")
-    })
-    BpmTaskDonePageItemRespVO convert(HistoricTaskInstance task, BpmTaskExtDO taskExtDO, HistoricProcessInstance processInstance, SysUserDO user);
+    BpmTaskDonePageItemRespVO convert2(HistoricTaskInstance bean);
 
     @Mappings({
             @Mapping(source = "id", target = "taskId"),
@@ -100,4 +76,51 @@ public interface BpmTaskConvert {
     })
     BpmTaskExtDO convert(org.activiti.api.task.model.Task bean);
 
+    default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks, Map<String, BpmTaskExtDO> bpmTaskExtDOMap,
+                                             HistoricProcessInstance processInstance, Map<Long, SysUserDO> userMap,
+                                             Map<Long, SysDeptDO> deptMap) {
+        return CollectionUtils.convertList(tasks, task -> {
+            BpmTaskRespVO respVO = convert3(task);
+            BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
+            copyTo(taskExtDO, respVO);
+            if (processInstance != null) {
+                SysUserDO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
+                respVO.setProcessInstance(convert(processInstance, startUser));
+            }
+            SysUserDO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
+            if (assignUser != null) {
+                respVO.setAssigneeUser(convert3(assignUser));
+                SysDeptDO dept = deptMap.get(assignUser.getDeptId());
+                if (dept != null) {
+                    respVO.getAssigneeUser().setDeptName(dept.getName());
+                }
+            }
+            return respVO;
+        });
+    }
+
+    @Mapping(source = "taskDefinitionKey", target = "definitionKey")
+    BpmTaskRespVO convert3(HistoricTaskInstance bean);
+    BpmTaskRespVO.User convert3(SysUserDO bean);
+
+    void copyTo(BpmTaskExtDO from, @MappingTarget BpmTaskDonePageItemRespVO to);
+
+    @Mappings({
+            @Mapping(source = "processInstance.id", target = "id"),
+            @Mapping(source = "processInstance.name", target = "name"),
+            @Mapping(source = "processInstance.startUserId", target = "startUserId"),
+            @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
+            @Mapping(source = "startUser.nickname", target = "startUserNickname")
+    })
+    BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, SysUserDO startUser);
+
+    @Mappings({
+            @Mapping(source = "processInstance.id", target = "id"),
+            @Mapping(source = "processInstance.name", target = "name"),
+            @Mapping(source = "processInstance.startUserId", target = "startUserId"),
+            @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
+            @Mapping(source = "startUser.nickname", target = "startUserNickname")
+    })
+    BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance, SysUserDO startUser);
+
 }

+ 0 - 55
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/definition/BpmFormDataDO.java

@@ -1,55 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition;
-
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
-import lombok.*;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * 工作流的表单结果
- * 用户每次填写工作流的申请表单时,会保存一条记录到该表】
- *
- * @author 芋道源码
- */
-@TableName(value = "bpm_form_data", autoResultMap = true)
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class BpmFormDataDO extends BaseDO {
-
-    /**
-     * 编号
-     */
-    private Long id;
-    /**
-     * 表单编号
-     *
-     * 关联 {@link BpmFormDO#getId()}
-     */
-    private Long formId;
-    /**
-     * 状态
-     */
-    private Integer status;
-    /**
-     * 表单配置
-     *
-     * 冗余 {@link BpmFormDO#getFields()}
-     * 主要考虑,表单是可以修改的
-     */
-    @TableField(typeHandler = JacksonTypeHandler.class)
-    private List<String> fields;
-    /**
-     * 表单值
-     */
-    @TableField(typeHandler = JacksonTypeHandler.class)
-    private Map<String, Object> values;
-
-}

+ 40 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/definition/BpmProcessDefinitionExtDO.java

@@ -1,12 +1,17 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition;
 
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmModelFormTypeEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import lombok.*;
 import org.activiti.engine.repository.Model;
 import org.activiti.engine.repository.ProcessDefinition;
 
+import java.util.List;
+
 /**
  * Bpm 流程定义的拓展表
  * 主要解决 Activiti {@link ProcessDefinition} 不支持拓展字段,所以新建拓展表
@@ -43,11 +48,45 @@ public class BpmProcessDefinitionExtDO extends BaseDO {
      * 描述
      */
     private String description;
+
+    /**
+     * 表单类型
+     *
+     * 关联 {@link BpmModelFormTypeEnum}
+     */
+    private Integer formType;
     /**
-     * 表单编号
+     * 动态表单编号
+     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时
      *
      * 关联 {@link BpmFormDO#getId()}
      */
     private Long formId;
+    /**
+     * 表单的配置
+     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时
+     *
+     * 冗余 {@link BpmFormDO#getConf()}
+     */
+    private String formConf;
+    /**
+     * 表单项的数组
+     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时
+     *
+     * 冗余 {@link BpmFormDO#getFields()} ()}
+     */
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private List<String> formFields;
+    /**
+     * 自定义表单的提交路径,使用 Vue 的路由地址
+     * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时
+     */
+    private String formCustomCreatePath;
+    /**
+     * 自定义表单的查看路径,使用 Vue 的路由地址
+     * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时
+     */
+    private String formCustomViewPath;
+
 
 }

+ 74 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/leave/BpmOALeaveDO.java

@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave;
+
+import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
+import lombok.*;
+import java.util.*;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import org.activiti.engine.runtime.ProcessInstance;
+
+/**
+ * OA 请假申请 DO
+ *
+ * {@link #day} 请假天数,目前先简单做。一般是分成请假上午和下午,可以是 1 整天,可以是 0.5 半天
+ *
+ * @author jason
+ * @author 芋道源码
+ */
+@TableName("bpm_oa_leave")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class BpmOALeaveDO extends BaseDO {
+
+    /**
+     * 请假表单主键
+     */
+    @TableId
+    private Long id;
+    /**
+     * 申请人的用户编号
+     *
+     * 关联 {@link SysUserDO#getId()}
+     */
+    private Long userId;
+    /**
+     * 请假类型
+     */
+    @TableField("`type`")
+    private String type;
+    /**
+     * 原因
+     */
+    private String reason;
+    /**
+     * 开始时间
+     */
+    private Date startTime;
+    /**
+     * 结束时间
+     */
+    private Date endTime;
+    /**
+     * 请假天数
+     */
+    private Long day;
+    /**
+     * 请假的结果
+     *
+     * 枚举 {@link cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum}
+     * 考虑到简单,所以直接复用了 BpmProcessInstanceResultEnum 枚举,也可以自己定义一个枚举哈
+     */
+    private Integer result;
+
+    /**
+     * 对应的流程编号
+     *
+     * 关联 {@link ProcessInstance#getId()}
+     */
+    private String processInstanceId;
+
+}

+ 0 - 60
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/leave/OALeaveDO.java

@@ -1,60 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave;
-
-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;
-
-}

+ 0 - 4
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/leave/package-info.java

@@ -1,4 +0,0 @@
-/**
- * TODO 芋艿:实现请假流程,接入工作流
- */
-package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave;

+ 15 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/task/BpmProcessInstanceExtDO.java

@@ -3,7 +3,10 @@ package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task;
 import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
 import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceStatusEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
@@ -12,6 +15,7 @@ import org.activiti.engine.repository.ProcessDefinition;
 import org.activiti.engine.runtime.ProcessInstance;
 
 import java.util.Date;
+import java.util.Map;
 
 /**
  * Bpm 流程实例的拓展表
@@ -28,6 +32,11 @@ import java.util.Date;
 //@AllArgsConstructor
 public class BpmProcessInstanceExtDO extends BaseDO {
 
+    /**
+     * 编号,自增
+     */
+    @TableId
+    private Long id;
     /**
      * 发起流程的用户编号
      *
@@ -78,4 +87,10 @@ public class BpmProcessInstanceExtDO extends BaseDO {
      */
     private Date endTime;
 
+    /**
+     * 提交的表单值
+     */
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, Object> formVariables;
+
 }

+ 6 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/task/BpmTaskExtDO.java

@@ -46,6 +46,12 @@ public class BpmTaskExtDO extends BaseDO {
      * 关联 {@link Task#getId()}
      */
     private String taskId;
+//    /**
+//     * 任务的标识
+//     *
+//     * 关联 {@link Task#getTaskDefinitionKey()}
+//     */
+//    private String definitionKey;
     /**
      * 任务的结果
      *

+ 7 - 2
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/definition/BpmProcessDefinitionExtMapper.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.definition;
 
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
@@ -9,10 +10,14 @@ import java.util.Collection;
 import java.util.List;
 
 @Mapper
-public interface BpmProcessDefinitionExtMapper extends BaseMapper<BpmProcessDefinitionExtDO> {
+public interface BpmProcessDefinitionExtMapper extends BaseMapperX<BpmProcessDefinitionExtDO> {
 
     default List<BpmProcessDefinitionExtDO> selectListByProcessDefinitionIds(Collection<String> processDefinitionIds) {
-        return selectList(new QueryWrapper<BpmProcessDefinitionExtDO>().in("process_definition_id", processDefinitionIds));
+        return selectList("process_definition_id", processDefinitionIds);
+    }
+
+    default BpmProcessDefinitionExtDO selectByProcessDefinitionId(String processDefinitionId) {
+        return selectOne("process_definition_id", processDefinitionId);
     }
 
 }

+ 29 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/oa/BpmOALeaveMapper.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.oa;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.BpmOALeavePageReqVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.BpmOALeaveDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 请假申请 Mapper
+ *
+ * @author jason
+ * @author 芋道源码
+ */
+@Mapper
+public interface BpmOALeaveMapper extends BaseMapperX<BpmOALeaveDO> {
+
+    default PageResult<BpmOALeaveDO> selectPage(Long userId, BpmOALeavePageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<BpmOALeaveDO>()
+                .eqIfPresent(BpmOALeaveDO::getUserId, userId)
+                .eqIfPresent(BpmOALeaveDO::getResult, reqVO.getResult())
+                .eqIfPresent(BpmOALeaveDO::getType, reqVO.getType())
+                .likeIfPresent(BpmOALeaveDO::getReason, reqVO.getReason())
+                .betweenIfPresent(BpmOALeaveDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
+                .orderByDesc(BpmOALeaveDO::getId));
+    }
+
+}

+ 0 - 47
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/oa/OALeaveMapper.java

@@ -1,47 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.oa;
-
-import java.util.*;
-
-import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.OALeaveDO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.OALeaveExportReqVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.OALeavePageReqVO;
-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 org.apache.ibatis.annotations.Mapper;
-
-/**
- * 请假申请 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")        );
-    }
-
-}

+ 4 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmProcessInstanceExtMapper.java

@@ -23,6 +23,10 @@ public interface BpmProcessInstanceExtMapper extends BaseMapperX<BpmProcessInsta
                 .orderByDesc("id"));
     }
 
+    default BpmProcessInstanceExtDO selectByProcessInstanceId(String processDefinitionId) {
+        return selectOne("process_instance_id", processDefinitionId);
+    }
+
     default void updateByProcessInstanceId(BpmProcessInstanceExtDO updateObj) {
         update(updateObj, new QueryWrapper<BpmProcessInstanceExtDO>()
                 .eq("process_instance_id", updateObj.getProcessInstanceId()));

+ 3 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmTaskExtMapper.java

@@ -19,4 +19,7 @@ public interface BpmTaskExtMapper extends BaseMapperX<BpmTaskExtDO> {
         return selectList(BpmTaskExtDO::getTaskId, taskIds);
     }
 
+    default List<BpmTaskExtDO> selectListByProcessInstanceId(String processInstanceId) {
+        return selectList("process_instance_id", processInstanceId);
+    }
 }

+ 4 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java

@@ -28,12 +28,14 @@ public interface BpmErrorCodeConstants {
     ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1009002003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置");
     ErrorCode MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG = new ErrorCode(1009002004, "部署流程失败," +
             "原因:用户任务({})未配置分配规则,请点击【修改流程】按钮进行配置");
+    ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1009003005, "流程定义部署失败,原因:信息未发生变化");
 
     // ========== 流程定义 1-009-003-000 ==========
     ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1009003000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");
     ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1009003001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图");
     ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1009003002, "流程定义不存在");
-    ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1009003002, "流程定义处于挂起状态");
+    ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1009003003, "流程定义处于挂起状态");
+    ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1009003004, "流程定义的模型不存在");
 
     // ========== 流程实例 1-009-004-000 ==========
     ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1009004000, "流程实例不存在");
@@ -42,6 +44,7 @@ public interface BpmErrorCodeConstants {
 
     // ========== 流程任务 1-009-005-000 ==========
     ErrorCode TASK_COMPLETE_FAIL_NOT_EXISTS = new ErrorCode(1009004000, "审批任务失败,原因:该任务不处于未审批");
+    ErrorCode TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1009004001, "审批任务失败,原因:该任务的审批人不是你");
 
     // ========== 流程任务分配规则 1-009-006-000 ==========
     ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1009006000, "流程({}) 的任务({}) 已经存在分配规则");

+ 5 - 3
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/definition/BpmTaskRuleScriptEnum.java

@@ -5,7 +5,7 @@ import lombok.Getter;
 
 /**
  * BPM 任务规则的脚本枚举
- * 目前暂时通过 TODO 硬编码,未来可以考虑 Groovy 动态脚本的方式
+ * 目前暂时通过 TODO 芋艿:硬编码,未来可以考虑 Groovy 动态脚本的方式
  *
  * @author 芋道源码
  */
@@ -13,8 +13,10 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum BpmTaskRuleScriptEnum {
 
-    ONE(1L, ""),
-    TWO(2L, "");
+    START_USER(10L, "流程发起人"),
+
+    LEADER_X1(20L, "流程发起人的一级领导"),
+    LEADER_X2(21L, "流程发起人的二级领导");
 
     /**
      * 脚本编号

+ 28 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/message/BpmMessageEnum.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.enums.message;
+
+import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * Bpm 消息的枚举
+ *
+ * @author 芋道源码
+ */
+@AllArgsConstructor
+@Getter
+public enum BpmMessageEnum {
+
+    PROCESS_INSTANCE_APPROVE("bpm_process_instance_approve"), // 流程任务被审批通过时,发送给申请人
+    PROCESS_INSTANCE_REJECT("bpm_process_instance_reject"), // 流程任务被审批不通过时,发送给申请人
+    TASK_ASSIGNED("bpm_task_assigned"); // 任务被分配时,发送给审批人
+
+    /**
+     * 短信模板的标识
+     *
+     * 关联 {@link SysSmsTemplateDO#getCode()}
+     */
+    private final String smsCode;
+
+
+}

+ 19 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/task/BpmProcessInstanceDeleteReasonEnum.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.enums.task;
 
+import cn.hutool.core.util.StrUtil;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
@@ -12,8 +13,25 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum BpmProcessInstanceDeleteReasonEnum {
 
-    REJECT_TASK("驳回任务");
+    REJECT_TASK("不通过任务,原因:{}"), // 修改文案时,需要注意 isRejectReason 方法
+    CANCEL_TASK("主动取消任务,原因:{}");
 
     private final String reason;
 
+    /**
+     * 格式化理由
+     *
+     * @param args 参数
+     * @return 理由
+     */
+    public String format(Object... args) {
+        return StrUtil.format(reason, args);
+    }
+
+    // ========== 逻辑 ==========
+
+    public static boolean isRejectReason(String reason) {
+        return StrUtil.startWith(reason, "不通过任务,原因:");
+    }
+
 }

+ 12 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/config/BpmActivitiConfiguration.java

@@ -2,13 +2,17 @@ package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.config;
 
 import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.BpmActivityBehaviorFactory;
 import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.BpmTaskAssignScript;
+import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.event.BpmProcessInstanceResultEventPublisher;
+import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.identity.EmptyUserGroupManager;
 import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.listener.BpmTackActivitiEventListener;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskAssignRuleService;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmUserGroupService;
 import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService;
 import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService;
 import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
+import org.activiti.api.runtime.shared.identity.UserGroupManager;
 import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -24,7 +28,7 @@ import static org.activiti.spring.boot.ProcessEngineAutoConfiguration.BEHAVIOR_F
 public class BpmActivitiConfiguration {
 
     /**
-     * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类,主要设置各种监听器
+     * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类,主要设置各种监听器、用户组管理
      */
     @Bean
     public ProcessEngineConfigurationConfigurer bpmProcessEngineConfigurationConfigurer(
@@ -32,6 +36,8 @@ public class BpmActivitiConfiguration {
         return configuration -> {
             // 注册监听器,例如说 BpmActivitiEventListener
             configuration.setEventListeners(Collections.singletonList(taskActivitiEventListener));
+            // 用户组
+            configuration.setUserGroupManager(new EmptyUserGroupManager());
         };
     }
 
@@ -67,4 +73,9 @@ public class BpmActivitiConfiguration {
         return bpmActivityBehaviorFactory;
     }
 
+    @Bean
+    public BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher(ApplicationEventPublisher publisher) {
+        return new BpmProcessInstanceResultEventPublisher(publisher);
+    }
+
 }

+ 6 - 8
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehavior.java

@@ -37,6 +37,9 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString
 
 /**
  * 自定义的流程任务的 assignee 负责人的分配
+ * 第一步,获得对应的分配规则;
+ * 第二步,根据分配规则,计算出分配任务的候选人。如果找不到,则直接报业务异常,不继续执行后续的流程;
+ * 第三步,随机选择一个候选人,则选择作为 assignee 负责人。
  *
  * @author 芋道源码
  */
@@ -70,18 +73,13 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
     protected void handleAssignments(TaskEntityManager taskEntityManager,
                                      String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups,
                                      TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution) {
-        // 获得任务的规则
+        // 第一步,获得任务的规则
         BpmTaskAssignRuleDO rule = getTaskRule(task);
-        // 获得任务的候选用户们
+        // 第二步,获得任务的候选用户们
         Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule);
-        // 设置负责人
+        // 第三步,设置一个作为负责人
         Long assigneeUserId = chooseTaskAssignee(candidateUserIds);
         taskEntityManager.changeTaskAssignee(task, String.valueOf(assigneeUserId));
-        // 设置候选人们
-        candidateUserIds.remove(assigneeUserId); // 已经成为负责人了,就不要在扮演候选人了
-        if (CollUtil.isNotEmpty(candidateUserIds)) {
-            task.addCandidateUsers(convertSet(candidateUserIds, String::valueOf));
-        }
     }
 
     private BpmTaskAssignRuleDO getTaskRule(TaskEntity task) {

+ 2 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScri
 import org.activiti.engine.impl.persistence.entity.TaskEntity;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Bpm 任务分配的自定义 Script 脚本
@@ -22,7 +23,7 @@ public interface BpmTaskAssignScript {
      * @param task 任务
      * @return 候选人用户的编号数组
      */
-    List<Long> calculateTaskCandidateUsers(TaskEntity task);
+    Set<Long> calculateTaskCandidateUsers(TaskEntity task);
 
     /**
      * 获得枚举值

+ 62 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java

@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.impl;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.BpmTaskAssignScript;
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysDeptDO;
+import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService;
+import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
+import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
+import org.activiti.engine.impl.persistence.entity.TaskEntity;
+import org.springframework.util.Assert;
+
+import javax.annotation.Resource;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
+import static java.util.Collections.emptySet;
+
+/**
+ * 分配给发起人的 Leader 审批的 Script 实现类
+ * 目前 Leader 的定义是,
+ *
+ * @author 芋道源码
+ */
+public abstract class BpmTaskAssignLeaderAbstractScript implements BpmTaskAssignScript {
+
+    @Resource
+    private SysUserService userService;
+    @Resource
+    private SysDeptService deptService;
+
+    protected Set<Long> calculateTaskCandidateUsers(TaskEntity task, int level) {
+        Assert.isTrue(level > 0, "level 必须大于 0");
+        // 获得发起人
+        Long startUserId = Long.parseLong(task.getProcessInstance().getStartUserId());
+        // 获得对应 leve 的部门
+        SysDeptDO dept = null;
+        for (int i = 0; i < level; i++) {
+            // 获得 level 对应的部门
+            if (dept == null) {
+                dept = getStartUserDept(startUserId);
+                if (dept == null) { // 找不到发起人的部门,所以无法使用该规则
+                    return emptySet();
+                }
+            } else {
+                SysDeptDO parentDept = deptService.getDept(dept.getParentId());
+                if (parentDept == null) { // 找不到父级部门,所以只好结束寻找。原因是:例如说,级别比较高的人,所在部门层级比较少
+                    break;
+                }
+                dept = parentDept;
+            }
+        }
+        return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet();
+    }
+
+    private SysDeptDO getStartUserDept(Long startUserId) {
+        SysUserDO startUser = userService.getUser(startUserId);
+        if (startUser.getDeptId() == null) { // 找不到部门,所以无法使用该规则
+            return null;
+        }
+        return deptService.getDept(startUser.getDeptId());
+    }
+
+}

+ 27 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.impl;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import org.activiti.engine.impl.persistence.entity.TaskEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 分配给发起人的一级 Leader 审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignLeaderX1Script extends BpmTaskAssignLeaderAbstractScript {
+
+    @Override
+    public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
+        return calculateTaskCandidateUsers(task, 1);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.LEADER_X1;
+    }
+
+}

+ 27 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.impl;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import org.activiti.engine.impl.persistence.entity.TaskEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 分配给发起人的二级 Leader 审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignLeaderX2Script extends BpmTaskAssignLeaderAbstractScript {
+
+    @Override
+    public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
+        return calculateTaskCandidateUsers(task, 2);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.LEADER_X2;
+    }
+
+}

+ 30 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/impl/BpmTaskAssignStartUserScript.java

@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.impl;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScriptEnum;
+import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.BpmTaskAssignScript;
+import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
+import org.activiti.engine.impl.persistence.entity.TaskEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 分配给发起人审批的 Script 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskAssignStartUserScript implements BpmTaskAssignScript {
+
+    @Override
+    public Set<Long> calculateTaskCandidateUsers(TaskEntity task) {
+        Long userId = Long.parseLong(task.getProcessInstance().getStartUserId());
+        return SetUtils.asSet(userId);
+    }
+
+    @Override
+    public BpmTaskRuleScriptEnum getEnum() {
+        return BpmTaskRuleScriptEnum.START_USER;
+    }
+
+}

+ 44 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/event/BpmProcessInstanceResultEvent.java

@@ -0,0 +1,44 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.event;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
+import lombok.Data;
+import org.springframework.context.ApplicationEvent;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 流程实例的结果发生变化的 Event
+ * 定位:由于额外增加了 {@link BpmProcessInstanceExtDO#getResult()} 结果,所以增加该事件
+ *
+ * @author 芋道源码
+ */
+@SuppressWarnings("ALL")
+@Data
+public class BpmProcessInstanceResultEvent extends ApplicationEvent {
+
+    /**
+     * 流程实例的编号
+     */
+    @NotNull(message = "流程实例的编号不能为空")
+    private String id;
+    /**
+     * 流程实例的 key
+     */
+    @NotNull(message = "流程实例的 key 不能为空")
+    private String processDefinitionKey;
+    /**
+     * 流程实例的结果
+     */
+    @NotNull(message = "流程实例的结果不能为空")
+    private Integer result;
+    /**
+     * 流程实例对应的业务标识
+     * 例如说,请假
+     */
+    private String businessKey;
+
+    public BpmProcessInstanceResultEvent(Object source) {
+        super(source);
+    }
+
+}

+ 34 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/event/BpmProcessInstanceResultEventListener.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.event;
+
+import cn.hutool.core.util.StrUtil;
+import org.springframework.context.ApplicationListener;
+
+/**
+ * {@link BpmProcessInstanceResultEvent} 的监听器
+ *
+ * @author 芋道源码
+ */
+public abstract class BpmProcessInstanceResultEventListener
+        implements ApplicationListener<BpmProcessInstanceResultEvent> {
+
+    @Override
+    public final void onApplicationEvent(BpmProcessInstanceResultEvent event) {
+        if (!StrUtil.equals(event.getProcessDefinitionKey(), getProcessDefinitionKey())) {
+            return;
+        }
+        onEvent(event);
+    }
+
+    /**
+     * @return 返回监听的流程定义 Key
+     */
+    protected abstract String getProcessDefinitionKey();
+
+    /**
+     * 处理事件
+     *
+     * @param event 事件
+     */
+    protected abstract void onEvent(BpmProcessInstanceResultEvent event);
+
+}

+ 24 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/event/BpmProcessInstanceResultEventPublisher.java

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.event;
+
+import lombok.AllArgsConstructor;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.validation.annotation.Validated;
+
+import javax.validation.Valid;
+
+/**
+ * {@link BpmProcessInstanceResultEvent} 的生产者
+ *
+ * @author 芋道源码
+ */
+@AllArgsConstructor
+@Validated
+public class BpmProcessInstanceResultEventPublisher {
+
+    private final ApplicationEventPublisher publisher;
+
+    public void sendProcessInstanceResultEvent(@Valid BpmProcessInstanceResultEvent event) {
+        publisher.publishEvent(event);
+    }
+
+}

+ 6 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/event/package-info.java

@@ -0,0 +1,6 @@
+/**
+ * 自定义 Event 实现,提供方便业务接入的 Listener!
+ *
+ * @author 芋道源码
+ */
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.event;

+ 37 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/identity/EmptyUserGroupManager.java

@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.identity;
+
+import org.activiti.api.runtime.shared.identity.UserGroupManager;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 空的 UserGroupManager 实现类,用于禁用 Activiti 自带的用户组实现。
+ * 原因是,我们使用了自己实现的任务分配规则,所以不需要 Activiti。
+ * 如果不去禁用,会存在一些场景下,会去查询用户所在的用户组,导致报错。
+ *
+ * @author 芋道源码
+ */
+public class EmptyUserGroupManager implements UserGroupManager {
+
+    @Override
+    public List<String> getUserGroups(String s) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public List<String> getUserRoles(String s) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public List<String> getGroups() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public List<String> getUsers() {
+        return Collections.emptyList();
+    }
+
+}

+ 3 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/listener/BpmProcessInstanceEventListener.java

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceS
 import org.activiti.api.model.shared.event.RuntimeEvent;
 import org.activiti.api.process.model.ProcessInstance;
 import org.activiti.api.process.model.events.ProcessRuntimeEvent;
+import org.activiti.api.process.runtime.events.ProcessCancelledEvent;
 import org.activiti.api.process.runtime.events.listener.ProcessEventListener;
 import org.activiti.api.process.runtime.events.listener.ProcessRuntimeEventListener;
 import org.activiti.api.task.model.events.TaskRuntimeEvent;
@@ -42,7 +43,8 @@ public class BpmProcessInstanceEventListener<T extends RuntimeEvent<?, ?>>
         }
         // 取消时,更新拓展表为取消
         if (event.getEventType() == ProcessRuntimeEvent.ProcessEvents.PROCESS_CANCELLED) {
-            processInstanceService.updateProcessInstanceExtCancel(event.getEntity());
+            processInstanceService.updateProcessInstanceExtCancel(event.getEntity(),
+                    ((ProcessCancelledEvent) event).getCause());
             return;
         }
         // 完成时,更新拓展表为已完成

+ 6 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/listener/BpmTaskEventListener.java

@@ -53,6 +53,12 @@ public class BpmTaskEventListener<T extends RuntimeEvent<?, ?>>
             return;
         }
 
+        // 审核人修改时,进行拓展表,并额外发送通知
+        if (event.getEventType() == TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED) {
+            taskService.updateTaskExtAssign(event.getEntity());
+            return;
+        }
+
         // 其它事件,进行更新拓展表
         taskService.updateTaskExt(event.getEntity());
     }

+ 28 - 2
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/BpmProcessDefinitionService.java

@@ -4,7 +4,8 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.process
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.process.BpmProcessDefinitionPageReqVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.process.BpmProcessDefinitionRespVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmDefinitionCreateReqDTO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import org.activiti.bpmn.model.BpmnModel;
@@ -21,6 +22,7 @@ import java.util.Set;
  *
  * @author yunlong.li
  * @author ZJQ
+ * @author 芋道源码
  */
 public interface BpmProcessDefinitionService {
 
@@ -74,6 +76,22 @@ public interface BpmProcessDefinitionService {
      */
     ProcessDefinition getProcessDefinition2(String id);
 
+    /**
+     * 获得流程定义标识对应的激活的流程定义
+     *
+     * @param key 流程定义的标识
+     * @return 流程定义
+     */
+    ProcessDefinition getActiveProcessDefinition(String key);
+
+    /**
+     * 获得编号对应的 BpmProcessDefinitionExtDO
+     *
+     * @param id 编号
+     * @return 流程定义拓展
+     */
+    BpmProcessDefinitionExtDO getProcessDefinitionExt(String id);
+
     /**
      * 获得 id 对应的 Deployment
      *
@@ -116,13 +134,21 @@ public interface BpmProcessDefinitionService {
      */
     List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds);
 
+    /**
+     * 获得需要创建的流程定义,是否和当前激活的流程定义相等
+     *
+     * @param createReqDTO 创建信息
+     * @return 是否相等
+     */
+    boolean isProcessDefinitionEquals(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO);
+
     /**
      * 创建流程定义
      *
      * @param createReqDTO 创建信息
      * @return 流程编号
      */
-    String createProcessDefinition(@Valid BpmDefinitionCreateReqDTO createReqDTO);
+    String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO);
 
     /**
      * 更新流程定义的挂起状态

+ 9 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/BpmTaskAssignRuleService.java

@@ -59,6 +59,15 @@ public interface BpmTaskAssignRuleService {
      */
     void updateTaskAssignRule(@Valid BpmTaskAssignRuleUpdateReqVO reqVO);
 
+    /**
+     * 判断指定流程模型和流程定义的分配规则是否相等
+     *
+     * @param modelId 流程模型编号
+     * @param processDefinitionId 流程定义编号
+     * @return 是否相等
+     */
+    boolean isTaskAssignRulesEquals(String modelId, String processDefinitionId);
+
     /**
      * 将流程流程模型的任务分配规则,复制一份给流程定义
      * 目的:每次流程模型部署时,都会生成一个新的流程定义,此时考虑到每次部署的流程不可变性,所以需要复制一份给该流程定义

+ 33 - 2
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/dto/BpmDefinitionCreateReqDTO.java → yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java

@@ -1,16 +1,21 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmModelFormTypeEnum;
 import lombok.Data;
 
+import javax.validation.constraints.AssertTrue;
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Objects;
 
 /**
  * 流程定义创建 Request DTO
  */
 @Data
-public class BpmDefinitionCreateReqDTO {
+public class BpmProcessDefinitionCreateReqDTO {
 
     // ========== 模型相关 ==========
 
@@ -43,7 +48,7 @@ public class BpmDefinitionCreateReqDTO {
      * BPMN XML
      */
     @NotEmpty(message = "BPMN XML 不能为空")
-    private String bpmnXml;
+    private byte[] bpmnBytes;
 
     // ========== 表单相关 ==========
 
@@ -57,6 +62,16 @@ public class BpmDefinitionCreateReqDTO {
      * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时
      */
     private Long formId;
+    /**
+     * 表单的配置
+     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时
+     */
+    private String formConf;
+    /**
+     * 表单项的数组
+     * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时
+     */
+    private List<String> formFields;
     /**
      * 自定义表单的提交路径,使用 Vue 的路由地址
      * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时
@@ -68,6 +83,22 @@ public class BpmDefinitionCreateReqDTO {
      */
     private String formCustomViewPath;
 
+    @AssertTrue(message = "流程表单信息不全")
+    public boolean isNormalFormTypeValid() {
+        // 如果非业务表单,则直接通过
+        if (!Objects.equals(formType, BpmModelFormTypeEnum.NORMAL.getType())) {
+            return true;
+        }
+        return formId != null && StrUtil.isNotEmpty(formConf) && CollUtil.isNotEmpty(formFields);
+    }
 
+    @AssertTrue(message = "业务表单信息不全")
+    public boolean isNormalCustomTypeValid() {
+        // 如果非业务表单,则直接通过
+        if (!Objects.equals(formType, BpmModelFormTypeEnum.CUSTOM.getType())) {
+            return true;
+        }
+        return StrUtil.isNotEmpty(formCustomCreatePath) && StrUtil.isNotEmpty(formCustomViewPath);
+    }
 
 }

+ 50 - 26
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmModelServiceImpl.java

@@ -7,9 +7,10 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.model.*
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.rule.BpmTaskAssignRuleRespVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.convert.definition.BpmModelConvert;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmFormDO;
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmModelFormTypeEnum;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskAssignRuleService;
-import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmDefinitionCreateReqDTO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmFormService;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmModelService;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
@@ -20,11 +21,9 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import lombok.extern.slf4j.Slf4j;
-import org.activiti.bpmn.converter.BpmnXMLConverter;
 import org.activiti.bpmn.model.BpmnModel;
 import org.activiti.engine.RepositoryService;
 import org.activiti.engine.impl.persistence.entity.SuspensionState;
-import org.activiti.engine.impl.util.io.StringStreamSource;
 import org.activiti.engine.repository.Deployment;
 import org.activiti.engine.repository.Model;
 import org.activiti.engine.repository.ModelQuery;
@@ -36,11 +35,7 @@ import org.springframework.util.ObjectUtils;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Consumer;
+import java.util.*;
 
 import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -52,6 +47,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
  * 主要进行 Activiti {@link Model} 的维护
  *
  * @author yunlongn
+ * @author 芋道源码
  */
 @Service
 @Validated
@@ -176,25 +172,23 @@ public class BpmModelServiceImpl implements BpmModelService {
         }
         // TODO 芋艿:校验流程图的有效性;例如说,是否有开始的元素,是否有结束的元素;
         // 校验表单已配
-        BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
-        if (metaInfo == null || metaInfo.getFormType() == null) {
-            throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
-        }
+        BpmFormDO form = checkFormConfig(model);
         // 校验任务分配规则已配置
         checkTaskAssignRuleAllConfig(id);
 
+        // 校验模型是否发生修改。如果未修改,则不允许创建
+        BpmProcessDefinitionCreateReqDTO definitionCreateReqDTO = BpmModelConvert.INSTANCE.convert2(model, form).setBpmnBytes(bpmnBytes);
+        if (processDefinitionService.isProcessDefinitionEquals(definitionCreateReqDTO)) { // 流程定义的信息相等
+            ProcessDefinition oldProcessInstance = processDefinitionService.getProcessDefinitionByDeploymentId(model.getDeploymentId());
+            if (oldProcessInstance != null && taskAssignRuleService.isTaskAssignRulesEquals(model.getId(), oldProcessInstance.getId())) {
+                throw exception(MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS);
+            }
+        }
         // 创建流程定义
-        BpmDefinitionCreateReqDTO definitionCreateReqDTO = BpmModelConvert.INSTANCE.convert2(model)
-                .setBpmnXml(StrUtil.utf8Str(bpmnBytes));
         String definitionId = processDefinitionService.createProcessDefinition(definitionCreateReqDTO);
 
         // 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。
-        if (StrUtil.isNotEmpty(model.getDeploymentId())) {
-            ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(model.getDeploymentId());
-            if (oldDefinition != null) {
-                processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), SuspensionState.SUSPENDED.getStateCode());
-            }
-        }
+        updateProcessDefinitionSuspended(model.getDeploymentId());
 
         // 更新 model 的 deploymentId,进行关联
         ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId);
@@ -225,7 +219,30 @@ public class BpmModelServiceImpl implements BpmModelService {
         });
     }
 
+    /**
+     * 校验流程表单已配置
+     *
+     * @param model 流程模型
+     * @return 流程表单
+     */
+    private BpmFormDO checkFormConfig(Model model) {
+        BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
+        if (metaInfo == null || metaInfo.getFormType() == null) {
+            throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
+        }
+        // 校验表单存在
+        if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) {
+            BpmFormDO form = formService.getForm(metaInfo.getFormId());
+            if (form == null) {
+                throw exception(FORM_NOT_EXISTS);
+            }
+            return form;
+        }
+        return null;
+    }
+
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void deleteModel(String id) {
         // 校验流程模型存在
         Model model = repositoryService.getModel(id);
@@ -234,6 +251,19 @@ public class BpmModelServiceImpl implements BpmModelService {
         }
         // 执行删除
         repositoryService.deleteModel(id);
+        // 禁用流程实例
+        updateProcessDefinitionSuspended(model.getDeploymentId());
+    }
+
+    private void updateProcessDefinitionSuspended(String deploymentId) {
+        if (StrUtil.isEmpty(deploymentId)) {
+            return;
+        }
+        ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId);
+        if (oldDefinition == null) {
+            return;
+        }
+        processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), SuspensionState.SUSPENDED.getStateCode());
     }
 
     @Override
@@ -272,10 +302,4 @@ public class BpmModelServiceImpl implements BpmModelService {
         }
     }
 
-    public static void main(String[] args) {
-        // 创建转换对象
-        BpmnXMLConverter converter = new BpmnXMLConverter();
-        BpmnModel bpmnModel = converter.convertToBpmnModel(new StringStreamSource(""), true, true);
-    }
-
 }

+ 66 - 24
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmProcessDefinitionServiceImpl.java

@@ -1,23 +1,23 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.service.definition.impl;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.process.BpmProcessDefinitionListReqVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.process.BpmProcessDefinitionPageReqVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.process.BpmProcessDefinitionRespVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.convert.definition.BpmProcessDefinitionConvert;
-import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmFormDO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.definition.BpmProcessDefinitionExtMapper;
-import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
-import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmDefinitionCreateReqDTO;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmFormService;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
+import cn.iocoder.yudao.framework.activiti.core.util.ActivitiUtils;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
 import lombok.extern.slf4j.Slf4j;
-import org.activiti.bpmn.converter.BpmnXMLConverter;
 import org.activiti.bpmn.model.BpmnModel;
 import org.activiti.engine.RepositoryService;
 import org.activiti.engine.impl.persistence.entity.SuspensionState;
@@ -31,10 +31,10 @@ import org.springframework.validation.annotation.Validated;
 import javax.annotation.Resource;
 import java.util.*;
 
-import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.PROCESS_DEFINITION_KEY_NOT_MATCH;
-import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.PROCESS_DEFINITION_NAME_NOT_MATCH;
+import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
+import static java.util.Collections.emptyList;
 
 /**
  * 流程定义实现
@@ -42,6 +42,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
  *
  * @author yunlongn
  * @author ZJQ
+ * @author 芋道源码
  */
 @Service
 @Validated
@@ -50,8 +51,6 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
 
     private static final String BPMN_FILE_SUFFIX = ".bpmn";
 
-    private static final BpmnXMLConverter BPMN_XML_CONVERTER = new BpmnXMLConverter();
-
     @Resource
     private RepositoryService repositoryService;
     @Resource
@@ -67,25 +66,25 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
             definitionQuery.processDefinitionKey(pageVO.getKey());
         }
         // 执行查询
-        List<ProcessDefinition> processDefinitions = definitionQuery.orderByProcessDefinitionId().desc()
+        List<ProcessDefinition> processDefinitions = definitionQuery.orderByProcessDefinitionVersion().desc()
                 .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
         if (CollUtil.isEmpty(processDefinitions)) {
-            return new PageResult<>(Collections.emptyList(), definitionQuery.count());
+            return new PageResult<>(emptyList(), definitionQuery.count());
         }
 
         // 获得 Deployment Map
         Set<String> deploymentIds = new HashSet<>();
-        processDefinitions.forEach(definition -> CollectionUtils.addIfNotNull(deploymentIds, definition.getDeploymentId()));
+        processDefinitions.forEach(definition -> addIfNotNull(deploymentIds, definition.getDeploymentId()));
         Map<String, Deployment> deploymentMap = getDeploymentMap(deploymentIds);
 
         // 获得 BpmProcessDefinitionDO Map
         List<BpmProcessDefinitionExtDO> processDefinitionDOs = processDefinitionMapper.selectListByProcessDefinitionIds(
                 convertList(processDefinitions, ProcessDefinition::getId));
-        Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap = CollectionUtils.convertMap(processDefinitionDOs,
+        Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap = convertMap(processDefinitionDOs,
                 BpmProcessDefinitionExtDO::getProcessDefinitionId);
 
         // 获得 Form Map
-        Set<Long> formIds = CollectionUtils.convertSet(processDefinitionDOs, BpmProcessDefinitionExtDO::getFormId);
+        Set<Long> formIds = convertSet(processDefinitionDOs, BpmProcessDefinitionExtDO::getFormId);
         Map<Long, BpmFormDO> formMap = bpmFormService.getFormMap(formIds);
 
         // 拼接结果
@@ -109,7 +108,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
         // 获得 BpmProcessDefinitionDO Map
         List<BpmProcessDefinitionExtDO> processDefinitionDOs = processDefinitionMapper.selectListByProcessDefinitionIds(
                 convertList(processDefinitions, ProcessDefinition::getId));
-        Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap = CollectionUtils.convertMap(processDefinitionDOs,
+        Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap = convertMap(processDefinitionDOs,
                 BpmProcessDefinitionExtDO::getProcessDefinitionId);
         // 执行查询,并返回
         return BpmProcessDefinitionConvert.INSTANCE.convertList3(processDefinitions, processDefinitionDOMap);
@@ -121,9 +120,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
         if (bpmnModel == null) {
             return null;
         }
-        // TODO 芋艿:重构到 activi util 里
-        byte[] bpmnBytes = BPMN_XML_CONVERTER.convertToXML(bpmnModel);
-        return StrUtil.utf8Str(bpmnBytes);
+        return ActivitiUtils.getBpmnXml(bpmnModel);
     }
 
     @Override
@@ -141,6 +138,16 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
         return repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
     }
 
+    @Override
+    public ProcessDefinition getActiveProcessDefinition(String key) {
+        return repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).active().singleResult();
+    }
+
+    @Override
+    public BpmProcessDefinitionExtDO getProcessDefinitionExt(String id) {
+        return processDefinitionMapper.selectByProcessDefinitionId(id);
+    }
+
     @Override
     public Deployment getDeployment(String id) {
         if (StrUtil.isEmpty(id)) {
@@ -152,35 +159,70 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
     @Override
     public List<Deployment> getDeployments(Set<String> ids) {
         if (CollUtil.isEmpty(ids)) {
-            return Collections.emptyList();
+            return emptyList();
         }
         List<Deployment> list = new ArrayList<>(ids.size());
         for (String id : ids) {
-            CollectionUtils.addIfNotNull(list, getDeployment(id));
+            addIfNotNull(list, getDeployment(id));
         }
         return list;
     }
 
     @Override
     public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) {
+        if (StrUtil.isEmpty(deploymentId)) {
+            return null;
+        }
         return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
     }
 
     @Override
     public List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds) {
         if (CollUtil.isEmpty(deploymentIds)) {
-            return Collections.emptyList();
+            return emptyList();
         }
         return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list();
     }
 
+    @Override
+    public boolean isProcessDefinitionEquals(BpmProcessDefinitionCreateReqDTO createReqDTO) {
+        // 校验 name、description 是否更新
+        ProcessDefinition oldProcessDefinition = getActiveProcessDefinition(createReqDTO.getKey());
+        if (oldProcessDefinition == null) {
+            return false;
+        }
+        BpmProcessDefinitionExtDO oldProcessDefinitionExt = getProcessDefinitionExt(oldProcessDefinition.getId());
+        if (!StrUtil.equals(createReqDTO.getName(), oldProcessDefinition.getName())
+                || !StrUtil.equals(createReqDTO.getDescription(), oldProcessDefinitionExt.getDescription())
+                || !StrUtil.equals(createReqDTO.getCategory(), oldProcessDefinition.getCategory())) {
+            return false;
+        }
+        // 校验 form 信息是否更新
+        if (!ObjectUtil.equal(createReqDTO.getFormType(), oldProcessDefinitionExt.getFormType())
+                || !ObjectUtil.equal(createReqDTO.getFormId(), oldProcessDefinitionExt.getFormId())
+                || !ObjectUtil.equal(createReqDTO.getFormConf(), oldProcessDefinitionExt.getFormConf())
+                || !ObjectUtil.equal(createReqDTO.getFormFields(), oldProcessDefinitionExt.getFormFields())
+                || !ObjectUtil.equal(createReqDTO.getFormCustomCreatePath(), oldProcessDefinitionExt.getFormCustomCreatePath())
+                || !ObjectUtil.equal(createReqDTO.getFormCustomViewPath(), oldProcessDefinitionExt.getFormCustomViewPath())) {
+            return false;
+        }
+        // 校验 BPMN XML 信息
+        BpmnModel newModel = ActivitiUtils.buildBpmnModel(createReqDTO.getBpmnBytes());
+        BpmnModel oldModel = getBpmnModel(oldProcessDefinition.getId());
+        if (!ActivitiUtils.equals(oldModel, newModel)) {
+            return false;
+        }
+        // 最终发现都一致,则返回 true
+        return true;
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class) // 因为进行多个 activiti 操作,所以开启事务
-    public String createProcessDefinition(BpmDefinitionCreateReqDTO createReqDTO) {
+    public String createProcessDefinition(BpmProcessDefinitionCreateReqDTO createReqDTO) {
         // 创建 Deployment 部署
         Deployment deploy = repositoryService.createDeployment()
                 .key(createReqDTO.getKey()).name(createReqDTO.getName()).category(createReqDTO.getCategory())
-                .addString(createReqDTO.getKey() + BPMN_FILE_SUFFIX, createReqDTO.getBpmnXml())
+                .addBytes(createReqDTO.getKey() + BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes())
                 .deploy();
 
         // 设置 ProcessDefinition 的 category 分类

+ 29 - 4
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmTaskAssignRuleServiceImpl.java

@@ -1,6 +1,9 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.service.definition.impl;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.rule.BpmTaskAssignRuleRespVO;
@@ -30,10 +33,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
 
 import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
 import static cn.iocoder.yudao.adminserver.modules.system.enums.SysDictTypeConstants.BPM_TASK_ASSIGN_RULE_TYPE;
@@ -142,6 +142,31 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
         taskRuleMapper.updateById(BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO));
     }
 
+    @Override
+    public boolean isTaskAssignRulesEquals(String modelId, String processDefinitionId) {
+        // 调用 VO 接口的原因是,过滤掉流程模型不需要的规则,保持和 copyTaskAssignRules 方法的一致性
+        List<BpmTaskAssignRuleRespVO> modelRules = getTaskAssignRuleList(modelId, null);
+        List<BpmTaskAssignRuleRespVO> processInstanceRules = getTaskAssignRuleList(null, processDefinitionId);
+        if (modelRules.size() != processInstanceRules.size()) {
+            return false;
+        }
+
+        // 遍历,匹配对应的规则
+        Map<String, BpmTaskAssignRuleRespVO> processInstanceRuleMap = CollectionUtils.convertMap(processInstanceRules,
+                BpmTaskAssignRuleRespVO::getTaskDefinitionKey);
+        for (BpmTaskAssignRuleRespVO modelRule : modelRules) {
+            BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey());
+            if (processInstanceRule == null) {
+                return false;
+            }
+            if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType())
+                || !ObjectUtil.equal(modelRule.getOptions(), processInstanceRule.getOptions())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     @Override
     public void copyTaskAssignRules(String fromModelId, String toProcessDefinitionId) {
         List<BpmTaskAssignRuleRespVO> rules = getTaskAssignRuleList(fromModelId, null);

+ 40 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/message/BpmMessageService.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.message;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
+
+import javax.validation.Valid;
+
+/**
+ * BPM 消息 Service 接口
+ *
+ * TODO 芋艿:未来支持消息的可配置;不同的流程,在什么场景下,需要发送什么消息,消息的内容是什么;
+ *
+ * @author 芋道源码
+ */
+public interface BpmMessageService {
+
+    /**
+     * 发送流程实例被通过的消息
+     *
+     * @param reqDTO 发送信息
+     */
+    void sendMessageWhenProcessInstanceApprove(@Valid BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO);
+
+
+    /**
+     * 发送流程实例被不通过的消息
+     *
+     * @param reqDTO 发送信息
+     */
+    void sendMessageWhenProcessInstanceReject(@Valid BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO);
+
+    /**
+     * 发送任务被分配的消息
+     *
+     * @param reqDTO 发送信息
+     */
+    void sendMessageWhenTaskAssigned(@Valid BpmMessageSendWhenTaskCreatedReqDTO reqDTO);
+
+}

+ 27 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+/**
+ * BPM 发送流程实例被通过 Request DTO
+ */
+@Data
+public class BpmMessageSendWhenProcessInstanceApproveReqDTO {
+
+    /**
+     * 流程实例的编号
+     */
+    @NotEmpty(message = "流程实例的编号不能为空")
+    private String processInstanceId;
+    /**
+     * 流程实例的名字
+     */
+    @NotEmpty(message = "流程实例的名字不能为空")
+    private String processInstanceName;
+    @NotNull(message = "发起人的用户编号")
+    private Long startUserId;
+
+}

+ 33 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+/**
+ * BPM 发送流程实例被不通过 Request DTO
+ */
+@Data
+public class BpmMessageSendWhenProcessInstanceRejectReqDTO {
+
+    /**
+     * 流程实例的编号
+     */
+    @NotEmpty(message = "流程实例的编号不能为空")
+    private String processInstanceId;
+    /**
+     * 流程实例的名字
+     */
+    @NotEmpty(message = "流程实例的名字不能为空")
+    private String processInstanceName;
+    @NotNull(message = "发起人的用户编号")
+    private Long startUserId;
+
+    /**
+     * 不通过理由
+     */
+    @NotEmpty(message = "不通过理由不能为空")
+    private String comment;
+
+}

+ 46 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java

@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+/**
+ * BPM 发送任务被分配 Request DTO
+ */
+@Data
+public class BpmMessageSendWhenTaskCreatedReqDTO {
+
+    /**
+     * 流程实例的编号
+     */
+    @NotEmpty(message = "流程实例的编号不能为空")
+    private String processInstanceId;
+    /**
+     * 流程实例的名字
+     */
+    @NotEmpty(message = "流程实例的名字不能为空")
+    private String processInstanceName;
+    @NotEmpty(message = "发起人的用户编号")
+    private String startUserId;
+    @NotEmpty(message = "发起人的昵称")
+    private String startUserNickname;
+
+    /**
+     * 流程任务的编号
+     */
+    @NotEmpty(message = "流程任务的编号不能为空")
+    private String taskId;
+    /**
+     * 流程任务的名字
+     */
+    @NotEmpty(message = "流程任务的名字不能为空")
+    private String taskName;
+
+    /**
+     * 审批人的用户编号
+     */
+    @NotNull(message = "审批人的用户编号不能为空")
+    private Long assigneeUserId;
+
+}

+ 68 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/message/impl/BpmMessageServiceImpl.java

@@ -0,0 +1,68 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.message.impl;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.message.BpmMessageEnum;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.message.BpmMessageService;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
+import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * BPM 消息 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+@Slf4j
+public class BpmMessageServiceImpl implements BpmMessageService {
+
+    @Resource
+    private SysSmsCoreService smsCoreService;
+
+    @Value("${yudao.url.admin-ui}")
+    private String adminUiUrl;
+
+    @Override
+    public void sendMessageWhenProcessInstanceApprove(BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO) {
+        Map<String, Object> templateParams = new HashMap<>();
+        templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
+        templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));
+        smsCoreService.sendSingleSmsToAdmin(null, reqDTO.getStartUserId(),
+                BpmMessageEnum.PROCESS_INSTANCE_APPROVE.getSmsCode(), templateParams);
+    }
+
+    @Override
+    public void sendMessageWhenProcessInstanceReject(BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO) {
+        Map<String, Object> templateParams = new HashMap<>();
+        templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
+        templateParams.put("comment", reqDTO.getComment());
+        templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));
+        smsCoreService.sendSingleSmsToAdmin(null, reqDTO.getStartUserId(),
+                BpmMessageEnum.PROCESS_INSTANCE_REJECT.getSmsCode(), templateParams);
+    }
+
+    @Override
+    public void sendMessageWhenTaskAssigned(BpmMessageSendWhenTaskCreatedReqDTO reqDTO) {
+        Map<String, Object> templateParams = new HashMap<>();
+        templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
+        templateParams.put("taskName", reqDTO.getTaskName());
+        templateParams.put("startUserNickname", reqDTO.getStartUserNickname());
+        templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));
+        smsCoreService.sendSingleSmsToAdmin(null, reqDTO.getAssigneeUserId(),
+                BpmMessageEnum.TASK_ASSIGNED.getSmsCode(), templateParams);
+    }
+
+    private String getProcessInstanceDetailUrl(String taskId) {
+        return adminUiUrl + "bpm/process-instance/detail?id=" + taskId;
+    }
+
+}

+ 52 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/BpmOALeaveService.java

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.oa;
+
+
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.*;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.BpmOALeaveDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import javax.validation.Valid;
+
+/**
+ * 请假申请 Service 接口
+ *
+ * @author jason
+ * @author 芋道源码
+ */
+public interface BpmOALeaveService {
+
+    /**
+     * 创建请假申请
+     *
+     * @param userId 用户编号
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createLeave(Long userId, @Valid BpmOALeaveCreateReqVO createReqVO);
+
+    /**
+     * 更新请假申请的状态
+     *
+     * @param id 编号
+     * @param result 结果
+     */
+    void updateLeaveResult(Long id, Integer result);
+
+    /**
+     * 获得请假申请
+     *
+     * @param id 编号
+     * @return 请假申请
+     */
+    BpmOALeaveDO getLeave(Long id);
+
+    /**
+     * 获得请假申请分页
+     *
+     * @param userId 用户编号
+     * @param pageReqVO 分页查询
+     * @return 请假申请分页
+     */
+    PageResult<BpmOALeaveDO> getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO);
+
+}

+ 0 - 40
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/LeaveApplyEndProcessor.java

@@ -1,40 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.service.oa;
-
-import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.OALeaveDO;
-import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.oa.OALeaveMapper;
-import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
-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;
-import java.util.Objects;
-
-
-/**
- * 请假流程结束流程监听处理服务, 根据请假申请审批通过,还是未通过, 更新请假表单
- */
-@Component
-public class LeaveApplyEndProcessor implements ExecutionListener {
-
-    @Resource
-    private OALeaveMapper leaveMapper;
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public void notify(DelegateExecution delegateExecution) {
-        final String businessKey = delegateExecution.getProcessInstanceBusinessKey();
-        final Object approved = delegateExecution.getVariable("approved");
-        OALeaveDO updateDo = new OALeaveDO();
-        updateDo.setId(Long.valueOf(businessKey));
-        if (Objects.equals(approved, true)) {
-            updateDo.setStatus(BpmProcessInstanceResultEnum.APPROVE.getResult());
-        } else {
-            updateDo.setStatus(BpmProcessInstanceResultEnum.REJECT.getResult());
-        }
-
-        leaveMapper.updateById(updateDo);
-    }
-
-}

+ 0 - 80
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/OALeaveService.java

@@ -1,80 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.service.oa;
-
-
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.*;
-import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.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);
-
-
-
-    /**
-     * 获取本人请假申请流程中审批人员
-     * @return  包括,本人部门的项目经理, 部门经理,  HR
-     */
-    OALeaveApplyMembersVO getLeaveApplyMembers();
-}

+ 86 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/impl/BpmOALeaveServiceImpl.java

@@ -0,0 +1,86 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.oa.impl;
+
+import cn.hutool.core.date.DateUtil;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.*;
+import cn.iocoder.yudao.adminserver.modules.bpm.convert.oa.BpmOALeaveConvert;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.BpmOALeaveDO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.oa.BpmOALeaveMapper;
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.oa.BpmOALeaveService;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.task.dto.BpmProcessInstanceCreateReqDTO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.*;
+
+import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+
+/**
+ * OA 请假申请 Service 实现类
+ *
+ * @author jason
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class BpmOALeaveServiceImpl implements BpmOALeaveService {
+
+    /**
+     * OA 请假对应的流程定义 KEY
+     */
+    public static final String PROCESS_KEY = "oa_leave";
+
+    @Resource
+    private BpmOALeaveMapper leaveMapper;
+
+    @Resource
+    private BpmProcessInstanceService processInstanceService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createLeave(Long userId, BpmOALeaveCreateReqVO createReqVO) {
+        // 插入 OA 请假单
+        long day = DateUtil.betweenDay(createReqVO.getStartTime(), createReqVO.getEndTime(), false);
+        BpmOALeaveDO leave = BpmOALeaveConvert.INSTANCE.convert(createReqVO).setUserId(userId).setDay(day)
+                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
+        leaveMapper.insert(leave);
+
+        // 发起 BPM 流程
+        Map<String, Object> processInstanceVariables = new HashMap<>();
+        processInstanceVariables.put("day", day);
+        String processInstanceId = processInstanceService.createProcessInstance(userId,
+                new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)
+                        .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId())));
+
+        // 将工作流的编号,更新到 OA 请假单中
+        leaveMapper.updateById(new BpmOALeaveDO().setId(leave.getId()).setProcessInstanceId(processInstanceId));
+        return leave.getId();
+    }
+
+    @Override
+    public void updateLeaveResult(Long id, Integer result) {
+        leaveMapper.updateById(new BpmOALeaveDO().setId(id).setResult(result));
+    }
+
+    private void validateLeaveExists(Long id) {
+        if (leaveMapper.selectById(id) == null) {
+            throw exception(OA_LEAVE_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public BpmOALeaveDO getLeave(Long id) {
+        return leaveMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<BpmOALeaveDO> getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO) {
+        return leaveMapper.selectPage(userId, pageReqVO);
+    }
+
+}

+ 0 - 195
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/impl/OALeaveServiceImpl.java

@@ -1,195 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.bpm.service.oa.impl;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.date.DateUtil;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.*;
-import cn.iocoder.yudao.adminserver.modules.bpm.convert.oa.OALeaveConvert;
-import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.OALeaveDO;
-import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.oa.OALeaveMapper;
-import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
-import cn.iocoder.yudao.adminserver.modules.bpm.service.oa.OALeaveService;
-import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysPostDO;
-import cn.iocoder.yudao.adminserver.modules.system.dal.mysql.dept.SysPostMapper;
-import cn.iocoder.yudao.adminserver.modules.system.dal.mysql.user.SysUserMapper;
-import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
-import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
-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.*;
-
-import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
-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;
-
-    @Resource
-    private SysPostMapper sysPostMapper;
-
-    @Resource
-    private SysUserMapper sysUserMapper;
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public Long createLeave(OALeaveCreateReqVO createReqVO) {
-        // 插入 OA 请假单
-        OALeaveDO leave = OALeaveConvert.INSTANCE.convert(createReqVO);
-        leave.setStatus(BpmProcessInstanceResultEnum.PROCESS.getResult());
-        // TODO @jason:应该是存储 userId??
-        leave.setUserId(SecurityFrameworkUtils.getLoginUser().getUsername());
-        leaveMapper.insert(leave);
-        Date startTime = createReqVO.getStartTime();
-        Date endTime = createReqVO.getEndTime();
-        long day = DateUtil.betweenDay(startTime, endTime, false);
-        if (day <= 0) {
-            throw ServiceExceptionUtil.exception(OA_DAY_LEAVE_ERROR);
-        }
-        Map<String, Object> taskVariables = createReqVO.getTaskVariables();
-        taskVariables.put("day", day);
-        // 创建工作流
-        Long id = leave.getId();
-        String businessKey = String.valueOf(id);
-        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(createReqVO.getProcessKey(), businessKey, taskVariables);
-        String processInstanceId = processInstance.getProcessInstanceId();
-
-        // 将工作流的编号,更新到 OA 请假单中
-        OALeaveDO updateDo = new OALeaveDO();
-        updateDo.setProcessInstanceId(processInstanceId);
-        updateDo.setId(id);
-        leaveMapper.updateById(updateDo);
-        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());
-
-        // TOTO @芋道源码 貌似 IDEA 会自动加上final(不需要加 final 哈。虽然是不变,但是代码比较少这么去写)
-        Object reApply = variables.get("reApply");
-        if(Objects.equals(reApply, true)){
-            // 更新 表单
-            OALeaveDO updateObj = OALeaveConvert.INSTANCE.convert(updateReqVO);
-            leaveMapper.updateById(updateObj);
-        }
-    }
-
-    // TODO @jason:要不,请假不支持删除,只支持取消?
-    @Override
-    public void deleteLeave(Long id) {
-        // 校验存在
-        this.validateLeaveExists(id);
-        // 删除
-        leaveMapper.deleteById(id);
-    }
-
-    private void validateLeaveExists(Long id) {
-        if (leaveMapper.selectById(id) == null) {
-            throw exception(OA_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);
-    }
-
-
-    /**
-     * 获取本人请假申请流程中审批人员
-     * @return 包括,本人部门的项目经理, 部门经理,  hr
-     */
-    @Override
-    public OALeaveApplyMembersVO getLeaveApplyMembers() {
-        final Long id = SecurityFrameworkUtils.getLoginUser().getId();
-        //项目经理
-        //TODO jason 定义enum
-        SysPostDO pmPostDO = sysPostMapper.selectByCode("pm");
-        if (Objects.isNull(pmPostDO)) {
-            throw ServiceExceptionUtil.exception(OA_PM_POST_NOT_EXISTS);
-        }
-        SysUserDO userDO = sysUserMapper.selectById(id);
-        final List<SysUserDO> pmUsers = sysUserMapper.selectListByDepartIdAndPostId(userDO.getDeptId(), pmPostDO.getId());
-        if (CollUtil.isEmpty(pmUsers)) {
-            throw ServiceExceptionUtil.exception(OA_DEPART_PM_POST_NOT_EXISTS);
-        }
-
-        //部门经理
-        SysPostDO bmPostDO = sysPostMapper.selectByCode("bm");
-        if (Objects.isNull(bmPostDO)) {
-            throw ServiceExceptionUtil.exception(OA_BM_POST_NOT_EXISTS);
-        }
-
-        final List<SysUserDO> bmUsers = sysUserMapper.selectListByDepartIdAndPostId(userDO.getDeptId(), bmPostDO.getId());
-        if (CollUtil.isEmpty(bmUsers)) {
-            throw ServiceExceptionUtil.exception(OA_DEPART_BM_POST_NOT_EXISTS);
-        }
-        //人事
-        SysPostDO hrPostDO = sysPostMapper.selectByCode("hr");
-        if (Objects.isNull(hrPostDO)) {
-            throw ServiceExceptionUtil.exception(OA_HR_POST_NOT_EXISTS);
-        }
-        final List<SysUserDO> hrUsers = sysUserMapper.selectListByDepartIdAndPostId(userDO.getDeptId(), hrPostDO.getId());
-        if (CollUtil.isEmpty(hrUsers)) {
-            throw ServiceExceptionUtil.exception(OA_DEPART_BM_POST_NOT_EXISTS);
-        }
-        return OALeaveApplyMembersVO.builder().pm(pmUsers.get(0).getUsername())
-                .bm(bmUsers.get(0).getUsername())
-                .hr(hrUsers.get(0).getUsername()).build();
-    }
-
-}

+ 32 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/oa/listener/BpmOALeaveResultListener.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.oa.listener;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.event.BpmProcessInstanceResultEvent;
+import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.event.BpmProcessInstanceResultEventListener;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.oa.BpmOALeaveService;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.oa.impl.BpmOALeaveServiceImpl;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * OA 请假单的结果的监听器实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmOALeaveResultListener extends BpmProcessInstanceResultEventListener {
+
+    @Resource
+    private BpmOALeaveService leaveService;
+
+    @Override
+    protected String getProcessDefinitionKey() {
+        return BpmOALeaveServiceImpl.PROCESS_KEY;
+    }
+
+    @Override
+    protected void onEvent(BpmProcessInstanceResultEvent event) {
+        leaveService.updateLeaveResult(Long.parseLong(event.getBusinessKey()), event.getResult());
+    }
+
+}

+ 37 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmActivityService.java

@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.task;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.activity.BpmActivityRespVO;
+
+import java.util.List;
+
+/**
+ * BPM 活动实例 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface BpmActivityService {
+
+    /**
+     * 获得指定流程实例的活动实例列表
+     *
+     * @param processInstanceId 流程实例的编号
+     * @return 活动实例列表
+     */
+    List<BpmActivityRespVO> getActivityListByProcessInstanceId(String processInstanceId);
+
+    /**
+     * 生成指定流程实例的高亮流程图,只高亮进行中的任务
+     *
+     * 友情提示,非该方法的注释。如果想实现更高级的高亮流程图(当前节点红色 + 完成节点为绿色),可参考如下内容:
+     *      博客一:https://blog.csdn.net/qq_40109075/article/details/110939639
+     *      博客二:https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-flowable/src/main/java/com/ruoyi/flowable/flow/CustomProcessDiagramGenerator.java
+     * 这里不实现的原理,需要自定义实现 ProcessDiagramGenerator 和 ProcessDiagramCanvas,代码量有点大
+     *
+     * 如果你想实现高亮已完成的任务,可参考 https://blog.csdn.net/qiuxinfa123/article/details/119579863 博客。不过测试下来,貌似不太对~
+     *
+     * @param processInstanceId 流程实例的编号
+     * @return 图的字节数组
+     */
+    byte[] generateHighlightDiagram(String processInstanceId);
+
+}

+ 30 - 15
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmProcessInstanceService.java

@@ -1,11 +1,9 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.service.task;
 
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCancelReqVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCreateReqVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceMyPageReqVO;
-import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstancePageItemRespVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.*;
 import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
 import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.task.dto.BpmProcessInstanceCreateReqDTO;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import org.activiti.engine.history.HistoricProcessInstance;
@@ -24,7 +22,7 @@ import java.util.Set;
 public interface BpmProcessInstanceService {
 
     /**
-     * 创建流程实例
+     * 创建流程实例(提供给前端)
      *
      * @param userId 用户编号
      * @param createReqVO 创建信息
@@ -32,6 +30,15 @@ public interface BpmProcessInstanceService {
      */
     String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO);
 
+    /**
+     * 创建流程实例(提供给内部)
+     *
+     * @param userId 用户编号
+     * @param createReqDTO 创建信息
+     * @return 实例的编号
+     */
+    String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO);
+
     /**
      * 取消流程实例
      *
@@ -49,15 +56,6 @@ public interface BpmProcessInstanceService {
     @Deprecated
     void deleteProcessInstance(String id, String reason);
 
-    /**
-     * 更新流程实例的结果
-     * 1. 如果更新为已拒绝时,会进行任务的删除
-     *
-     * @param id 流程编号
-     * @param result 结果,{@link BpmProcessInstanceResultEnum}
-     */
-    void updateProcessInstanceResult(String id, Integer result);
-
     /**
      * 获得流程实例的分页
      *
@@ -68,6 +66,14 @@ public interface BpmProcessInstanceService {
     PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId,
                                                                           @Valid BpmProcessInstanceMyPageReqVO pageReqVO);
 
+    /**
+     * 获得流程实例 VO 信息
+     *
+     * @param id 流程实例的编号
+     * @return 流程实例
+     */
+    BpmProcessInstanceRespVO getProcessInstanceVO(String id);
+
     /**
      * 获得流程实例
      *
@@ -138,8 +144,9 @@ public interface BpmProcessInstanceService {
      * 更新 ProcessInstance 拓展记录为取消
      *
      * @param instance 流程任务
+     * @param reason 取消原因
      */
-    void updateProcessInstanceExtCancel(org.activiti.api.process.model.ProcessInstance instance);
+    void updateProcessInstanceExtCancel(org.activiti.api.process.model.ProcessInstance instance, String reason);
 
     /**
      * 更新 ProcessInstance 拓展记录为完成
@@ -148,4 +155,12 @@ public interface BpmProcessInstanceService {
      */
     void updateProcessInstanceExtComplete(org.activiti.api.process.model.ProcessInstance instance);
 
+    /**
+     * 更新 ProcessInstance 拓展记录为不通过
+     *
+     * @param id 流程编号
+     * @param comment 理由。例如说,审批不通过时,需要传递该值
+     */
+    void updateProcessInstanceExtReject(String id, String comment);
+
 }

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است