Skip to content

钩子方法参考

🪝 钩子详解:Basic继承体系中所有钩子方法的完整说明,必须遵循Basic继承指南规范

📋 目录


钩子方法概述

什么是钩子方法?

钩子方法是Basic继承体系提供的扩展点,允许在特定的操作节点插入自定义的业务逻辑。

⚠️ 重要:必须使用正确的导入路径

在使用钩子方法时,必须确保所有相关类使用项目正确的包路径:

java
// ✅ 正确的导入路径
import jpwise.util.UserProvider;                    // 用户提供者
import jpwise.model.base.UserInfo;                  // 用户信息
import jpwise.model.flow.FlowTaskEntity;            // 工作流任务实体
import jpwise.engine.model.flowengine.FlowModel;    // 工作流模型
import jpwise.base.enums.FlowRecordEnum;             // 工作流记录枚举
import jpwise.util.JsonUtil;                        // JSON工具类
import jpwise.util.DateUtil;                        // 日期工具类
import jpwise.util.StringUtil;                      // 字符串工具类
import jpwise.util.RandomUtil;                      // 随机工具类
import jpwise.exception.BusinessException;          // 业务异常
import jpwise.exception.DataException;              // 数据异常

⚠️ 常见错误:使用错误的包路径会导致编译失败或运行时异常。

📋 快速开始模板

java
@Slf4j  // ✅ 必须添加
@Service
@UseDataSource(DbNameConst.JPWISE_DEMO)
public class YourServiceImpl extends BasicDemoServiceImpl<YourMapper, YourEntity> 
                            implements YourService {
    
    @Autowired
    private UserProvider userProvider;
    
    // 只添加实际需要的钩子方法
}

### 🔥 核心编码规范(必须遵循)
1. **Entity优先原则** - 转换为Entity对象进行类型安全操作
2. **避免双重操作** - 不要同时修改entity和dic
3. **统一转换** - 最后用JsonUtil.entityToMap()转换回dic
4. **按需添加** - 只有用户明确需要时才添加对应钩子方法
5. **调用super方法** - 钩子方法中必须调用父类方法

### 钩子方法分类
- **数据操作钩子** - 保存、删除等操作的前后处理
- **子表操作钩子** - 主子表数据的协调处理
- **工作流钩子** - 工作流各个环节的业务处理
- **数据获取钩子** - 查询数据的后处理

---

## 数据操作钩子

### beforeSave - 保存前处理
**时机**:数据保存到数据库之前  
**用途**:数据验证、字段补全、业务规则检查

