16.Activiti8 SpringBoot3.X 部署与测试

1.pom文件

注意下:Activiti 8 需要从 Alfresco Nexus 仓库获取

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.15</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.acticiti.test</groupId>
    <artifactId>activitiSpringBoot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>activitiSpringBoot</name>
    <description>activitiSpringBoot</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
        <!-- Activiti 8 稳定版本 -->
        <activiti.version>8.0.0</activiti.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>

        <!-- Spring Security  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- 连接池,Spring Boot 默认使用 HikariCP -->
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
        </dependency>

        <!-- Activiti 工作流引擎 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>${activiti.version}</version>
        </dependency>

        <!-- Activiti 图像生成(用于查看流程图) -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-image-generator</artifactId>
            <version>${activiti.version}</version>
        </dependency>
    </dependencies>

    <!-- Activiti 8 需要从 Alfresco Nexus 仓库获取 -->
    <repositories>
        <repository>
            <id>activiti-releases</id>
            <url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>activiti-releases</id>
            <url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases</url>
        </pluginRepository>
    </pluginRepositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default-compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <annotationProcessorPaths>
                                <path>
                                    <groupId>org.projectlombok</groupId>
                                    <artifactId>lombok</artifactId>
                                </path>
                            </annotationProcessorPaths>
                        </configuration>
                    </execution>
                    <execution>
                        <id>default-testCompile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                        <configuration>
                            <annotationProcessorPaths>
                                <path>
                                    <groupId>org.projectlombok</groupId>
                                    <artifactId>lombok</artifactId>
                                </path>
                            </annotationProcessorPaths>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2.application.yml

这里主要做业务数据库和activiti数据库配置,activiti基本配置

yaml 复制代码
spring:
  datasource:
    # 主数据源(业务数据库)
    primary:
      jdbc-url: jdbc:mysql://localhost:3306/activitidemo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 1234
      type: com.zaxxer.hikari.HikariDataSource
      hikari:
        pool-name: PrimaryHikariPool
        maximum-pool-size: 10
    # Activiti 专用数据源
    activiti:
      jdbc-url: jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 1234
      type: com.zaxxer.hikari.HikariDataSource
      hikari:
        pool-name: ActivitiHikariPool
        maximum-pool-size: 10
  activiti:
    process-definition-location-prefix: classpath*:/processes/**/*.bpmn
    database-schema-update: true
    check-process-definitions: true

# MyBatis配置
mybatis:
  # 搜索指定包别名
  typeAliasesPackage: com.activiti.**.entity
  # 配置mapper的扫描,找到所有的mapper.xml映射文件
  mapperLocations: classpath*:mapper/**/*Mapper.xml

3.bpmn测试文件

