# 流程附件上传机制优化

## 任务概述
将 `BpmProcessInstanceController.uploadAttachment` 方法从硬编码映射改为通过配置表动态获取实体类和Mapper类，与 `ProcessQueryServiceImpl` 保持一致的反射机制。

## 实施方案
**方案1：完全使用配置表机制（已采用）**
- 移除硬编码的 `entityTypeMap` 
- 注入 `ProcessMappingConfigService`
- 修改 `resolveEntityType` 方法通过配置表动态获取类信息
- 参数兼容：`processType` 映射到 `processCode`

## 具体修改

### 1. 移除硬编码机制
```java
// 删除了以下内容：
private final Map<String, List> entityTypeMap = new ConcurrentHashMap<>();

@PostConstruct
public void init() {
    // 注册支持的实体类型
    entityTypeMap.put("oa_reimbursement", new ArrayList<Class<?>>(Arrays.asList(BpmOAReimbursementDO.class, BpmOAReimbursementMapper.class)));
    // ... 其他30多个映射关系
}
```

### 2. 新增依赖注入
```java
@Resource
private ProcessMappingConfigService processMappingConfigService;
```

### 3. 重构解析方法
```java
private List<Class<?>> resolveEntityType(String processCode) {
    try {
        // 1. 获取流程映射配置
        BpmProcessMappingConfigDO config = processMappingConfigService.getProcessMappingConfigByCode(processCode);
        if (config == null) {
            log.error("[resolveEntityType][流程映射配置不存在] processCode={}", processCode);
            throw exception(PROCESS_MAPPING_CONFIG_NOT_EXISTS);
        }

        // 2. 动态加载实体类和Mapper类
        Class<?> entityClass = ClassUtil.loadClass(config.getEntityClass());
        Class<?> mapperClass = ClassUtil.loadClass(config.getMapperClass());

        // 3. 验证Mapper类是否可获取Bean实例
        SpringUtil.getBean(mapperClass);

        return Arrays.asList(entityClass, mapperClass);
        
    } catch (Exception e) {
        log.error("[resolveEntityType][解析流程类型失败] processCode={}", processCode, e);
        throw new ServiceException(500, String.format("不支持的流程类型: %s, 错误信息: %s", processCode, e.getMessage()));
    }
}
```

### 4. 新增import语句
```java
import cn.hutool.core.util.ClassUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.module.bpm.dal.dataobject.processmapping.BpmProcessMappingConfigDO;
import cn.iocoder.yudao.module.bpm.service.processmapping.ProcessMappingConfigService;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_MAPPING_CONFIG_NOT_EXISTS;
```

## 技术优势

### 1. 统一反射机制
- ✅ 与 `ProcessQueryServiceImpl` 采用相同的配置表查询方式
- ✅ 使用相同的工具类（`ClassUtil`, `SpringUtil`）
- ✅ 统一的异常处理和错误码

### 2. 动态配置支持
- ✅ 支持通过配置表动态添加新的流程类型
- ✅ 无需修改代码和重启服务
- ✅ 配置变更可通过管理界面完成

### 3. 代码质量提升
- ✅ 消除了30多行硬编码映射
- ✅ 减少了代码维护成本
- ✅ 提高了系统的可扩展性

### 4. 兼容性保证
- ✅ API接口保持不变
- ✅ 前端代码无需修改
- ✅ `processType` 参数直接作为 `processCode` 使用

## 配置表结构
系统依赖 `bmp_process_mapping_config` 表的以下字段：
- `process_code`: 流程唯一标识（对应前端传入的 `processType`）
- `entity_class`: 实体类全限定名
- `mapper_class`: Mapper接口全限定名

## 使用示例
前端调用方式保持不变：
```javascript
{
  "processType": "oa_reimbursement",  // 会作为processCode查询配置表
  "processBusinessId": 123,
  "fileItems": [...]
}
```

后台会自动：
1. 根据 `processType="oa_reimbursement"` 查询配置表
2. 获取对应的实体类和Mapper类
3. 动态实例化并调用附件更新服务

## 执行时间
2024年12月19日

## 状态
✅ 已完成（已优化架构）

## 后续优化
**2024年12月19日 - 架构优化：**

### 1. 分层重构
- **问题**：`resolveEntityType` 方法不应放在Controller层
- **解决**：将类型解析逻辑移至 `BpmOAAttachmentService` 服务层
- **优势**：符合分层架构原则，Controller只负责接口调用

### 2. 使用ReflectionInvoker工具类
- **问题**：`BpmOAAttachmentServiceImpl.updateAttachment` 使用手动反射
- **解决**：替换为 `ReflectionInvoker.invokeMapperMethod` 调用
- **优势**：更安全、统一的反射机制，支持代理类处理

### 3. 优化后的调用链
```
Controller -> Service (类型解析) -> Service (附件更新) -> ReflectionInvoker (Mapper调用)
```

### 4. 主要变更文件
- `BpmOAAttachmentService.java`: 添加 `resolveEntityType` 接口方法
- `BpmOAAttachmentServiceImpl.java`: 实现类型解析和优化反射调用
- `BpmProcessInstanceController.java`: 简化Controller逻辑，移除业务逻辑

### 5. 技术收益
- ✅ 更清晰的分层架构
- ✅ 统一的反射调用机制
- ✅ 更好的错误处理和日志记录
- ✅ 代码复用性提升

## 验证要点
1. 确保配置表中存在对应的流程映射配置
2. 验证实体类和Mapper类路径正确
3. 测试异常情况的错误处理和日志记录
4. 确认ReflectionInvoker正确处理代理类和方法调用 