Java 后端开发2026年项目面试技巧(DeepSeek总结版)与面经总结
目录指引:
策略模式应用
XXL-JOB + 策略模式的组合,能够完美实现调度与业务的解耦,既享受到XXL-JOB的分布式调度能力,又保留了策略模式的灵活扩展性。
一、整体架构设计

二、核心代码实现
1. 任务策略接口(与XXL-JOB解耦)
/**
* 任务策略接口 - 专注于业务逻辑,不关心调度
*/
public interface ITaskStrategy {
/**
* 任务唯一标识
*/
String getTaskId();
/**
* 任务名称
*/
String getTaskName();
/**
* 任务分组
*/
String getTaskGroup();
/**
* 执行任务
* @param context 任务上下文,包含参数、执行环境等
* @return 执行结果
*/
TaskResult execute(TaskContext context);
/**
* 任务执行前的校验
*/
default boolean validate(TaskContext context) {
return true;
}
/**
* 任务超时时间(毫秒),默认30分钟
*/
default long getTimeout() {
return 30 * 60 * 1000L;
}
/**
* 是否允许并发执行
*/
default boolean allowConcurrent() {
return false;
}
/**
* 任务描述
*/
default String getDescription() {
return "";
}
}
/**
* 任务执行结果
*/
@Data
@Builder
public class TaskResult {
private boolean success;
private String taskId;
private String taskName;
private String message;
private Object data;
private long executionTime;
private Map<String, Object> metrics;
public static TaskResult success(String message) {
return TaskResult.builder()
.success(true)
.message(message)
.build();
}
public static TaskResult failure(String message) {
return TaskResult.builder()
.success(false)
.message(message)
.build();
}
}2. 具体任务策略实现
@Component
public class TempFileCleanupTask implements ITaskStrategy {
@Value("${task.temp-file.retention-days:7}")
private int retentionDays;
@Override
public String getTaskId() {
return "tempFileCleanupTask";
}
@Override
public String getTaskName() {
return "清理临时文件";
}
@Override
public String getTaskGroup() {
return "MAINTENANCE";
}
@Override
public TaskResult execute(TaskContext context) {
log.info("开始清理临时文件,保留天数: {}", retentionDays);
try {
File tempDir = new File("/tmp/procurement");
if (!tempDir.exists()) {
return TaskResult.success("临时目录不存在,无需清理");
}
int deletedCount = deleteOldFiles(tempDir, retentionDays);
return TaskResult.builder()
.success(true)
.message(String.format("成功清理 %d 个临时文件", deletedCount))
.metrics(Map.of("deletedCount", deletedCount))
.build();
} catch (Exception e) {
log.error("清理临时文件失败", e);
return TaskResult.failure("清理失败: " + e.getMessage());
}
}
}
@Component
public class SupplierDataSyncTask implements ITaskStrategy {
@Override
public String getTaskId() {
return "supplierDataSyncTask";
}
@Override
public String getTaskName() {
return "同步供应商数据";
}
@Override
public String getTaskGroup() {
return "SYNC";
}
@Override
public long getTimeout() {
return 60 * 60 * 1000L; // 1小时超时
}
@Override
public TaskResult execute(TaskContext context) {
log.info("开始同步供应商数据...");
// 获取分片参数(用于分布式分片执行)
Integer shardIndex = context.getIntParam("shardIndex", 0);
Integer shardTotal = context.getIntParam("shardTotal", 1);
try {
// 只同步当前分片的数据
int syncedCount = syncFromExternalSystem(shardIndex, shardTotal);
return TaskResult.builder()
.success(true)
.message(String.format("同步完成,共 %d 条", syncedCount))
.metrics(Map.of(
"syncedCount", syncedCount,
"shardIndex", shardIndex,
"shardTotal", shardTotal
))
.build();
} catch (Exception e) {
log.error("同步供应商数据失败", e);
return TaskResult.failure("同步失败: " + e.getMessage());
}
}
}
// 其他38+个任务策略类似...3. 策略注册中心
@Component
public class TaskStrategyRegistry {
private final Map<String, ITaskStrategy> strategyMap = new ConcurrentHashMap<>();
private final Map<String, TaskMetadata> metadataMap = new ConcurrentHashMap<>();
@Autowired
private List<ITaskStrategy> strategies;
@PostConstruct
public void initialize() {
log.info("开始初始化任务策略注册中心...");
strategies.forEach(strategy -> {
String taskId = strategy.getTaskId();
strategyMap.put(taskId, strategy);
// 构建任务元数据
TaskMetadata metadata = TaskMetadata.builder()
.taskId(taskId)
.taskName(strategy.getTaskName())
.taskGroup(strategy.getTaskGroup())
.description(strategy.getDescription())
.timeout(strategy.getTimeout())
.allowConcurrent(strategy.allowConcurrent())
.build();
metadataMap.put(taskId, metadata);
log.info("注册任务策略: {} - {}", taskId, strategy.getTaskName());
});
log.info("任务策略注册完成,共注册 {} 个任务", strategyMap.size());
}
public ITaskStrategy getStrategy(String taskId) {
ITaskStrategy strategy = strategyMap.get(taskId);
if (strategy == null) {
throw new TaskNotFoundException("未找到任务: " + taskId);
}
return strategy;
}
public List<TaskMetadata> getAllMetadata() {
return new ArrayList<>(metadataMap.values());
}
public boolean isTaskExist(String taskId) {
return strategyMap.containsKey(taskId);
}
}4. XXL-JOB统一调度入口
@Component
@Slf4j
public class UnifiedTaskJobHandler {
@Autowired
private TaskStrategyRegistry strategyRegistry;
@Autowired
private TaskExecutionMonitor monitor;
@Autowired
private TaskAlertService alertService;
/**
* 统一任务执行入口 - XXL-JOB调用此方法
*/
@XxlJob("executeTask")
public void executeTask() throws Exception {
// 从XXL-JOB上下文中获取参数
String taskId = XxlJobHelper.getJobParam();
log.info("接收到任务调度指令: taskId={}", taskId);
if (StringUtils.isBlank(taskId)) {
XxlJobHelper.handleFail("任务ID不能为空");
return;
}
// 获取任务策略
ITaskStrategy strategy = strategyRegistry.getStrategy(taskId);
// 构建任务上下文
TaskContext context = buildTaskContext();
// 执行任务并监控
long startTime = System.currentTimeMillis();
TaskResult result = null;
try {
// 执行前置校验
if (!strategy.validate(context)) {
XxlJobHelper.handleFail("任务前置校验失败");
return;
}
// 执行任务(带超时控制)
result = executeWithTimeout(strategy, context);
// 记录执行结果
long duration = System.currentTimeMillis() - startTime;
monitor.recordExecution(taskId, result, duration);
// 处理结果
if (result.isSuccess()) {
log.info("任务执行成功: {}, 耗时: {}ms", taskId, duration);
XxlJobHelper.handleSuccess("执行成功: " + result.getMessage());
} else {
log.error("任务执行失败: {}, 原因: {}", taskId, result.getMessage());
XxlJobHelper.handleFail("执行失败: " + result.getMessage());
// 发送告警
alertService.sendTaskFailureAlert(taskId, result, duration);
}
} catch (TimeoutException e) {
log.error("任务执行超时: {}", taskId, e);
XxlJobHelper.handleFail("执行超时");
alertService.sendTaskTimeoutAlert(taskId, strategy.getTimeout());
} catch (Exception e) {
log.error("任务执行异常: {}", taskId, e);
XxlJobHelper.handleFail("执行异常: " + e.getMessage());
alertService.sendTaskExceptionAlert(taskId, e);
}
}
/**
* 带超时控制的任务执行
*/
private TaskResult executeWithTimeout(ITaskStrategy strategy, TaskContext context)
throws TimeoutException, Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<TaskResult> future = executor.submit(() -> strategy.execute(context));
try {
return future.get(strategy.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
future.cancel(true);
throw e;
} finally {
executor.shutdownNow();
}
}
/**
* 构建任务上下文
*/
private TaskContext buildTaskContext() {
TaskContext context = new TaskContext();
// 从XXL-JOB获取分片参数
int shardIndex = XxlJobHelper.getShardIndex();
int shardTotal = XxlJobHelper.getShardTotal();
context.setParam("shardIndex", shardIndex);
context.setParam("shardTotal", shardTotal);
// 获取自定义参数(JSON格式)
String jobParam = XxlJobHelper.getJobParam();
if (StringUtils.isNotBlank(jobParam)) {
// 支持JSON格式的自定义参数
try {
Map<String, Object> params = JsonUtils.parseMap(jobParam);
params.forEach(context::setParam);
} catch (Exception e) {
log.warn("解析任务参数失败,使用原始参数: {}", jobParam);
context.setParam("rawParam", jobParam);
}
}
// 设置执行环境信息
context.setParam("executorHost", getLocalHost());
context.setParam("executorPort", getLocalPort());
context.setParam("executionTime", LocalDateTime.now().toString());
return context;
}
}5. 任务执行监控
@Component
@Slf4j
public class TaskExecutionMonitor {
@Autowired
private MeterRegistry meterRegistry;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 执行中的任务缓存
private final Set<String> runningTasks = ConcurrentHashMap.newKeySet();
public void recordExecution(String taskId, TaskResult result, long duration) {
// 更新运行状态
runningTasks.remove(taskId);
// 记录指标
Timer.Sample sample = Timer.start();
Timer.builder("task.execution.duration")
.tags("taskId", taskId, "success", String.valueOf(result.isSuccess()))
.register(meterRegistry)
.record(duration, TimeUnit.MILLISECONDS);
// 记录计数
Counter.builder("task.execution.count")
.tags("taskId", taskId, "success", String.valueOf(result.isSuccess()))
.register(meterRegistry)
.increment();
// 存储执行历史到Redis(用于最近执行状态查询)
String historyKey = "task:history:" + taskId;
redisTemplate.opsForList().leftPush(historyKey, ExecutionRecord.of(taskId, result, duration));
redisTemplate.opsForList().trim(historyKey, 0, 99); // 只保留最近100条
// 记录日志
log.info("任务执行记录: taskId={}, success={}, duration={}ms, message={}",
taskId, result.isSuccess(), duration, result.getMessage());
}
public boolean isTaskRunning(String taskId) {
return runningTasks.contains(taskId);
}
public void markTaskStarted(String taskId) {
runningTasks.add(taskId);
}
}6. 任务管理端点(用于XXL-JOB同步)
@RestController
@RequestMapping("/api/task-management")
public class TaskManagementController {
@Autowired
private TaskStrategyRegistry registry;
@Autowired
private TaskExecutionMonitor monitor;
/**
* 获取所有任务信息 - 用于同步到XXL-JOB
*/
@GetMapping("/tasks")
public ApiResponse<List<TaskMetadata>> getAllTasks() {
return ApiResponse.success(registry.getAllMetadata());
}
/**
* 获取任务执行状态
*/
@GetMapping("/tasks/{taskId}/status")
public ApiResponse<TaskStatus> getTaskStatus(@PathVariable String taskId) {
boolean isRunning = monitor.isTaskRunning(taskId);
List<ExecutionRecord> recentExecutions = monitor.getRecentExecutions(taskId, 10);
TaskStatus status = TaskStatus.builder()
.taskId(taskId)
.isRunning(isRunning)
.recentExecutions(recentExecutions)
.build();
return ApiResponse.success(status);
}
/**
* 手动触发任务执行
*/
@PostMapping("/tasks/{taskId}/trigger")
public ApiResponse<Void> triggerTask(@PathVariable String taskId) {
// 通过XXL-JOB的API触发任务
xxlJobClient.triggerTask(taskId);
return ApiResponse.success("任务已触发");
}
}三、XXL-JOB控制台配置
在XXL-JOB的管理界面中,只需要配置一个任务:
| 配置项 | 值 |
|---|---|
| 任务描述 | 统一任务调度入口 |
| JobHandler | executeTask |
| 运行模式 | BEAN模式 |
| 任务参数 | {taskId},如 tempFileCleanupTask |
# 不同任务的调度配置通过任务参数区分
# 示例:配置多个任务
- 任务: 清理临时文件
JobHandler: executeTask
任务参数: tempFileCleanupTask
Cron: 0 0 2 * * ?
- 任务: 同步供应商数据
JobHandler: executeTask
任务参数: supplierDataSyncTask
Cron: 0 0 1 * * ?
- 任务: 供应商评估超时检查
JobHandler: executeTask
任务参数: assessmentTimeoutCheckTask
Cron: 0 */5 * * * ?
# ... 其他39+个任务,都在XXL-JOB中配置为使用统一的JobHandler四、架构优势总结
| 对比维度 | 纯策略模式 | 纯XXL-JOB | XXL-JOB + 策略模式 |
|---|---|---|---|
| 调度高可用 | ❌ 需自研 | ✅ 原生支持 | ✅ 原生支持 |
| 可视化运维 | ❌ 需自研 | ✅ 原生支持 | ✅ 原生支持 |
| 任务扩展性 | ✅ 灵活 | ⚠️ 需配置 | ✅ 灵活 |
| 代码复用 | ✅ 高复用 | ❌ 重复配置 | ✅ 高复用 |
| 业务解耦 | ✅ 解耦 | ⚠️ 耦合度一般 | ✅ 彻底解耦 |
| 动态配置 | ✅ 灵活 | ⚠️ 需重启 | ✅ 动态灵活 |
| 分片能力 | ❌ 需自研 | ✅ 原生支持 | ✅ 原生支持 |
核心价值
"通过XXL-JOB + 策略模式的组合,我们实现了调度平台标准化与业务逻辑灵活化的完美统一。XXL-JOB负责解决分布式调度、高可用、可视化运维等平台级问题,而策略模式则负责让40+个任务的业务逻辑保持清晰、独立和可扩展。两者各司其职,互不侵入。"
扩展能力
// 新增加一个任务只需要三步:
// 1. 实现ITaskStrategy接口
@Component
public class NewBusinessTask implements ITaskStrategy {
@Override
public String getTaskId() { return "newBusinessTask"; }
@Override
public TaskResult execute(TaskContext context) {
// 业务逻辑
return TaskResult.success("执行完成");
}
}
// 2. 无需修改任何已有代码,策略注册中心会自动扫描注册
// 3. 在XXL-JOB控制台配置调度,指向统一入口即可:
// JobHandler: executeTask, 参数: newBusinessTask这就是**"平台级调度能力 + 业务级策略灵活性"**的最佳实践!
Java工作流实现详解
在采购公共服务系统中,工作流主要体现在供应商生命周期管理、采购审批流程、供应商评估流程等业务场景。
一、整体架构设计