xml 复制代码
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://www.activiti.org/test" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://activiti.org/bpmn" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1784192091813" name="" targetNamespace="http://activiti.org/bpmn" typeLanguage="http://www.w3.org/2001/XMLSchema">
    <process id="myProcess" isClosed="false" isExecutable="true" processType="None">
        <startEvent id="_2" name="StartEvent"/>
        <userTask activiti:assignee="${applyUser}" activiti:exclusive="true" id="_3" name="员工提交请假申请"/>
        <userTask activiti:assignee="${managerUser}" activiti:exclusive="true" id="_4" name="部门经理审批"/>
        <endEvent id="_5" name="EndEvent"/>
        <sequenceFlow id="_6" sourceRef="_2" targetRef="_3"/>
        <sequenceFlow id="_7" sourceRef="_3" targetRef="_4"/>
        <exclusiveGateway gatewayDirection="Unspecified" id="_9" name="ExclusiveGateway"/>
        <sequenceFlow id="_10" sourceRef="_4" targetRef="_9"/>
        <sequenceFlow id="_11" sourceRef="_9" targetRef="_5">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${auditResult == 'pass'}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="_12" sourceRef="_9" targetRef="_3">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${auditResult == 'reject'}]]></conditionExpression>
        </sequenceFlow>
    </process>
    <bpmndi:BPMNDiagram documentation="background=#2B2D30;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
        <bpmndi:BPMNPlane bpmnElement="myProcess">
            <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
                <dc:Bounds height="32.0" width="32.0" x="270.0" y="50.0"/>
                <bpmndi:BPMNLabel>
                    <dc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
                <dc:Bounds height="55.0" width="85.0" x="250.0" y="130.0"/>
                <bpmndi:BPMNLabel>
                    <dc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
                <dc:Bounds height="55.0" width="85.0" x="250.0" y="270.0"/>
                <bpmndi:BPMNLabel>
                    <dc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
                <dc:Bounds height="32.0" width="32.0" x="130.0" y="310.0"/>
                <bpmndi:BPMNLabel>
                    <dc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="_9" id="Shape-_9" isMarkerVisible="false">
                <dc:Bounds height="32.0" width="32.0" x="130.0" y="210.0"/>
                <bpmndi:BPMNLabel>
                    <dc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_9" targetElement="_3">
                <di:waypoint x="162.0" y="226.0"/>
                <di:waypoint x="250.0" y="157.5"/>
                <bpmndi:BPMNLabel>
                    <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="_2" targetElement="_3">
                <di:waypoint x="286.0" y="82.0"/>
                <di:waypoint x="286.0" y="130.0"/>
                <bpmndi:BPMNLabel>
                    <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7" sourceElement="_3" targetElement="_4">
                <di:waypoint x="292.5" y="185.0"/>
                <di:waypoint x="292.5" y="270.0"/>
                <bpmndi:BPMNLabel>
                    <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="_9" targetElement="_5">
                <di:waypoint x="146.0" y="242.0"/>
                <di:waypoint x="146.0" y="310.0"/>
                <bpmndi:BPMNLabel>
                    <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="_4" targetElement="_9">
                <di:waypoint x="250.0" y="297.5"/>
                <di:waypoint x="162.0" y="226.0"/>
                <bpmndi:BPMNLabel>
                    <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
    </bpmndi:BPMNDiagram>
</definitions>

4.ActivitiDataSourceConfig标记主数据库

kotlin 复制代码
package com.activiti.config;

import org.activiti.spring.SpringProcessEngineConfiguration;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;
@Configuration
public class ActivitiDataSourceConfig {

    // 1. 创建业务数据源 Bean,并标记为 @Primary
    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    // 2. 创建 Activiti 数据源 Bean
    @Bean(name = "activitiDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.activiti")
    public DataSource activitiDataSource() {
        return DataSourceBuilder.create().build();
    }

    // 3. 配置 Activiti 引擎使用 activitiDataSource
    @Bean
    public ProcessEngineConfigurationConfigurer processEngineConfigurationConfigurer() {
        return configuration -> {
            // 强制设置 Activiti 使用专用数据源
            configuration.setDataSource(activitiDataSource());
            // 配置数据库表更新策略,开发时可设为 true 以自动建表/更新表
            configuration.setDatabaseSchemaUpdate("true");
            // 启用历史记录
            configuration.setDbHistoryUsed(true);
            configuration.setHistoryLevel(org.activiti.engine.impl.history.HistoryLevel.FULL);
        };
    }
}

5.entity

Leave 复制代码
package com.activiti.entity;

import lombok.Data;
import java.util.Date;

@Data
public class Leave {
    private Long id;
    private String applyUser;
    private String managerUser;
    private Integer leaveDays;
    private String reason;
    private String rejectComment;
    private Date startTime;
    private Date endTime;
    // 0待审批 1通过 2驳回待重提
    private Integer auditStatus;
    private Date createTime;
    private Date updateTime;
}
LeaveDTO 复制代码
package com.activiti.entity.dto;

import lombok.Data;

import java.util.Date;

