面试笔录 - 2026项目面试技巧与面经

Java 后端开发2026年项目面试技巧(DeepSeek总结版)与面经总结

目录指引

策略模式应用

XXL-JOB + 策略模式的组合,能够完美实现调度与业务的解耦,既享受到XXL-JOB的分布式调度能力,又保留了策略模式的灵活扩展性。


一、整体架构设计

deepseek_mermaid_20260626_e67528

二、核心代码实现

1. 任务策略接口(与XXL-JOB解耦)

java
/**
 * 任务策略接口 - 专注于业务逻辑,不关心调度
 */
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. 具体任务策略实现

java
@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. 策略注册中心

java
@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统一调度入口

java
@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. 任务执行监控

java
@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同步)

java
@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的管理界面中,只需要配置一个任务

配置项
任务描述统一任务调度入口
JobHandlerexecuteTask
运行模式BEAN模式
任务参数{taskId},如 tempFileCleanupTask
yaml
# 不同任务的调度配置通过任务参数区分
# 示例:配置多个任务
- 任务: 清理临时文件
  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-JOBXXL-JOB + 策略模式
调度高可用❌ 需自研✅ 原生支持✅ 原生支持
可视化运维❌ 需自研✅ 原生支持✅ 原生支持
任务扩展性✅ 灵活⚠️ 需配置✅ 灵活
代码复用✅ 高复用❌ 重复配置✅ 高复用
业务解耦✅ 解耦⚠️ 耦合度一般✅ 彻底解耦
动态配置✅ 灵活⚠️ 需重启✅ 动态灵活
分片能力❌ 需自研✅ 原生支持✅ 原生支持

核心价值

"通过XXL-JOB + 策略模式的组合,我们实现了调度平台标准化业务逻辑灵活化的完美统一。XXL-JOB负责解决分布式调度、高可用、可视化运维等平台级问题,而策略模式则负责让40+个任务的业务逻辑保持清晰、独立和可扩展。两者各司其职,互不侵入。"

扩展能力

java
// 新增加一个任务只需要三步:

// 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工作流实现详解

在采购公共服务系统中,工作流主要体现在供应商生命周期管理采购审批流程供应商评估流程等业务场景。

一、整体架构设计

deepseek_mermaid_20260717_796875

二、核心工作流场景实现

场景一:供应商生命周期管理(状态机驱动)

业务背景:供应商从注册到淘汰,经历多个状态流转,每个状态变更都需要审批或触发相应的业务动作。

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依赖
xml
<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
<?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 工作流服务封装
java
@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 审批任务监听器
java
/**
 * 任务创建监听器 - 自动分配任务
 */
@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 自定义任务分配策略
java
@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;
        }
    }
}

场景三:供应商评估工作流

java
@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)

java
@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("转办成功");
    }
}

场景五:工作流事件监听与消息广播

java
@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天以内。"


状态机详解

一、什么是状态机

状态机是管理对象状态流转的设计模式,核心思想是:状态 + 事件 + 动作 = 状态迁移

[当前状态] --(触发事件)--> [执行动作] --> [目标状态]

二、为什么需要状态机

在没有状态机的情况下,状态管理通常是这样的:

java
// ❌ 反例:状态逻辑散落在各处
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 供应商生命周期状态机

java
/**
 * 供应商状态枚举
 */
@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 状态机对外服务层

java
/**
 * 状态机对外服务
 */
@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 状态机配置管理(支持动态调整)

java
/**
 * 状态机配置实体
 */
@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.
AI基础与SpringAI
面试笔录 - 面经与场景题