二、核心工作流场景实现
场景一:供应商生命周期管理(状态机驱动)
业务背景:供应商从注册到淘汰,经历多个状态流转,每个状态变更都需要审批或触发相应的业务动作。
/**
* 供应商状态枚举
*/
public enum SupplierStatus {
DRAFT("草稿", "供应商信息录入中"),
PENDING_APPROVAL("待审批", "提交审批,等待审核"),
APPROVED("已通过", "审批通过,可参与业务"),
ACTIVE("已激活", "正式合作供应商"),
SUSPENDED("已暂停", "暂停合作"),
TERMINATED("已终止", "终止合作"),
REJECTED("已驳回", "审批驳回");
private String desc;
private String detail;
}
/**
* 供应商生命周期管理(状态机引擎)
*/
@Service
@Slf4j
public class SupplierLifecycleManager {
@Autowired
private SupplierRepository supplierRepository;
@Autowired
private ApprovalService approvalService;
@Autowired
private ApplicationEventPublisher eventPublisher;
/**
* 提交供应商审批(草稿 → 待审批)
*/
@Transactional
public void submitForApproval(Supplier supplier, User submitter) {
// 1. 状态校验:只有草稿状态才能提交
if (supplier.getStatus() != SupplierStatus.DRAFT) {
throw new IllegalStateException("当前状态不允许提交审批");
}
// 2. 数据完整性校验
validateSupplierData(supplier);
// 3. 更新状态
supplier.setStatus(SupplierStatus.PENDING_APPROVAL);
supplier.setSubmitter(submitter);
supplier.setSubmitTime(LocalDateTime.now());
supplierRepository.save(supplier);
// 4. 创建审批流程实例
String processInstanceId = approvalService.startProcess(
"supplier_approval", // 流程定义Key
supplier.getId().toString(),
submitter
);
supplier.setProcessInstanceId(processInstanceId);
supplierRepository.save(supplier);
log.info("供应商已提交审批: {}, 流程实例: {}", supplier.getId(), processInstanceId);
// 5. 发布事件
eventPublisher.publishEvent(new SupplierSubmittedEvent(supplier, submitter));
}
/**
* 审批通过(待审批 → 已通过)
*/
@Transactional
public void approveSupplier(Long supplierId, User approver, String comment) {
Supplier supplier = supplierRepository.findById(supplierId)
.orElseThrow(() -> new SupplierNotFoundException(supplierId));
// 状态校验
if (supplier.getStatus() != SupplierStatus.PENDING_APPROVAL) {
throw new IllegalStateException("供应商不在待审批状态");
}
// 执行审批(调用审批服务)
ApprovalResult result = approvalService.approve(
supplier.getProcessInstanceId(),
approver,
comment
);
if (result.isApproved()) {
// 状态流转:待审批 → 已通过
supplier.setStatus(SupplierStatus.APPROVED);
supplier.setApprover(approver);
supplier.setApprovalTime(LocalDateTime.now());
supplier.setApprovalComment(comment);
supplierRepository.save(supplier);
// 自动激活(如果不需要额外激活步骤)
// 或者发送通知让操作员手动激活
activateSupplier(supplier);
log.info("供应商审批通过: {}, 审批人: {}", supplierId, approver.getUsername());
// 发布审批通过事件
eventPublisher.publishEvent(new SupplierApprovedEvent(supplier, approver));
}
}
/**
* 激活供应商(已通过 → 已激活)
*/
@Transactional
public void activateSupplier(Supplier supplier) {
if (supplier.getStatus() != SupplierStatus.APPROVED) {
throw new IllegalStateException("只有已通过的供应商才能激活");
}
supplier.setStatus(SupplierStatus.ACTIVE);
supplier.setActivateTime(LocalDateTime.now());
supplierRepository.save(supplier);
// 触发激活后的业务动作
// 1. 创建供应商账号
createSupplierAccount(supplier);
// 2. 同步到下游系统
syncToDownstream(supplier);
// 3. 发送欢迎邮件
sendWelcomeEmail(supplier);
log.info("供应商已激活: {}", supplier.getId());
// 发布激活事件
eventPublisher.publishEvent(new SupplierActivatedEvent(supplier));
}
/**
* 暂停供应商(已激活 → 已暂停)
*/
@Transactional
public void suspendSupplier(Long supplierId, String reason, User operator) {
Supplier supplier = supplierRepository.findById(supplierId).orElseThrow();
if (supplier.getStatus() != SupplierStatus.ACTIVE) {
throw new IllegalStateException("只有已激活的供应商才能暂停");
}
// 需要审批的暂停流程
String processInstanceId = approvalService.startProcess(
"supplier_suspend",
supplierId.toString(),
operator
);
supplier.setStatus(SupplierStatus.SUSPENDED);
supplier.setSuspendReason(reason);
supplier.setProcessInstanceId(processInstanceId);
supplierRepository.save(supplier);
// 清理相关资源
clearSupplierCache(supplierId);
notifyDownstreamSuspension(supplier);
log.info("供应商已暂停: {}", supplierId);
}
}场景二:基于Activiti的工作流引擎
引入Activiti:对于复杂的审批流程,我们引入了Activiti作为工作流引擎。
2.1 Maven依赖
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-image-generator</artifactId>
<version>7.1.0.M6</version>
</dependency>2.2 BPMN流程定义(供应商准入流程)
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="http://www.activiti.org/processdef">
<bpmn:process id="supplier_approval" name="供应商准入审批" isExecutable="true">
<!-- 开始事件 -->
<bpmn:startEvent id="startEvent" name="提交申请"/>
<!-- 采购员初审 -->
<bpmn:userTask id="buyerReview" name="采购员初审"
activiti:assignee="${buyer}">
<bpmn:documentation>验证供应商基本信息的完整性和真实性</bpmn:documentation>
</bpmn:userTask>
<!-- 质量部审核 -->
<bpmn:userTask id="qualityReview" name="质量部审核"
activiti:candidateGroups="QUALITY_TEAM">
<bpmn:documentation>审核供应商质量体系认证和产品质量</bpmn:documentation>
</bpmn:userTask>
<!-- 财务部审核 -->
<bpmn:userTask id="financeReview" name="财务部审核"
activiti:candidateGroups="FINANCE_TEAM">
<bpmn:documentation>审核供应商财务状况和付款条件</bpmn:documentation>
</bpmn:userTask>
<!-- 采购总监审批 -->
<bpmn:userTask id="directorApproval" name="采购总监审批"
activiti:candidateGroups="DIRECTOR">
<bpmn:documentation>最终审批,决定是否准入</bpmn:documentation>
</bpmn:userTask>
<!-- 排他网关:审批结果判断 -->
<bpmn:exclusiveGateway id="approvalGateway" name="审批结果"/>
<!-- 通过路径 -->
<bpmn:endEvent id="approvedEnd" name="审批通过"/>
<!-- 驳回路径 -->
<bpmn:endEvent id="rejectedEnd" name="审批驳回"/>
<!-- 连接 -->
<bpmn:sequenceFlow sourceRef="startEvent" targetRef="buyerReview"/>
<bpmn:sequenceFlow sourceRef="buyerReview" targetRef="qualityReview"/>
<bpmn:sequenceFlow sourceRef="qualityReview" targetRef="financeReview"/>
<bpmn:sequenceFlow sourceRef="financeReview" targetRef="directorApproval"/>
<bpmn:sequenceFlow sourceRef="directorApproval" targetRef="approvalGateway"/>
<bpmn:sequenceFlow sourceRef="approvalGateway" targetRef="approvedEnd">
<bpmn:conditionExpression xsi:type="tFormalExpression">
${approved == true}
</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow sourceRef="approvalGateway" targetRef="rejectedEnd">
<bpmn:conditionExpression xsi:type="tFormalExpression">
${approved == false}
</bpmn:conditionExpression>
</bpmn:sequenceFlow>
</bpmn:process>
</bpmn:definitions>2.3 工作流服务封装
@Service
@Slf4j
public class ActivitiWorkflowService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
@Autowired
private RepositoryService repositoryService;
@Autowired
private IdentityService identityService;
/**
* 启动流程实例
*/
public String startProcess(String processKey, String businessKey,
String starter, Map<String, Object> variables) {
// 设置流程发起人
identityService.setAuthenticatedUserId(starter);
// 启动流程
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey(processKey, businessKey, variables);
log.info("流程实例已启动: processKey={}, businessKey={}, instanceId={}",
processKey, businessKey, processInstance.getId());
return processInstance.getId();
}
/**
* 提交审批任务
*/
public ApprovalResult completeTask(String taskId, String assignee,
Map<String, Object> variables, String comment) {
try {
// 设置当前用户
identityService.setAuthenticatedUserId(assignee);
// 添加审批意见
if (comment != null) {
taskService.addComment(taskId, null, comment);
}
// 完成任务
taskService.complete(taskId, variables);
// 获取审批结果
String processInstanceId = getProcessInstanceId(taskId);
boolean approved = (boolean) variables.getOrDefault("approved", false);
log.info("任务已完成: taskId={}, assignee={}, approved={}",
taskId, assignee, approved);
return ApprovalResult.success(processInstanceId, approved);
} catch (Exception e) {
log.error("任务完成失败", e);
return ApprovalResult.failure("审批失败: " + e.getMessage());
}
}
/**
* 获取用户的待办任务
*/
public List<TaskDetail> getUserTasks(String userId) {
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee(userId)
.orderByTaskCreateTime()
.desc()
.list();
return tasks.stream()
.map(this::convertToTaskDetail)
.collect(Collectors.toList());
}
/**
* 获取流程历史
*/
public List<HistoricActivityInstance> getProcessHistory(String processInstanceId) {
return historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processInstanceId)
.orderByHistoricActivityInstanceStartTime()
.asc()
.list();
}
/**
* 获取当前任务的候选人
*/
public List<User> getTaskCandidates(String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(taskId);
// 根据候选人ID查询用户信息
return identityLinks.stream()
.filter(link -> "candidate".equals(link.getType()))
.map(IdentityLink::getUserId)
.map(this::getUserByUserId)
.collect(Collectors.toList());
}
}2.4 审批任务监听器
/**
* 任务创建监听器 - 自动分配任务
*/
@Component
public class TaskCreateListener implements TaskListener {
@Autowired
private TaskAssignmentService assignmentService;
@Override
public void notify(DelegateTask delegateTask) {
String eventName = delegateTask.getEventName();
if (EVENTNAME_CREATE.equals(eventName)) {
// 根据任务定义Key自动分配处理人
String taskKey = delegateTask.getTaskDefinitionKey();
String assignee = assignmentService.determineAssignee(
delegateTask.getProcessInstanceId(),
taskKey
);
if (assignee != null) {
delegateTask.setAssignee(assignee);
log.info("任务已自动分配给: {},任务: {}", assignee, delegateTask.getName());
} else {
// 如果无法自动分配,设置候选人组
String candidateGroup = assignmentService.determineCandidateGroup(taskKey);
if (candidateGroup != null) {
delegateTask.addCandidateGroup(candidateGroup);
log.info("任务已设置候选组: {},任务: {}", candidateGroup, delegateTask.getName());
}
}
}
}
}
/**
* 任务完成监听器
*/
@Component
public class TaskCompleteListener implements TaskListener {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private SupplierRepository supplierRepository;
@Override
public void notify(DelegateTask delegateTask) {
if (EVENTNAME_COMPLETE.equals(delegateTask.getEventName())) {
String processInstanceId = delegateTask.getProcessInstanceId();
String taskKey = delegateTask.getTaskDefinitionKey();
Map<String, Object> variables = delegateTask.getVariables();
// 发布任务完成事件
eventPublisher.publishEvent(new TaskCompletedEvent(
processInstanceId,
taskKey,
variables,
delegateTask.getAssignee()
));
// 如果是最后一个审批节点,更新业务数据
if ("directorApproval".equals(taskKey)) {
boolean approved = (boolean) variables.getOrDefault("approved", false);
String businessKey = getBusinessKey(processInstanceId);
if (businessKey != null) {
updateSupplierStatus(businessKey, approved);
}
}
}
}
}2.5 自定义任务分配策略
@Service
@Slf4j
public class TaskAssignmentService {
@Autowired
private UserService userService;
@Autowired
private DepartmentService departmentService;
/**
* 根据任务Key确定处理人
*/
public String determineAssignee(String processInstanceId, String taskKey) {
// 获取流程上下文
ProcessContext context = getProcessContext(processInstanceId);
switch (taskKey) {
case "buyerReview":
// 采购员初审 → 提交人所在部门的采购员
return findDepartmentBuyer(context.getSubmitterDept());
case "qualityReview":
// 质量部审核 → 质量部当前值班人员
return findQualityReviewer();
case "financeReview":
// 财务部审核 → 财务部指定人员
return findFinanceReviewer();
case "directorApproval":
// 采购总监审批 → 采购总监
return findProcurementDirector();
default:
return null;
}
}
/**
* 确定候选组(当无法确定具体处理人时)
*/
public String determineCandidateGroup(String taskKey) {
switch (taskKey) {
case "buyerReview":
return "BUYER_GROUP";
case "qualityReview":
return "QUALITY_GROUP";
case "financeReview":
return "FINANCE_GROUP";
case "directorApproval":
return "DIRECTOR_GROUP";
default:
return null;
}
}
}场景三:供应商评估工作流
@Service
@Slf4j
public class AssessmentWorkflowService {
@Autowired
private ActivitiWorkflowService workflowService;
@Autowired
private AssessmentEngine assessmentEngine;
/**
* 启动供应商评估流程
*/
public String startAssessment(Long supplierId, String templateId, User starter) {
Map<String, Object> variables = new HashMap<>();
variables.put("supplierId", supplierId);
variables.put("templateId", templateId);
variables.put("starter", starter.getUsername());
// 启动流程
String processInstanceId = workflowService.startProcess(
"supplier_assessment",
supplierId.toString(),
starter.getUsername(),
variables
);
log.info("评估流程已启动: supplierId={}, processInstanceId={}",
supplierId, processInstanceId);
return processInstanceId;
}
/**
* 执行评估计算(流程中的服务任务)
*/
@Service
public static class AssessmentCalculateDelegate implements JavaDelegate {
@Autowired
private AssessmentEngine assessmentEngine;
@Autowired
private AssessmentResultRepository resultRepository;
@Override
public void execute(DelegateExecution execution) {
Long supplierId = (Long) execution.getVariable("supplierId");
String templateId = (String) execution.getVariable("templateId");
String processInstanceId = execution.getProcessInstanceId();
log.info("开始执行评估计算: supplierId={}, templateId={}", supplierId, templateId);
try {
// 执行评估计算
AssessmentResult result = assessmentEngine.calculate(supplierId, templateId);
// 保存评估结果
result.setProcessInstanceId(processInstanceId);
resultRepository.save(result);
// 设置流程变量
execution.setVariable("totalScore", result.getTotalScore());
execution.setVariable("level", result.getLevel().name());
execution.setVariable("approved", result.getTotalScore() >= 60);
log.info("评估计算完成: supplierId={}, score={}, level={}",
supplierId, result.getTotalScore(), result.getLevel());
} catch (Exception e) {
log.error("评估计算失败", e);
execution.setVariable("approved", false);
execution.setVariable("errorMessage", e.getMessage());
throw new BpmnError("ASSESSMENT_ERROR", "评估计算异常");
}
}
}
}场景四:审批流程前端集成(REST API)
@RestController
@RequestMapping("/api/workflow")
@Slf4j
public class WorkflowController {
@Autowired
private ActivitiWorkflowService workflowService;
@Autowired
private WorkflowDiagramService diagramService;
/**
* 获取用户待办任务列表
*/
@GetMapping("/tasks")
public ApiResponse<List<TaskVO>> getTasks(@RequestParam String userId) {
List<TaskDetail> tasks = workflowService.getUserTasks(userId);
List<TaskVO> taskVOs = tasks.stream()
.map(this::convertTaskVO)
.collect(Collectors.toList());
return ApiResponse.success(taskVOs);
}
/**
* 获取流程详情(含高亮流程图)
*/
@GetMapping("/process/{processInstanceId}")
public ApiResponse<ProcessDetailVO> getProcessDetail(@PathVariable String processInstanceId) {
ProcessDetailVO detail = new ProcessDetailVO();
// 获取流程基本信息
detail.setProcessInstanceId(processInstanceId);
detail.setHistory(workflowService.getProcessHistory(processInstanceId));
// 获取当前任务
List<Task> tasks = workflowService.getCurrentTasks(processInstanceId);
detail.setCurrentTasks(tasks);
// 生成流程图(高亮当前节点)
String diagram = diagramService.generateDiagram(processInstanceId);
detail.setDiagram(diagram);
return ApiResponse.success(detail);
}
/**
* 提交审批
*/
@PostMapping("/tasks/{taskId}/complete")
public ApiResponse<Void> completeTask(
@PathVariable String taskId,
@RequestBody TaskCompleteRequest request) {
Map<String, Object> variables = new HashMap<>();
variables.put("approved", request.isApproved());
variables.put("comment", request.getComment());
ApprovalResult result = workflowService.completeTask(
taskId,
request.getAssignee(),
variables,
request.getComment()
);
if (result.isSuccess()) {
return ApiResponse.success("审批完成");
} else {
return ApiResponse.error(result.getMessage());
}
}
/**
* 转办任务
*/
@PostMapping("/tasks/{taskId}/transfer")
public ApiResponse<Void> transferTask(
@PathVariable String taskId,
@RequestParam String fromUserId,
@RequestParam String toUserId) {
workflowService.transferTask(taskId, fromUserId, toUserId);
return ApiResponse.success("转办成功");
}
}场景五:工作流事件监听与消息广播
@Component
@Slf4j
public class WorkflowEventListener {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 流程完成事件监听
*/
@EventListener
public void onProcessCompleted(ProcessCompletedEvent event) {
String processInstanceId = event.getProcessInstanceId();
String businessKey = event.getBusinessKey();
Map<String, Object> variables = event.getVariables();
log.info("流程已完成: {}, businessKey={}", processInstanceId, businessKey);
// 根据流程类型处理后续业务
String processType = (String) variables.get("processType");
if ("SUPPLIER_APPROVAL".equals(processType)) {
// 供应商审批完成:更新状态并通知下游
boolean approved = (boolean) variables.get("approved");
Long supplierId = Long.parseLong(businessKey);
// 更新供应商状态
updateSupplierAfterApproval(supplierId, approved);
// 广播消息到下游系统
SupplierApprovalMessage message = new SupplierApprovalMessage(
supplierId,
approved,
variables.get("approver").toString(),
variables.get("comment") != null ? variables.get("comment").toString() : null
);
rabbitTemplate.convertAndSend(
"workflow.exchange.topic",
"supplier.approval.completed",
message
);
} else if ("SUPPLIER_SUSPEND".equals(processType)) {
// 供应商暂停流程完成
Long supplierId = Long.parseLong(businessKey);
// 执行暂停后的清理工作
performSuspendActions(supplierId);
}
}
}三、面试回答话术
"在采购公共服务系统中,我设计并实现了基于状态机+工作流引擎的审批流程体系,主要应用于供应商生命周期管理和评估审批。
在核心设计上,我采用状态机模式管理供应商的完整生命周期(草稿→待审批→已通过→已激活→暂停→终止),每个状态变更都触发对应的审批流程或业务动作。
在复杂审批场景,我引入了Activiti工作流引擎,支持多级审批、会签、转办等能力。通过BPMN标准定义审批流程,实现了采购员初审→质量部审核→财务部审核→采购总监审批的四级审批模型。
在任务分配上,我实现了动态任务分配策略,根据流程上下文(如提交人部门、审批节点类型)自动确定审批人或候选组,支持任务自动转办和超时提醒。
在系统集成上,我将工作流引擎与业务服务深度整合:审批通过后自动更新供应商状态、同步下游系统、发送通知;审批驳回后记录驳回原因并支持重新提交。
在用户体验上,我提供了完整的流程图可视化,高亮显示当前审批节点和历史轨迹,让用户清晰了解审批进度。
这套工作流体系支撑了日均数百个审批流程的稳定运行,将供应商准入周期从2-3周缩短到3天以内。"
状态机详解
一、什么是状态机
状态机是管理对象状态流转的设计模式,核心思想是:状态 + 事件 + 动作 = 状态迁移。
[当前状态] --(触发事件)--> [执行动作] --> [目标状态]二、为什么需要状态机
在没有状态机的情况下,状态管理通常是这样的:
// ❌ 反例:状态逻辑散落在各处
public void submit(Supplier supplier) {
if ("DRAFT".equals(supplier.getStatus())) {
supplier.setStatus("PENDING");
// 保存、发消息...
} else {
throw new Exception("状态不允许提交");
}
}
public void approve(Supplier supplier) {
if ("PENDING".equals(supplier.getStatus())) {
supplier.setStatus("APPROVED");
// 保存、发消息...
} else if ("DRAFT".equals(supplier.getStatus())) {
// 不同的逻辑...
}
// 更多的if-else...
}问题:状态逻辑分散、边界不清晰、容易遗漏、难以维护。
三、采购系统中的状态机实现
3.1 供应商生命周期状态机
/**
* 供应商状态枚举
*/
@Getter
@AllArgsConstructor
public enum SupplierStatus {
DRAFT("草稿", "初始创建"),
PENDING_APPROVAL("待审批", "已提交审批"),
APPROVED("已通过", "审批通过"),
ACTIVE("已激活", "正式合作"),
SUSPENDED("已暂停", "暂停合作"),
TERMINATED("已终止", "终止合作"),
REJECTED("已驳回", "审批驳回");
private final String desc;
private final String detail;
}
/**
* 状态机事件定义
*/
@Getter
@AllArgsConstructor
public enum SupplierEvent {
SUBMIT("提交审批"),
APPROVE("审批通过"),
REJECT("审批驳回"),
ACTIVATE("激活"),
SUSPEND("暂停"),
TERMINATE("终止"),
RESUBMIT("重新提交");
private final String desc;
}
/**
* 供应商状态机核心实现
*/
@Component
@Slf4j
public class SupplierStateMachine {
// 状态转移表:定义所有合法的状态转移
private static final Map<SupplierStatus, Map<SupplierEvent, SupplierStatus>> TRANSITIONS = new EnumMap<>(SupplierStatus.class);
// 状态前置校验器
private static final Map<SupplierStatus, List<Predicate<Supplier>>> PRE_VALIDATORS = new EnumMap<>(SupplierStatus.class);
// 状态后置处理器
private static final Map<SupplierStatus, List<Consumer<Supplier>>> POST_PROCESSORS = new EnumMap<>(SupplierStatus.class);
@Autowired
private SupplierRepository supplierRepository;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private RabbitTemplate rabbitTemplate;
// 静态初始化:定义状态转移规则
static {
// 初始化转移表
TRANSITIONS.put(SupplierStatus.DRAFT, new EnumMap<>(SupplierEvent.class));
TRANSITIONS.put(SupplierStatus.PENDING_APPROVAL, new EnumMap<>(SupplierEvent.class));
TRANSITIONS.put(SupplierStatus.APPROVED, new EnumMap<>(SupplierEvent.class));
TRANSITIONS.put(SupplierStatus.ACTIVE, new EnumMap<>(SupplierEvent.class));
TRANSITIONS.put(SupplierStatus.SUSPENDED, new EnumMap<>(SupplierEvent.class));
TRANSITIONS.put(SupplierStatus.TERMINATED, new EnumMap<>(SupplierEvent.class));
TRANSITIONS.put(SupplierStatus.REJECTED, new EnumMap<>(SupplierEvent.class));
// 定义合法转移:DRAFT → PENDING_APPROVAL (SUBMIT)
TRANSITIONS.get(SupplierStatus.DRAFT).put(SupplierEvent.SUBMIT, SupplierStatus.PENDING_APPROVAL);
TRANSITIONS.get(SupplierStatus.DRAFT).put(SupplierEvent.TERMINATE, SupplierStatus.TERMINATED);
// PENDING_APPROVAL → APPROVED (APPROVE) / REJECTED (REJECT)
TRANSITIONS.get(SupplierStatus.PENDING_APPROVAL).put(SupplierEvent.APPROVE, SupplierStatus.APPROVED);
TRANSITIONS.get(SupplierStatus.PENDING_APPROVAL).put(SupplierEvent.REJECT, SupplierStatus.REJECTED);
// APPROVED → ACTIVE (ACTIVATE)
TRANSITIONS.get(SupplierStatus.APPROVED).put(SupplierEvent.ACTIVATE, SupplierStatus.ACTIVE);
TRANSITIONS.get(SupplierStatus.APPROVED).put(SupplierEvent.TERMINATE, SupplierStatus.TERMINATED);
// ACTIVE → SUSPENDED (SUSPEND) / TERMINATED (TERMINATE)
TRANSITIONS.get(SupplierStatus.ACTIVE).put(SupplierEvent.SUSPEND, SupplierStatus.SUSPENDED);
TRANSITIONS.get(SupplierStatus.ACTIVE).put(SupplierEvent.TERMINATE, SupplierStatus.TERMINATED);
// SUSPENDED → ACTIVE (ACTIVATE) / TERMINATED (TERMINATE)
TRANSITIONS.get(SupplierStatus.SUSPENDED).put(SupplierEvent.ACTIVATE, SupplierStatus.ACTIVE);
TRANSITIONS.get(SupplierStatus.SUSPENDED).put(SupplierEvent.TERMINATE, SupplierStatus.TERMINATED);
// REJECTED → PENDING_APPROVAL (RESUBMIT) / TERMINATED (TERMINATE)
TRANSITIONS.get(SupplierStatus.REJECTED).put(SupplierEvent.RESUBMIT, SupplierStatus.PENDING_APPROVAL);
TRANSITIONS.get(SupplierStatus.REJECTED).put(SupplierEvent.TERMINATE, SupplierStatus.TERMINATED);
// 注册前置校验器
PRE_VALIDATORS.put(SupplierStatus.PENDING_APPROVAL, Arrays.asList(
SupplierStateMachine::validateSupplierDataComplete,
SupplierStateMachine::validateSupplierCategory
));
PRE_VALIDATORS.put(SupplierStatus.ACTIVE, Arrays.asList(
SupplierStateMachine::validateSupplierApproved
));
// 注册后置处理器
POST_PROCESSORS.put(SupplierStatus.ACTIVE, Arrays.asList(
SupplierStateMachine::createSupplierAccount,
SupplierStateMachine::syncToDownstream,
SupplierStateMachine::sendWelcomeEmail
));
POST_PROCESSORS.put(SupplierStatus.SUSPENDED, Arrays.asList(
SupplierStateMachine::clearSupplierCache,
SupplierStateMachine::notifyDownstreamSuspension
));
POST_PROCESSORS.put(SupplierStatus.TERMINATED, Arrays.asList(
SupplierStateMachine::clearAllCache,
SupplierStateMachine::notifyDownstreamTermination,
SupplierStateMachine::archiveSupplierData
));
}
/**
* 核心方法:触发状态变更
*/
@Transactional
public Supplier fireEvent(Long supplierId, SupplierEvent event,
Map<String, Object> context) {
// 1. 查询当前状态
Supplier supplier = supplierRepository.findById(supplierId)
.orElseThrow(() -> new SupplierNotFoundException(supplierId));
SupplierStatus currentStatus = supplier.getStatus();
SupplierStatus targetStatus = getNextStatus(currentStatus, event);
log.info("状态变更: supplierId={}, {} --[{}]--> {}",
supplierId, currentStatus, event.getDesc(), targetStatus);
// 2. 执行前置校验
executePreValidators(currentStatus, supplier, context);
// 3. 执行状态变更
supplier.setStatus(targetStatus);
supplier.setLastEvent(event.getDesc());
supplier.setUpdateTime(LocalDateTime.now());
// 记录状态变更历史
saveStateHistory(supplier, currentStatus, targetStatus, event, context);
// 4. 保存
supplier = supplierRepository.save(supplier);
// 5. 执行后置处理器
executePostProcessors(targetStatus, supplier, context);
// 6. 发布状态变更事件
eventPublisher.publishEvent(new SupplierStatusChangedEvent(
supplier, currentStatus, targetStatus, event, context
));
log.info("状态变更完成: supplierId={}, 新状态={}", supplierId, targetStatus);
return supplier;
}
/**
* 获取下一个状态
*/
private SupplierStatus getNextStatus(SupplierStatus current, SupplierEvent event) {
SupplierStatus next = TRANSITIONS.getOrDefault(current, Collections.emptyMap())
.get(event);
if (next == null) {
throw new IllegalStateException(
String.format("非法状态转移: %s --[%s]--> ?",
current.getDesc(), event.getDesc())
);
}
return next;
}
/**
* 执行前置校验器
*/
private void executePreValidators(SupplierStatus status, Supplier supplier,
Map<String, Object> context) {
List<Predicate<Supplier>> validators = PRE_VALIDATORS.get(status);
if (validators == null) return;
for (Predicate<Supplier> validator : validators) {
if (!validator.test(supplier)) {
throw new ValidationException("状态前置校验失败: " + validator.toString());
}
}
}
/**
* 执行后置处理器
*/
private void executePostProcessors(SupplierStatus status, Supplier supplier,
Map<String, Object> context) {
List<Consumer<Supplier>> processors = POST_PROCESSORS.get(status);
if (processors == null) return;
for (Consumer<Supplier> processor : processors) {
try {
processor.accept(supplier);
} catch (Exception e) {
log.error("后置处理器执行失败: {}", e.getMessage(), e);
// 记录失败,通过补偿任务重试
compensationService.recordFailure(supplier.getId(),
"POST_PROCESSOR_" + status.name(), e.getMessage());
// 不抛出异常,避免事务回滚
}
}
}
// ============ 前置校验器实现 ============
private static boolean validateSupplierDataComplete(Supplier supplier) {
return StringUtils.hasText(supplier.getCode())
&& StringUtils.hasText(supplier.getName())
&& supplier.getCategory() != null;
}
private static boolean validateSupplierCategory(Supplier supplier) {
return supplier.getCategory() != null
&& supplier.getCategory().getCode() != null;
}
private static boolean validateSupplierApproved(Supplier supplier) {
return supplier.getStatus() == SupplierStatus.APPROVED;
}
// ============ 后置处理器实现 ============
private static void createSupplierAccount(Supplier supplier) {
log.info("创建供应商账号: {}", supplier.getId());
// 调用账号服务
}
private static void syncToDownstream(Supplier supplier) {
log.info("同步供应商到下游系统: {}", supplier.getId());
// 通过MQ广播
}
private static void sendWelcomeEmail(Supplier supplier) {
log.info("发送欢迎邮件: {}", supplier.getId());
// 发送邮件
}
private static void clearSupplierCache(Supplier supplier) {
log.info("清除供应商缓存: {}", supplier.getId());
// 清除Redis缓存
}
private static void notifyDownstreamSuspension(Supplier supplier) {
log.info("通知下游系统暂停: {}", supplier.getId());
// 发送暂停通知
}
private static void clearAllCache(Supplier supplier) {
log.info("清除所有缓存: {}", supplier.getId());
}
private static void notifyDownstreamTermination(Supplier supplier) {
log.info("通知下游系统终止: {}", supplier.getId());
}
private static void archiveSupplierData(Supplier supplier) {
log.info("归档供应商数据: {}", supplier.getId());
// 数据归档
}
/**
* 记录状态变更历史
*/
private void saveStateHistory(Supplier supplier, SupplierStatus oldStatus,
SupplierStatus newStatus, SupplierEvent event,
Map<String, Object> context) {
SupplierStateHistory history = SupplierStateHistory.builder()
.supplierId(supplier.getId())
.oldStatus(oldStatus.name())
.newStatus(newStatus.name())
.event(event.name())
.operator((String) context.getOrDefault("operator", "system"))
.context(JsonUtils.toJson(context))
.createTime(LocalDateTime.now())
.build();
// 异步保存历史
CompletableFuture.runAsync(() ->
stateHistoryRepository.save(history));
}
/**
* 批量状态变更(用于定时任务)
*/
public void batchFireEvents(List<StateChangeRequest> requests) {
List<CompletableFuture<Void>> futures = requests.stream()
.map(req -> CompletableFuture.runAsync(() -> {
try {
fireEvent(req.getSupplierId(), req.getEvent(), req.getContext());
} catch (Exception e) {
log.error("批量状态变更失败: {}", req.getSupplierId(), e);
}
}, stateMachineExecutor))
.collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.orTimeout(5, TimeUnit.MINUTES)
.join();
}
}3.2 状态机对外服务层
/**
* 状态机对外服务
*/
@Service
public class SupplierStateService {
@Autowired
private SupplierStateMachine stateMachine;
@Autowired
private ApprovalService approvalService;
/**
* 提交审批(带业务上下文)
*/
public Supplier submitForApproval(Long supplierId, User submitter) {
Map<String, Object> context = new HashMap<>();
context.put("submitter", submitter.getUsername());
context.put("submitTime", LocalDateTime.now());
context.put("processType", "SUPPLIER_APPROVAL");
Supplier supplier = stateMachine.fireEvent(
supplierId,
SupplierEvent.SUBMIT,
context
);
// 提交后额外业务:创建审批流程
approvalService.createApprovalProcess(supplier, submitter);
return supplier;
}
/**
* 审批通过
*/
public Supplier approveSupplier(Long supplierId, User approver, String comment) {
Map<String, Object> context = new HashMap<>();
context.put("approver", approver.getUsername());
context.put("comment", comment);
context.put("approveTime", LocalDateTime.now());
return stateMachine.fireEvent(
supplierId,
SupplierEvent.APPROVE,
context
);
}
/**
* 激活供应商
*/
public Supplier activateSupplier(Long supplierId, User operator) {
Map<String, Object> context = new HashMap<>();
context.put("operator", operator.getUsername());
context.put("activateTime", LocalDateTime.now());
return stateMachine.fireEvent(
supplierId,
SupplierEvent.ACTIVATE,
context
);
}
}3.3 状态机配置管理(支持动态调整)
/**
* 状态机配置实体
*/
@Entity
@Table(name = "state_machine_config")
@Data
public class StateMachineConfig {
@Id
private Long id;
private String entityType; // 实体类型:SUPPLIER, ORDER等
private String currentState;
private String event;
private String targetState;
private Integer priority; // 优先级
private String preValidator; // 前置校验器Bean名称
private String postProcessor; // 后置处理器Bean名称
private Boolean enabled = true;
private String description;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
/**
* 动态状态机配置加载器
*/
@Component
@Slf4j
public class DynamicStateMachineConfig {
@Autowired
private StateMachineConfigRepository configRepository;
@Autowired
private ApplicationContext applicationContext;
// 缓存状态机配置
private final LoadingCache<String, List<StateMachineConfig>> configCache =
Caffeine.newBuilder()
.maximumSize(100)
.refreshAfterWrite(5, TimeUnit.MINUTES)
.build(this::loadConfigs);
/**
* 从数据库加载配置
*/
private List<StateMachineConfig> loadConfigs(String entityType) {
List<StateMachineConfig> configs = configRepository
.findByEntityTypeAndEnabledTrue(entityType);
log.info("加载状态机配置: entityType={}, count={}", entityType, configs.size());
return configs;
}
/**
* 动态添加状态转移规则(无需重启)
*/
@Transactional
public void addTransition(StateMachineConfig config) {
configRepository.save(config);
configCache.invalidate(config.getEntityType());
log.info("动态添加状态转移规则: {} --{}--> {}",
config.getCurrentState(), config.getEvent(), config.getTargetState());
}
/**
* 执行状态转移
*/
public SupplierStatus executeTransition(String entityType, String currentState,
String event, Map<String, Object> context) {
List<StateMachineConfig> configs = configCache.get(entityType);
StateMachineConfig matchedConfig = configs.stream()
.filter(c -> c.getCurrentState().equals(currentState))
.filter(c -> c.getEvent().equals(event))
.findFirst()
.orElseThrow(() -> new IllegalStateException(
String.format("未找到匹配的状态转移规则: %s --%s--> ?", currentState, event)
));
// 执行前置校验器
if (matchedConfig.getPreValidator() != null) {
Supplier<Boolean> validator = (Supplier<Boolean>) applicationContext
.getBean(matchedConfig.getPreValidator());
if (!validator.get()) {
throw new ValidationException("前置校验失败");
}
}
// 执行后置处理器
if (matchedConfig.getPostProcessor() != null) {
Runnable processor = (Runnable) applicationContext
.getBean(matchedConfig.getPostProcessor());
processor.run();
}
return SupplierStatus.valueOf(matchedConfig.getTargetState());
}
}四、为什么用状态机
| 优势 | 说明 | 采购系统中的应用 |
|---|---|---|
| 消除if-else | 用转移表替代散落的条件判断 | 代码简洁度提升60% |
| 代码自解释 | 状态定义即文档 | 新人理解成本降低70% |
| 防止非法流转 | 状态转移表控制所有合法变更 | 供应商无法从"草稿"直接跳到"已激活" |
| 易于扩展 | 新增状态只需修改转移表 | 新增"供应商冻结"状态只需改配置 |
| 事件驱动 | 状态变更自动触发业务动作 | 激活自动创建账号、同步下游 |
| 可观测 | 所有状态变更都可记录审计 | 满足上市合规要求 |
核心价值:把复杂的业务规则变成一张清晰的"状态-事件-目标状态"表,让代码更简洁、逻辑更清晰、维护更安全。
To Be Continued.