```java
@Override
public Map<String, Object> beforeSave(Map<String, Object> dic, Boolean isNew) 
       throws BusinessException, DataException {
    
    // ✅ 正确:转换为Entity对象进行类型安全操作
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    if (isNew) {
        // 新增时的处理 - 只操作entity对象
        if (StringUtil.isEmpty(entity.getCODE())) {
            // 生成业务编号
            entity.setCODE(generateBusinessCode());
        }
        
        // ✅ 正确:避免硬编码常量,使用方法获取
        entity.setSTATUS(getDefaultStatus());
        
        // 设置申请人信息
        UserInfo userInfo = userProvider.get();
        entity.setAPPLICANTNAME(userInfo.getUserName());
        
    } else {
        // 更新时的处理
        if (!getDraftStatus().equals(entity.getSTATUS())) {
            throw new BusinessException("只有草稿状态的记录才能修改");
        }
    }
    
    // 业务规则验证
    validateBusinessRules(entity);
    
    // 数据完整性检查
    if (StringUtil.isEmpty(entity.getTITLE())) {
        throw new BusinessException("标题不能为空");
    }
    
    // ✅ 正确:最后统一转换回dic,避免同时操作entity和dic
    Map<String, Object> result = JsonUtil.entityToMap(entity);
    result.putAll(dic); // 保留原有的其他字段
    
    return super.beforeSave(result, isNew);
}

private void validateBusinessRules(MyEntity entity) {
    if (entity.getAMOUNT() != null && entity.getAMOUNT().compareTo(getMaxAmount()) > 0) {
        throw new BusinessException("金额不能超过限制");
    }
}

❌ 错误示例 - 避免这样写:

java
@Override
public Map<String, Object> beforeSave(Map<String, Object> dic, Boolean isNew) {
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // ❌ 错误:同时修改entity和dic(重复操作)
    entity.setCODE("NEW001");
    dic.put("CODE", "NEW001");     // 重复操作
    
    return super.beforeSave(dic, isNew);
}

常用场景

  • 生成业务编号
  • 设置默认值
  • 数据验证
  • 权限检查
  • 重复数据检查

afterSave - 保存后处理

时机:数据成功保存到数据库之后
用途:发送通知、更新缓存、记录日志、触发其他业务

java
@Override
public Map<String, Object> afterSave(Map<String, Object> dic, Boolean isNew) 
       throws BusinessException, DataException {
    
    // ✅ 正确:转换为Entity对象进行类型安全操作
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    if (isNew) {
        // 新增后处理
        log.info("用户{}创建了新记录:{}", userProvider.get().getUserId(), entity.getCODE());
        
        // 发送创建通知
        sendCreateNotification(entity);
        
        // 更新统计缓存
        updateStatisticsCache();
        
    } else {
        // 更新后处理
        log.info("用户{}更新了记录:{}", userProvider.get().getUserId(), entity.getCODE());
        
        // 发送更新通知
        sendUpdateNotification(entity);
    }
    
    // 清除相关缓存
    clearEntityCache(entity.getID());
    
    // ✅ 正确:如果需要修改数据,先操作entity再转换
    if (needsPostProcessing(entity)) {
        entity.setLastProcessTime(DateUtil.getNowDate());
        
        // 转换回dic返回
        Map<String, Object> result = JsonUtil.entityToMap(entity);
        result.putAll(dic); // 保留其他字段
        return super.afterSave(result, isNew);
    }
    
    return super.afterSave(dic, isNew);
}

常用场景

  • 发送通知消息
  • 更新缓存
  • 记录操作日志
  • 触发后续流程
  • 统计数据更新

beforeDelete - 删除前验证

时机:数据删除(软删除)之前
用途:删除权限检查、关联数据验证、业务规则验证

java
@Override
public boolean beforeDelete(Map<String, Object> dic) 
       throws BusinessException, DataException {
    
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // 权限检查
    if (!hasDeletePermission(entity)) {
        throw new BusinessException("您没有删除此记录的权限");
    }
    
    // 状态检查
    if ("APPROVED".equals(entity.getSTATUS())) {
        throw new BusinessException("已审批的记录不能删除");
    }
    
    // 关联数据检查
    if (hasRelatedData(entity.getID())) {
        throw new BusinessException("存在关联数据,无法删除");
    }
    
    // 业务规则检查
    if (entity.getAMOUNT() != null && entity.getAMOUNT().compareTo(BigDecimal.ZERO) > 0) {
        throw new BusinessException("有金额的记录需要经过审批才能删除");
    }
    
    return super.beforeDelete(dic);
}

private boolean hasDeletePermission(MyEntity entity) {
    UserInfo user = userProvider.get();
    // 创建者或管理员可以删除
    return user.getUserId().equals(entity.getCREATORUSER()) || user.isAdmin();
}

private boolean hasRelatedData(String id) {
    // 检查是否有子表数据或其他关联数据
    return relatedService.countByMainId(id) > 0;
}

常用场景

  • 删除权限验证
  • 数据状态检查
  • 关联数据验证
  • 业务规则验证

afterDelete - 删除后处理

时机:数据成功删除(软删除)之后
用途:清理缓存、发送通知、记录日志

java
@Override
public void afterDelete(Map<String, Object> dic) 
       throws BusinessException, DataException {
    
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // 清理相关缓存
    String cacheKey = "business:" + entity.getID();
    redisUtil.remove(cacheKey);
    
    // 清理列表缓存
    String listCachePattern = "business:list:*";
    redisUtil.removePattern(listCachePattern);
    
    // 发送删除通知
    String title = "记录删除通知";
    String content = "记录\"" + entity.getTITLE() + "\"已被删除";
    messageService.sentMessage(getNotificationUsers(), title, content, 1);
    
    // 记录操作日志
    log.info("用户{}删除了记录:{}", userProvider.get().getUserId(), entity.getID());
    
    super.afterDelete(dic);
}

子表操作钩子

beforeSaveSubTable - 子表保存前处理

时机:子表数据保存之前
用途:子表数据验证、关联ID设置、业务规则检查

java
@Override
public List<Map<String, Object>> beforeSaveSubTable(Map<String, Object> dic, 
                                                   Class<?> subTableEntity, 
                                                   List<Map<String, Object>> subTable) 
       throws BusinessException {
    
    // 获取主表数据
    MyEntity mainEntity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // 验证子表数据完整性
    if (subTable.isEmpty()) {
        throw new BusinessException("至少需要一条明细记录");
    }
    
    BigDecimal totalAmount = BigDecimal.ZERO;
    
    for (Map<String, Object> subItem : subTable) {
        // 设置关联ID
        subItem.put("MAINID", mainEntity.getID());
        
        // 转换为子表实体进行验证
        DetailEntity detail = JsonUtil.getJsonToBean(subItem, DetailEntity.class);
        
        // 验证子表数据
        if (StringUtil.isEmpty(detail.getDETAILNAME())) {
            throw new BusinessException("明细名称不能为空");
        }
        
        if (detail.getQUANTITY() == null || detail.getQUANTITY() <= 0) {
            throw new BusinessException("数量必须大于0");
        }
        
        if (detail.getAMOUNT() == null || detail.getAMOUNT().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("金额必须大于0");
        }
        
        // 计算总金额
        totalAmount = totalAmount.add(detail.getAMOUNT());
        
        // 生成子表ID(如果没有)
        if (StringUtil.isEmpty(detail.getID())) {
            subItem.put("ID", RandomUtil.uuId());
        }
    }
    
    // 验证总金额
    if (totalAmount.compareTo(new BigDecimal("1000000")) > 0) {
        throw new BusinessException("总金额不能超过100万");
    }
    
    // 更新主表的总金额
    dic.put("TOTALAMOUNT", totalAmount);
    
    return super.beforeSaveSubTable(dic, subTableEntity, subTable);
}

常用场景

  • 设置子表关联ID
  • 子表数据验证
  • 主子表数据一致性检查
  • 汇总计算

afterSaveSubTable - 子表保存后处理

时机:子表数据成功保存之后
用途:更新主表汇总、发送通知、更新缓存

java
@Override
public void afterSaveSubTable(Map<String, Object> dic, 
                             Class<?> subTableEntity, 
                             List<Map<String, Object>> subTable) 
       throws BusinessException {
    
    MyEntity mainEntity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // 重新计算主表汇总数据
    BigDecimal totalAmount = subTable.stream()
        .map(item -> JsonUtil.getJsonToBean(item, DetailEntity.class))
        .map(DetailEntity::getAMOUNT)
        .reduce(BigDecimal.ZERO, BigDecimal::add);
    
    // 更新主表
    mainEntity.setTOTALAMOUNT(totalAmount);
    this.updateById(mainEntity);
    
    // 清理缓存
    redisUtil.remove("business:" + mainEntity.getID());
    
    // 记录日志
    log.info("主表{}的子表数据已更新,共{}条记录", mainEntity.getID(), subTable.size());
    
    super.afterSaveSubTable(dic, subTableEntity, subTable);
}

工作流钩子

beforeTaskExec - 流程任务执行前

时机:工作流任务执行前(提交、审批、拒绝等操作前)
用途:流程前验证、数据补全、状态检查

java
@Override
public Map<String, Object> beforeTaskExec(Map<String, Object> dic, boolean isNew, 
                                         FlowTaskEntity flowTask, FlowModel flowModel, 
                                         FlowRecordEnum flowRecordEnum) {
    
    // ✅ 正确:转换为Entity对象进行类型安全操作
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // 根据流程操作类型进行不同处理
    switch (flowRecordEnum) {
        case submit:
            // 提交时处理
            if (StringUtil.isEmpty(entity.getCODE())) {
                // 生成业务编号
                entity.setCODE(generateBusinessCode());
            }
            
            // 验证提交条件
            validateForSubmit(entity);
            
            // ✅ 正确:避免硬编码,使用方法获取状态
            entity.setSTATUS(getSubmitStatus());
            entity.setSUBMITTIME(DateUtil.getNowDate());
            break;
            
        case agree:
            // 审批通过时处理
            log.info("审批人{}同意了申请{}", userProvider.get().getUserId(), entity.getCODE());
            entity.setSTATUS(getApprovedStatus());
            break;
            
        case reject:
            // 拒绝时处理
            entity.setSTATUS(getRejectedStatus());
            entity.setREJECTTIME(DateUtil.getNowDate());
            break;
            
        case revoke:
            // 撤回时处理
            entity.setSTATUS(getDraftStatus());
            entity.setREVOKETIME(DateUtil.getNowDate());
            break;
    }
    
    // ✅ 正确:最后统一转换回dic
    Map<String, Object> result = JsonUtil.entityToMap(entity);
    result.putAll(dic); // 保留其他字段
    
    return super.beforeTaskExec(result, isNew, flowTask, flowModel, flowRecordEnum);
}

// ✅ 正确:避免硬编码,提取为私有方法
private String generateBusinessCode() {
    String date = DateUtil.dateNow("yyyyMMdd");
    String random = RandomUtil.enUuid().substring(0, 4).toUpperCase();
    return getCodePrefix() + date + random;
}

private String getCodePrefix() {
    return "BIZ"; // 可以从配置或枚举读取
}

private void validateForSubmit(MyEntity entity) {
    if (entity.getAMOUNT() == null || entity.getAMOUNT().compareTo(BigDecimal.ZERO) <= 0) {
        throw new BusinessException("金额必须大于0才能提交");
    }
    
    if (StringUtil.isEmpty(entity.getTITLE())) {
        throw new BusinessException("标题不能为空");
    }
    
    // 其他提交前验证
}

private String getSubmitStatus() {
    return "PENDING"; // 可以从枚举或配置读取
}

private String getApprovedStatus() {
    return "APPROVED"; // 可以从枚举或配置读取
}

private String getRejectedStatus() {
    return "REJECTED"; // 可以从枚举或配置读取
}

private String getDraftStatus() {
    return "DRAFT"; // 可以从枚举或配置读取
}

❌ 错误示例 - 避免这样写:

java
@Override
public Map<String, Object> beforeTaskExec(Map<String, Object> dic, boolean isNew, 
                                         FlowTaskEntity flowTask, FlowModel flowModel, 
                                         FlowRecordEnum flowRecordEnum) {
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    if (flowRecordEnum == submit) {
        
        // ❌ 错误:同时操作entity和dic(重复操作)
        dic.put("CODE", "BIZ20240814001");
        dic.put("STATUS", "PENDING");
    }
    
    return super.beforeTaskExec(dic, isNew, flowTask, flowModel, flowRecordEnum);
}

afterTaskExec - 流程任务执行后

时机:工作流任务执行后
用途:发送通知、记录日志、更新相关数据

java
@Override
public void afterTaskExec(Map<String, Object> dic, boolean isNew, 
                         FlowTaskEntity flowTask, FlowModel flowModel, 
                         FlowRecordEnum flowRecordEnum) 
       throws BusinessException, DataException {
    
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // 根据操作类型发送不同通知
    switch (flowRecordEnum) {
        case submit:
            // 提交后通知审批人
            List<String> approvers = getApprovers(entity);
            if (!approvers.isEmpty()) {
                String title = "待审批提醒";
                String content = String.format("您有新的审批任务:%s", entity.getTITLE());
                messageService.sentMessage(approvers, title, content, 1);
            }
            break;
            
        case agree:
            // 审批通过后通知申请人
            String approveTitle = "审批通过通知";
            String approveContent = String.format("您的申请【%s】已审批通过", entity.getTITLE());
            messageService.sentMessage(Arrays.asList(entity.getCREATORUSER()), 
                                     approveTitle, approveContent, 1);
            break;
            
        case reject:
            // 拒绝后通知申请人
            String rejectTitle = "审批拒绝通知";
            String rejectContent = String.format("您的申请【%s】已被拒绝", entity.getTITLE());
            messageService.sentMessage(Arrays.asList(entity.getCREATORUSER()), 
                                     rejectTitle, rejectContent, 1);
            break;
    }
    
    super.afterTaskExec(dic, isNew, flowTask, flowModel, flowRecordEnum);
}

onFlowEnd - 流程结束时

时机:整个工作流程结束时(流程通过)
用途:最终的业务处理、状态更新、资源分配

java
@Override
public void onFlowEnd(Map<String, Object> dic, FlowTaskEntity flowTask, 
                     FlowModel flowModel, FlowRecordEnum flowRecordEnum) 
       throws BusinessException, DataException {
    
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // 注意:此方法只在流程最终通过时调用,无需判断状态
    
    // 更新最终状态
    entity.setSTATUS("APPROVED");
    entity.setAPPROVALTIME(DateUtil.getNowDate());
    this.updateById(entity);
    
    // 执行业务逻辑
    executeFinalBusiness(entity);
    
    // 发送最终通知
    String title = "流程完成通知";
    String content = String.format("您的申请【%s】已完成全部审批流程", entity.getTITLE());
    messageService.sentMessage(Arrays.asList(entity.getCREATORUSER()), title, content, 1);
    
    // 通知相关人员
    List<String> relatedUsers = getRelatedUsers(entity);
    if (!relatedUsers.isEmpty()) {
        String relatedTitle = "业务处理通知";
        String relatedContent = String.format("申请【%s】已审批完成,请及时处理", entity.getTITLE());
        messageService.sentMessage(relatedUsers, relatedTitle, relatedContent, 1);
    }
    
    // 记录完成日志
    log.info("流程{}已完成,业务ID:{}", flowTask.getFlowId(), entity.getID());
    
    super.onFlowEnd(dic, flowTask, flowModel, flowRecordEnum);
}

flowResurgence - 流程复活

时机:流程被复活时
用途:恢复流程状态、重置数据

java
@Override
public void flowResurgence(Map<String, Object> dic) 
       throws BusinessException, DataException {
    
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // 恢复到流程中状态
    entity.setSTATUS("PENDING");
    this.updateById(entity);
    
    // 发送复活通知
    String title = "流程恢复通知";
    String content = String.format("申请【%s】的流程已恢复", entity.getTITLE());
    messageService.sentMessage(Arrays.asList(entity.getCREATORUSER()), title, content, 1);
    
    log.info("流程已复活,业务ID:{}", entity.getID());
    
    super.flowResurgence(dic);
}

数据获取钩子

afterGetData - 获取数据后处理

时机:查询单条数据之后
用途:数据转换、敏感信息过滤、附加信息补充

java
@Override
public Map<String, Object> afterGetData(Map<String, Object> dic) {
    
    // ✅ 正确:转换为Entity对象进行类型安全操作
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // 添加扩展信息 - 只操作entity对象
    // 创建人姓名
    if (StringUtil.isNotEmpty(entity.getCREATORUSER())) {
        String creatorName = userService.getUserName(entity.getCREATORUSER());
        entity.setCREATORNAME(creatorName);
    }
    
    // 部门名称
    if (StringUtil.isNotEmpty(entity.getORGANIZE())) {
        String organizeName = organizeService.getOrganizeName(entity.getORGANIZE());
        entity.setORGANIZENAME(organizeName);
    }
    
    // 状态显示名称
    if (StringUtil.isNotEmpty(entity.getSTATUS())) {
        String statusName = getStatusDisplayName(entity.getSTATUS());
        entity.setSTATUSNAME(statusName);
    }
    
    // 格式化时间显示
    if (entity.getCREATORTIME() != null) {
        String createTimeFormat = DateUtil.dateFormat(entity.getCREATORTIME(), "yyyy-MM-dd HH:mm:ss");
        entity.setCREATETIMEFORMAT(createTimeFormat);
    }
    
    // 权限信息 - 只操作entity对象
    UserInfo user = userProvider.get();
    boolean canEdit = canEdit(entity, user);
    boolean canDelete = canDelete(entity, user);
    entity.setCANEDIT(canEdit);
    entity.setCANDELETE(canDelete);
    
    // ✅ 正确:最后统一转换回dic
    Map<String, Object> result = JsonUtil.entityToMap(entity);
    result.putAll(dic); // 保留原有字段
    
    // 过滤敏感信息(在最终结果上操作)
    if (!user.isAdmin()) {
        // 非管理员不显示某些敏感字段
        result.remove("SENSITIVEINFO");
    }
    
    return super.afterGetData(result);
}

❌ 错误示例 - 避免这样写:

java
@Override
public Map<String, Object> afterGetData(Map<String, Object> dic) {
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // ❌ 错误:同时操作entity和dic
    if (StringUtil.isNotEmpty(entity.getSTATUS())) {
        String statusName = getStatusDisplayName(entity.getSTATUS());
        entity.setSTATUSNAME(statusName);  // 操作entity
        dic.put("STATUSNAME", statusName); // 又操作dic,重复操作
    }
    
    // ❌ 错误:直接在dic上操作,没有经过entity
    dic.put("TIMERANGE", "2024-01-01 ~ 2024-01-02");
    
    return super.afterGetData(dic);
}

钩子方法最佳实践

1. 按需实现原则

java
// ✅ 正确:只实现需要的钩子方法
@Slf4j  // 必须添加日志注解
@Service
@UseDataSource(DbNameConst.JPWISE_DEMO)
public class BusinessServiceImpl extends BasicDemoServiceImpl<BusinessMapper, BusinessEntity> 
                                implements BusinessService {
    
    // 只有当用户明确需要保存前验证时才添加此方法
    @Override
    public Map<String, Object> beforeSave(Map<String, Object> dic, Boolean isNew) 
           throws BusinessException, DataException {
        
        // ✅ 正确:遵循Basic继承指南规范
        BusinessEntity entity = JsonUtil.getJsonToBean(dic, BusinessEntity.class);
        
        // 具体的业务验证逻辑
        if (isNew) {
            entity.setCODE(generateBusinessCode());
            entity.setSTATUS(getDefaultStatus());
        }
        
        validateBusinessRules(entity);
        
        // 最后统一转换回dic
        Map<String, Object> result = JsonUtil.entityToMap(entity);
        result.putAll(dic);
        
        return super.beforeSave(result, isNew);
    }
}

// ❌ 错误:预先生成所有钩子方法
public class BusinessServiceImpl extends BasicDemoServiceImpl<BusinessMapper, BusinessEntity> {
    // 不要预先添加空的钩子方法
    @Override
    public Map<String, Object> beforeSave(Map<String, Object> dic, Boolean isNew) {
        return super.beforeSave(dic, isNew); // 空实现没有意义
    }
    
    @Override
    public Map<String, Object> afterSave(Map<String, Object> dic, Boolean isNew) {
        return super.afterSave(dic, isNew); // 空实现没有意义
    }
    // ... 其他空钩子方法
}

2. 异常处理规范

java
// ✅ 正确:异常声明与基类一致
@Override
public Map<String, Object> beforeSave(Map<String, Object> dic, Boolean isNew) 
       throws BusinessException, DataException {
    
    // 使用项目定义的异常类型
    if (validationFails) {
        throw new BusinessException("业务验证失败");
    }
    
    if (dataFormatError) {
        throw new DataException("数据格式错误");
    }
    
    return super.beforeSave(dic, isNew);
}

// ❌ 错误:异常声明不一致
@Override
public Map<String, Object> beforeSave(Map<String, Object> dic, Boolean isNew) 
       throws Exception {  // 错误:与基类声明不一致
    return super.beforeSave(dic, isNew);
}

3. 调用父类方法

java
// ✅ 正确:必须调用父类方法
@Override
public Map<String, Object> beforeSave(Map<String, Object> dic, Boolean isNew) 
       throws BusinessException, DataException {
    
    // 自定义业务逻辑
    customBusinessLogic(dic);
    
    // 必须调用父类方法
    return super.beforeSave(dic, isNew);
}

// ❌ 错误:忘记调用父类方法
@Override
public Map<String, Object> beforeSave(Map<String, Object> dic, Boolean isNew) {
    customBusinessLogic(dic);
    return dic;  // 错误:没有调用super方法
}

4. 钩子方法组合使用

多个钩子方法可以组合使用,形成完整的业务流程处理链:

java
@Service
@UseDataSource(DbNameConst.JPWISE_DEMO)
public class BusinessServiceImpl extends BasicDemoServiceImpl<BusinessMapper, BusinessEntity> 
                              implements BusinessService {
    
    // 1. 保存前:数据验证和补全
    @Override
    public Map<String, Object> beforeSave(Map<String, Object> dic, Boolean isNew) {
        BusinessEntity entity = JsonUtil.getJsonToBean(dic, BusinessEntity.class);
        
        if (isNew) {
            entity.setCODE(generateBusinessCode());
            entity.setSTATUS(getDefaultStatus());
        }
        
        validateBusinessRules(entity);
        
        Map<String, Object> result = JsonUtil.entityToMap(entity);
        result.putAll(dic);
        return super.beforeSave(result, isNew);
    }
    
    // 2. 流程提交前:业务验证
    @Override
    public Map<String, Object> beforeTaskExec(Map<String, Object> dic, boolean isNew, 
                                             FlowTaskEntity flowTask, FlowModel flowModel, 
                                             FlowRecordEnum flowRecordEnum) {
        if (flowRecordEnum == submit) {
            BusinessEntity entity = JsonUtil.getJsonToBean(dic, BusinessEntity.class);
            validateForSubmit(entity);
            
            entity.setSTATUS("PENDING");
            Map<String, Object> result = JsonUtil.entityToMap(entity);
            result.putAll(dic);
            return super.beforeTaskExec(result, isNew, flowTask, flowModel, flowRecordEnum);
        }
        
        return super.beforeTaskExec(dic, isNew, flowTask, flowModel, flowRecordEnum);
    }
    
    // 3. 流程结束后:最终处理
    @Override
    public void onFlowEnd(Map<String, Object> dic, FlowTaskEntity flowTask, 
                         FlowModel flowModel, FlowRecordEnum flowRecordEnum) {
        BusinessEntity entity = JsonUtil.getJsonToBean(dic, BusinessEntity.class);
        
        // 发送通知、更新状态、触发后续业务
        sendNotification(entity);
        updateBusinessStatus(entity);
        
        super.onFlowEnd(dic, flowTask, flowModel, flowRecordEnum);
    }
}

📋 核心规范总结

钩子方法标准流程

java
@Override
public Map<String, Object> hookMethodName(Map<String, Object> dic, ...) {
    // 1. 转换为Entity对象
    MyEntity entity = JsonUtil.getJsonToBean(dic, MyEntity.class);
    
    // 2. 业务逻辑处理
    // 只操作entity对象
    
    // 3. 最后转换回dic并返回
    Map<String, Object> result = JsonUtil.entityToMap(entity);
    result.putAll(dic);
    
    return super.hookMethodName(result, ...);
}

⚠️ 关键要点

  • Entity优先 - 转换为Entity对象进行类型安全操作
  • 避免双重操作 - 不要同时修改entity和dic
  • 按需添加 - 只有明确需要时才添加钩子方法
  • 必须调用super - 确保框架逻辑正常执行

💡 详细指南: 完整编码规范请参考 Basic继承指南