| name | workflow-audit |
| description | 为 mfish-nocode-pro 项目集成 Flowable 工作流审批能力,包括注册 FlowKey、实体审批状态字段、Service 启动/撤回流程、Controller 审批回调接口、Feign 回调接口注册五个步骤的完整代码模板。当用户说"带工作流审批"、"发布审核流程"、"集成工作流"、"审批回调"时使用此 skill。 |
工作流审批集成
适用于需要发布/审核流程的业务模块(如大屏发布、内容审核等),在标准 CRUD 基础上叠加工作流能力。
核心概念
| 组件 | 说明 |
|---|
FlowableParam<T> | 启动工作流参数:key(流程定义key)、id(业务id)、prefix(回调URL前缀)、callback(回调Feign接口全路径) |
FlowKey 枚举 | 流程定义key枚举,新业务须在此注册,路径:mf-api/mf-workflow-api/.../FlowKey.java |
RemoteAuditApi<T> | 审批回调接口,Controller 需实现 approved/rejected/canceled 三个方法 |
WorkflowCompleteResult | 回调结果体:processInstanceId、comment、eventName |
RemoteWorkflowService | Feign 客户端,用于启动/删除流程实例 |
审批状态约定
| auditState 值 | 含义 | 触发时机 |
|---|
0 | 审核中 | 发布提交时设置 |
1 | 已通过 | approved 回调时设置 |
2 | 未通过 | rejected 回调时设置 |
null | 已取消 | canceled 回调时设置 |
集成步骤
第一步:注册 FlowKey
在 FlowKey.java 枚举中添加新流程定义 key:
新业务名称 ("xxx_release"),
第二步:实体新增审批状态字段
@Schema(description = "审核状态 null 未发布 0 审核中 1 已通过 2 未通过")
private Integer auditState;
注意:实体类必须添加完整的 Swagger 注解:
- 类级别:
@Schema(description = "描述信息", name = "类名")
- 字段级别:每个字段都需添加
@Schema(description = "字段描述")
- 便于生成完整的 API 文档和 Swagger UI 展示
第三步:Service 中启动和撤回工作流
import cn.com.mfish.common.core.constants.RPCConstants;
import cn.com.mfish.common.core.entity.WorkflowCompleteResult;
import cn.com.mfish.common.core.exception.MyRuntimeException;
import cn.com.mfish.common.core.web.Result;
import cn.com.mfish.common.workflow.api.entity.FlowableParam;
import cn.com.mfish.common.workflow.api.enums.FlowKey;
import cn.com.mfish.common.workflow.api.remote.RemoteWorkflowService;
import jakarta.annotation.Resource;
import org.springframework.transaction.annotation.Transactional;
@Service
public class {类名}ServiceImpl extends ServiceImpl<{类名}Mapper, {类名}> implements {类名}Service {
@Resource
RemoteWorkflowService remoteWorkflowService;
@Override
@Transactional
public Result<{类名}> insert{类名}({类名} entity) {
if (baseMapper.exists(new LambdaQueryWrapper<{类名}>()
.eq({类名}::getSourceId, entity.getSourceId()))) {
throw new MyRuntimeException("错误:已存在,不能重复发布!");
}
entity.setAuditState(0);
if (save(entity)) {
startProcess(entity);
return Result.ok(entity, "{中文名称}-发布成功!");
}
return Result.fail(entity, "错误:{中文名称}-发布失败!");
}
@Override
@Transactional
public Result<Boolean> delete{类名}(String id) {
{类名} entity = getById(id);
if (entity == null) {
return Result.ok(false, "错误:记录不存在!");
}
if (remove(new LambdaQueryWrapper<{类名}>().eq({类名}::getId, id))) {
Result<String> result = remoteWorkflowService.delProcessByBusinessKey(
RPCConstants.INNER, entity.getId(), "用户撤回");
if (!result.isSuccess()) {
throw new MyRuntimeException(result.getMsg());
}
return Result.ok(true, "撤回成功!");
}
return Result.fail(false, "错误:撤回失败!");
}
@Override
public Result<String> audit(String id, Integer auditState, WorkflowCompleteResult result) {
{类名} entity = baseMapper.selectById(id);
if (entity == null) throw new MyRuntimeException("错误:记录不存在!");
entity.setAuditState(auditState);
if (!updateById(entity)) throw new MyRuntimeException("错误:审批操作异常!");
return Result.ok(id, "审批操作成功!");
}
private void startProcess({类名} entity) {
Result<String> result = remoteWorkflowService.startProcess(RPCConstants.INNER,
new FlowableParam<String>()
.setKey(FlowKey.{对应枚举}.toString())
.setId(entity.getId())
.setPrefix("{回调URL前缀}")
.setCallback("{Feign接口全路径}")
);
if (!result.isSuccess()) throw new MyRuntimeException(result.getMsg());
}
}
第四步:Controller 追加审批回调接口
工作流引擎审批完成后,通过 Feign 回调这三个接口,无需 @RequiresPermissions:
import cn.com.mfish.common.core.entity.WorkflowCompleteResult;
@PostMapping("/approved/{id}")
public Result<String> approved(
@PathVariable String id,
@RequestBody WorkflowCompleteResult result) {
return {变量名}Service.audit(id, 1, result);
}
@PostMapping("/rejected/{id}")
public Result<String> rejected(
@PathVariable String id,
@RequestBody WorkflowCompleteResult result) {
return {变量名}Service.audit(id, 2, result);
}
@PostMapping("/canceled/{id}")
public Result<String> canceled(
@PathVariable String id,
@RequestBody WorkflowCompleteResult result) {
return {变量名}Service.audit(id, null, result);
}
第五步:注册 Feign 回调接口(微服务模式)
在 mf-api/mf-xxx-api 模块中定义回调 Feign 接口,继承 RemoteAuditApi<String>:
import cn.com.mfish.common.core.entity.RemoteAuditApi;
@FeignClient(
contextId = "remote{类名}Service",
value = ServiceConstants.XXX_SERVICE,
fallbackFactory = Remote{类名}FallBack.class
)
public interface Remote{类名}Service extends RemoteAuditApi<String> {
}
FlowableParam.callback 填写此接口的全路径类名
FlowableParam.prefix 填写 Controller 的 @RequestMapping 路径(不含 /)
注意事项
prefix 与 Controller @RequestMapping 必须一致,回调 URL 拼接规则:/{prefix}/approved/{id}
- 编辑已发布记录前需校验审核状态:
auditState=1 的记录禁止直接编辑,需先撤回
- 重复发布校验:发布前通过
sourceId 或业务唯一键检查是否已存在记录
- 撤回时需同步调用
remoteWorkflowService.delProcessByBusinessKey(...) 删除工作流实例
- Service 接口中需声明
audit(String id, Integer auditState, WorkflowCompleteResult result) 方法
- 异常处理规范:人为抛出的业务异常统一采用
MyRuntimeException 处理
- 引入包:
import cn.com.mfish.common.core.exception.MyRuntimeException;
- 使用场景:记录不存在、重复提交、状态校验失败等业务异常情况
- 示例代码:
if (entity == null) {
throw new MyRuntimeException("错误:记录不存在!");
}
if (baseMapper.exists(new LambdaQueryWrapper<Entity>()
.eq(Entity::getSourceId, sourceId))) {
throw new MyRuntimeException("错误:已存在,不能重复发布!");
}
if (!"approved".equals(entity.getAuditState())) {
throw new MyRuntimeException("错误:审批状态不正确!");
}
- 消息格式:建议以
"错误:" 开头,便于前端统一处理和识别
- 不要使用:避免直接使用
RuntimeException 或其他自定义异常
- 安全规范 - 异常信息不暴露给前端:
- 核心原则:返回给前端的错误消息必须是友好的、通用的提示,不能包含具体的异常堆栈或技术细节
- 错误示例:
return Result.fail(false, "错误:流程配置不正确," + e.getMessage()); ❌
- 正确示例:
return Result.fail(false, "错误:流程配置不正确,请检查流程设计是否完整且符合规范"); ✅
- 日志记录:详细的异常信息应通过
log.error() 记录到日志文件中,便于开发人员排查
- 实现模式:
try {
BpmnConverter.convertToBpmn(...);
} catch (Exception e) {
log.error("流程格式化失败:{}", e.getMessage(), e);
return Result.fail(false, "错误:流程配置不正确,请检查流程设计是否完整且符合规范");
}
相关参考
规范补充(2026-04)
- 新审批业务必须先在
mf-api 下提供独立 Feign 回调接口模块(例如 mf-demo-api),接口继承 RemoteAuditApi<T>,并提供 fallbackFactory。
FlowableParam.callback 必须填写 API 模块中的 Feign 接口全限定名(不要填写业务模块本地类路径)。
- 单体模式覆盖实现必须放在
mf-common/mf-common-api,Bean 名与 Feign contextId 对应(如 remoteDemoLeaveApplyService)。
mf-common-api 通过依赖 API 模块实现解耦,不直接依赖 mf-business;如需调用业务方法,优先通过 Spring 容器按 Bean 名调用。
优化记录(2026-04-19)
mf-demo-api 创建后,必须同步加入根 pom.xml 的 dependencyManagement,避免子模块遗漏版本管理。
BootDemoLeaveApplyService 禁止通过反射获取 demoLeaveApplyService,应改为依赖注入 DemoLeaveApplyService。
DemoLeaveApplyService 直接下沉到 mf-common(例如 mf-common-demo),不要再额外拆分 DemoLeaveApplyAuditService。
- 该模式下保持依赖方向为:
mf-common-api -> mf-common-demo、mf-business -> mf-common-demo,并为后续 Feign 扩展预留统一服务接口。
依赖约束(2026-04-19)
mf-common-demo 的 pom.xml 默认仅引入:
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring</artifactId></dependency>。
- 不要默认引入
mybatis-plus-annotation、mybatis-plus-extension、fastexcel;仅在确有直接编译依赖时再按最小集补充。
- 若公共模块实体/接口仅用于跨层共享,优先保持依赖最小化,避免把业务模块的技术依赖扩散到
mf-common。