@Data
public class LeaveDTO {
    private String applyUser;
    private String managerUser;
    private Integer leaveDays;
    private String reason;
    private String rejectComment;
    private Date startTime;
    private Date endTime;
    // 业务主键,启动流程后赋值
    private Long businessKey;
}
AuditDTO 复制代码
package com.activiti.entity.dto;

import lombok.Data;

@Data
public class AuditDTO {
    private String taskId;
    // pass / reject
    private String auditResult;
    // 驳回意见
    private String rejectComment;
}

6.三层架构

less 复制代码
package com.activiti.controller;

import com.activiti.entity.dto.AuditDTO;
import com.activiti.entity.dto.LeaveDTO;
import com.activiti.entity.Leave;
import com.activiti.service.LeaveService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/leave")
public class LeaveController {

    @Autowired
    private LeaveService leaveService;

    /**
     * 新建请假单,启动流程
     */
    @PostMapping("/start")
    public Map<String, Object> start(@RequestBody LeaveDTO dto) {
        return leaveService.startProcess(dto);
    }

    /**
     * 经理审批:通过 / 驳回(带驳回意见)
     */
    @PostMapping("/audit")
    public String audit(@RequestBody AuditDTO auditDTO) {
        return leaveService.audit(auditDTO);
    }

    /**
     * 驳回后员工修改单据重新提交
     */
    @PostMapping("/resubmit/{businessKey}")
    public String resubmit(@PathVariable Long businessKey, @RequestBody LeaveDTO dto) {
        return leaveService.resubmit(businessKey, dto);
    }

    /**
     * 查询用户待办任务
     */
    @GetMapping("/todo/{userId}")
    public List<Task> todo(@PathVariable String userId) {
        return leaveService.getTodoTask(userId);
    }

    /**
     * 根据业务ID查询单据详情
     */
    @GetMapping("/detail/{businessKey}")
    public Leave detail(@PathVariable Long businessKey) {
        return leaveService.getLeaveDetail(businessKey);
    }

    /**
     * 根据业务ID查询运行中流程
     */
    @GetMapping("/proc/running/{businessKey}")
    public ProcessInstance runningProc(@PathVariable Long businessKey) {
        return leaveService.getRunningProc(businessKey);
    }

    /**
     * 根据业务ID查询历史流程
     */
    @GetMapping("/proc/history/{businessKey}")
    public HistoricProcessInstance historyProc(@PathVariable Long businessKey) {
        return leaveService.getHistoryProc(businessKey);
    }

    /**
     * 根据业务ID查询当前待办任务
     */
    @GetMapping("/task/{businessKey}")
    public Task getTask(@PathVariable Long businessKey) {
        return leaveService.getTaskByBusinessKey(businessKey);
    }

    /**
     * 查询用户全部历史审批记录
     */
    @GetMapping("/history/{userId}")
    public List<HistoricTaskInstance> history(@PathVariable String userId) {
        return leaveService.getHistoryTask(userId);
    }
}
scss 复制代码
package com.activiti.service;

import com.activiti.entity.dto.AuditDTO;
import com.activiti.entity.dto.LeaveDTO;
import com.activiti.entity.Leave;
import com.activiti.mapper.LeaveMapper;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class LeaveService {

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private LeaveMapper leaveMapper;

    /**
     * 1. 新建请假单并启动流程,绑定businessKey
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> startProcess(LeaveDTO dto) {
        // 保存业务单据
        Leave leave = new Leave();
        leave.setApplyUser(dto.getApplyUser());
        leave.setManagerUser(dto.getManagerUser());
        leave.setLeaveDays(dto.getLeaveDays());
        leave.setReason(dto.getReason());
        leave.setStartTime(dto.getStartTime());
        leave.setEndTime(dto.getEndTime());
        leaveMapper.insert(leave);
        Long businessKey = leave.getId();
        dto.setBusinessKey(businessKey);

        // 流程变量
        Map<String, Object> vars = buildProcessVars(dto);
        System.out.println("////////////*********"+vars);


        // 绑定businessKey启动流程
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(
                "myProcess",
                businessKey.toString(),
                vars
        );

        Map<String, Object> res = new HashMap<>();
        res.put("businessKey", businessKey);
        res.put("processInstanceId", instance.getId());
        return res;
    }

    /**
     * 2. 经理审批:支持通过/驳回,保存驳回意见
     */
    @Transactional(rollbackFor = Exception.class)
    public String audit(AuditDTO auditDTO) {
        Task task = taskService.createTaskQuery().taskId(auditDTO.getTaskId()).singleResult();
        ProcessInstance procInst = runtimeService.createProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .singleResult();
        Long businessKey = Long.valueOf(procInst.getBusinessKey());

        Map<String, Object> vars = new HashMap<>();
        vars.put("auditResult", auditDTO.getAuditResult());
        vars.put("rejectComment", auditDTO.getRejectComment());

        // 完成审批任务,驱动流程流转
        taskService.complete(auditDTO.getTaskId(), vars);

        if ("pass".equals(auditDTO.getAuditResult())) {
            // 审批通过,更新状态1
            leaveMapper.updateStatusAndComment(businessKey, 1, "");
            return "审批通过,流程办结";
        } else {
            // 驳回,更新状态2,保存驳回意见
            leaveMapper.updateStatusAndComment(businessKey, 2, auditDTO.getRejectComment());
            return "驳回成功,请申请人修改后重新提交";
        }
    }

    /**
     * 3. 驳回后员工修改单据、重新提交申请
     */
    @Transactional(rollbackFor = Exception.class)
    public String resubmit(Long businessKey, LeaveDTO dto) {
        // 1. 更新业务单据信息,状态重置为待审批
        leaveMapper.updateLeaveInfo(businessKey, dto.getLeaveDays(), dto.getReason(), dto.getStartTime(), dto.getEndTime());

        // 2. 根据businessKey查询当前流程实例
        ProcessInstance procInst = runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey.toString())
                .singleResult();

        // 3. 查询当前员工提交任务
        Task applyTask = taskService.createTaskQuery()
                .processInstanceId(procInst.getId())
                .taskAssignee(dto.getApplyUser())
                .singleResult();

        // 4. 更新流程变量并完成提交任务,流转到经理审批
        Map<String, Object> vars = buildProcessVars(dto);

        taskService.complete(applyTask.getId(), vars);
        return "修改后重新提交成功,等待经理审批";
    }

    /**
     * 组装流程公共变量
     */
    private Map<String, Object> buildProcessVars(LeaveDTO dto) {
        Map<String, Object> vars = new HashMap<>();
        vars.put("applyUser", dto.getApplyUser());
        vars.put("managerUser", dto.getManagerUser());
        vars.put("leaveDays", dto.getLeaveDays());
        vars.put("reason", dto.getReason());
        vars.put("startTime", dto.getStartTime());
        vars.put("endTime", dto.getEndTime());
        // 初始化审批结果,避免网关表达式找不到变量
        vars.put("auditResult", "");
        return vars;
    }

    // ===================== 查询接口 =====================
    // 根据业务ID查询运行中流程
    public ProcessInstance getRunningProc(Long businessKey) {
        return runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey.toString())
                .singleResult();
    }

    // 根据业务ID查询历史流程
    public HistoricProcessInstance getHistoryProc(Long businessKey) {
        return historyService.createHistoricProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey.toString())
                .singleResult();
    }

    // 查询用户待办
    public List<Task> getTodoTask(String assignee) {
        return taskService.createTaskQuery()
                .taskAssignee(assignee)
                .orderByTaskCreateTime()
                .desc()
                .list();
    }

    // 查询历史任务
    public List<HistoricTaskInstance> getHistoryTask(String userId) {
        return historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(userId)
                .orderByHistoricTaskInstanceEndTime().desc()
                .list();
    }

    // 查询单据详情
    public Leave getLeaveDetail(Long businessKey) {
        return leaveMapper.selectById(businessKey);
    }

    // 根据业务ID查询当前待办任务
    public Task getTaskByBusinessKey(Long businessKey) {
        ProcessInstance procInst = getRunningProc(businessKey);
        if (procInst == null) return null;
        return taskService.createTaskQuery()
                .processInstanceId(procInst.getId())
                .singleResult();
    }
}
LeaveMapper 复制代码
package com.activiti.mapper;


import com.activiti.entity.Leave;
import org.apache.ibatis.annotations.*;

import java.util.Date;

@Mapper
public interface LeaveMapper {

    // 新增请假单
    @Insert("INSERT INTO t_leave(apply_user,manager_user,leave_days,reason,start_time,end_time,audit_status) " +
            "VALUES(#{applyUser},#{managerUser},#{leaveDays},#{reason},#{startTime},#{endTime},0)")
    Long insert(Leave leave);

    // 根据业务ID查询
    @Select("SELECT * FROM t_leave WHERE id = #{id}")
    Leave selectById(@Param("id") Long id);

    // 更新单据状态+驳回意见
    @Update("UPDATE t_leave SET audit_status=#{auditStatus},reject_comment=#{rejectComment} WHERE id=#{id}")
    void updateStatusAndComment(@Param("id") Long id,
                                @Param("auditStatus") Integer auditStatus,
                                @Param("rejectComment") String rejectComment);

    // 修改请假内容(驳回后重提交)
    @Update("UPDATE t_leave SET leave_days=#{leaveDays},reason=#{reason},start_time=#{startTime},end_time=#{endTime},audit_status=0,reject_comment='' WHERE id=#{id}")
    void updateLeaveInfo(@Param("id") Long id,
                         @Param("leaveDays") Integer leaveDays,
                         @Param("reason") String reason,
                         @Param("startTime") Date startTime,
                         @Param("endTime") Date endTime);
}

业务表

sql 复制代码
create table activitidemo.t_leave
(
    id             bigint auto_increment comment '业务主键=BusinessKey'
        primary key,
    apply_user     varchar(50)                            not null comment '申请人账号',
    manager_user   varchar(50)                            not null comment '审批经理账号',
    leave_days     int                                    not null comment '请假天数',
    reason         varchar(500)                           not null comment '请假原因',
    reject_comment varchar(500) default ''                null comment '驳回意见',
    start_time     date                                   not null comment '请假开始时间',
    end_time       date                                   not null comment '请假结束时间',
    audit_status   tinyint      default 0                 null comment '0待审批 1审批通过 2驳回待重提',
    create_time    datetime     default CURRENT_TIMESTAMP null,
    update_time    datetime     default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP
)
    comment '请假业务单据';
相关推荐
用户2080468045616 小时前
Python3 数据类型转换新手实战指南
后端
苏三说技术16 小时前
为什么Spring要“抛弃”Feign?
后端
神奇小汤圆17 小时前
Java代理模式深度解析:静态代理 vs 动态代理 vs CGLIB
后端
爱勇宝17 小时前
AI不会淘汰所有人,但会淘汰这6种人
前端·后端·程序员
Conan在掘金17 小时前
鸿蒙 ArkUI 进阶:@Extend 和 @Styles,把样式也抽成「乐高块」的正确姿势
后端
orient17 小时前
为什么需要并发编程
后端
Cosolar17 小时前
AI Agent 架构原理详解:从一次提问到任务完成的完整闭环
人工智能·后端·架构
Conan在掘金17 小时前
鸿蒙 ArkUI 深水区:Stage 模型多维状态,从「组件内」跳到「应用级」的分水岭
后端
长栎18 小时前
用AI优化Redis缓存失效,我把热key问题想简单了
后端