scaffold-common -- 基础公共模块
模块概述
scaffold-common 是整个 Scaffold v2 平台的基石模块,所有其他模块均依赖于此模块。它提供了统一的响应格式、基础实体定义、全局异常处理、通用工具类、跨模块 SPI 接口等核心基础设施。引入 scaffold-common 后,通过 Spring Boot 自动配置机制即可开箱即用,无需手动注册 Bean。
功能列表
- 统一响应体
Result<T>与错误码枚举ResultCode - 基础实体
BaseEntity(雪花ID、审计字段、逻辑删除) - 分页请求/响应模型
PageQuery/PageResult<T> - 业务异常
BusinessException与全局异常处理器 - 操作日志注解
@OperLog - 对象拷贝工具
BeanCopyUtils、JSON 工具JsonUtils、Servlet 工具ServletUtils、链路追踪工具TraceUtils - Jackson 全局配置(Long 序列化为 String、日期时间格式化、灵活反序列化)
- MyBatis-Plus 全局配置(分页插件、乐观锁、防全表更新、自动填充)
- 链路追踪过滤器
TraceFilter - BPM 集成 SPI 接口
BpmIntegrationService - 财务集成 SPI 接口(
FinanceEvent、FinanceEventPublisher、FinanceSubjectMapper、FinanceVoucherGenerator)
核心组件
BaseEntity
所有业务实体的父类,位于 com.scaffold.common.core.entity.BaseEntity。
@TableName("your_table")
@SuperBuilder
@NoArgsConstructor
public class YourEntity extends BaseEntity {
// 仅定义业务字段即可
private String name;
}
继承后自动获得以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
id |
Long | 雪花算法主键(序列化为 String,防止 JS 精度丢失) |
createBy |
Long | 创建人ID(INSERT 时自动填充) |
createTime |
LocalDateTime | 创建时间(INSERT 时自动填充) |
updateBy |
Long | 更新人ID(INSERT/UPDATE 时自动填充) |
updateTime |
LocalDateTime | 更新时间(INSERT/UPDATE 时自动填充) |
deleted |
Integer | 逻辑删除标记(0 未删除,1 已删除) |
主键策略为 IdType.ASSIGN_ID(雪花算法),无需手动赋值。deleted 字段配合 @TableLogic 实现逻辑删除,查询时自动过滤已删除记录。
Result
统一响应体,位于 com.scaffold.common.core.model.Result。所有 Controller 方法均应返回 Result<T>。
// 成功(无数据)
return Result.success();
// 成功(带数据)
return Result.success(userVO);
// 成功(带消息和数据)
return Result.success("操作成功", userVO);
// 失败(默认系统错误)
return Result.fail();
// 失败(自定义消息)
return Result.fail("用户名已存在");
// 失败(指定错误码枚举)
return Result.fail(ResultCode.DATA_NOT_FOUND);
// 失败(指定码和消息)
return Result.fail(3001, "订单不存在");
响应格式:
{
"code": 0,
"message": "操作成功",
"data": {},
"traceId": "18f3a2b0c1d4e5f6a7b8c9d0",
"timestamp": 1703275200000
}
其中 code === 0 表示成功,前端 request.ts 已做对应处理。
ResultCode
错误码枚举,位于 com.scaffold.common.core.model.ResultCode。
PageQuery / PageResult
分页模型,用于所有列表查询接口。
PageQuery(请求参数):
@Data
public class OrderPageQuery extends PageQuery {
private String orderNo; // 业务查询条件
private Integer status;
}
| 字段 | 类型 | 默认值 | 校验 |
|---|---|---|---|
pageNum |
Long | 1 | 最小值 1 |
pageSize |
Long | 10 | 范围 1~500 |
提供 getOffset() 方法可直接用于 MyBatis-Plus 分页查询。
PageResult(响应结果):
// 构建
PageResult<OrderVO> page = PageResult.of(total, records, pageNum, pageSize);
// 空结果
PageResult<OrderVO> empty = PageResult.empty();
| 字段 | 类型 | 说明 |
|---|---|---|
total |
Long | 总记录数 |
records |
List<T> | 当前页数据列表 |
pageNum |
Long | 当前页码 |
pageSize |
Long | 每页条数 |
totalPages |
Long | 总页数 |
hasNext |
Boolean | 是否有下一页 |
hasPrevious |
Boolean | 是否有上一页 |
BusinessException
业务异常,位于 com.scaffold.common.exception.BusinessException。在 Service 层抛出此异常,GlobalExceptionHandler 会统一捕获并转换为 Result.fail() 响应。
// 简单用法
throw new BusinessException("订单状态不允许此操作");
// 使用 ResultCode
throw new BusinessException(ResultCode.DATA_NOT_FOUND);
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "订单 123 不存在");
// 自定义错误码
throw new BusinessException(3005, "库存不足");
// 携带原始异常
throw new BusinessException(ResultCode.THIRD_PARTY_ERROR, "支付调用失败", e);
注解使用
@OperLog
操作日志注解,标注在 Controller 方法上,由审计模块自动采集记录。
@PostMapping("/add")
@OperLog(module = "用户管理", type = 1)
public Result<Void> addUser(@RequestBody UserAddRequest request) { ... }
@PostMapping("/update")
@OperLog(module = "用户管理", type = 2, recordResult = true)
public Result<Void> updateUser(@RequestBody UserUpdateRequest request) { ... }
@PostMapping("/delete")
@OperLog(module = "用户管理", type = 3, recordParams = false)
public Result<Void> deleteUser(@RequestBody Long id) { ... }
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
module |
String | "" | 操作模块名称 |
type |
int | 0 | 操作类型:0-其他,1-新增,2-修改,3-删除 |
recordParams |
boolean | true | 是否记录请求参数 |
recordResult |
boolean | false | 是否记录响应体 |
工具类
BeanCopyUtils
基于 Spring BeanUtils 封装的对象属性拷贝工具。
// Entity 转 VO(创建新对象)
UserVO vo = BeanCopyUtils.copy(userEntity, UserVO.class);
// 属性拷贝到已有对象
BeanCopyUtils.copy(updateRequest, existingEntity);
// 忽略 null 值拷贝(适合部分更新场景)
BeanCopyUtils.copyIgnoreNull(updateRequest, existingEntity);
// 列表拷贝
List<UserVO> voList = BeanCopyUtils.copyList(userEntities, UserVO.class);
JsonUtils
JSON 序列化/反序列化工具。
// 对象转 JSON
String json = JsonUtils.toJson(obj);
// JSON 转对象
UserVO vo = JsonUtils.parse(json, UserVO.class);
// JSON 转 Map
Map<String, Object> map = JsonUtils.parseMap(json);
// JSON 转 List
List<UserVO> list = JsonUtils.parseList(json, UserVO.class);
// JSON 转复杂泛型
List<Map<String, Object>> result = JsonUtils.parse(json, new TypeReference<>() {});
ServletUtils
Servlet 请求工具类。
// 获取当前请求对象
HttpServletRequest request = ServletUtils.getRequest();
// 获取客户端真实 IP(支持代理)
String ip = ServletUtils.getClientIp(request);
// 获取浏览器信息
String browser = ServletUtils.getBrowser(request);
// 获取操作系统
String os = ServletUtils.getOs(request);
TraceUtils
基于 SLF4J MDC 的链路追踪工具,配合 TraceFilter 使用。每个请求自动生成 traceId 并放入 MDC,日志输出时可通过 %X{traceId} 引用。
// 获取当前 traceId
String traceId = TraceUtils.getTraceId();
// 手动设置 traceId(一般不需要,TraceFilter 已自动处理)
TraceUtils.setTraceId("custom-trace-id");
// 清除 traceId
TraceUtils.removeTraceId();
跨模块 SPI 接口
BpmIntegrationService
BPM 审批流程集成接口。由 scaffold-bpm 模块提供真实实现,BPM 模块不存在时自动降级为 NopBpmIntegrationService(空操作)。
@Autowired
private BpmIntegrationService bpmIntegrationService;
public void submitOrder(Order order) {
if (bpmIntegrationService.isBpmAvailable()) {
bpmIntegrationService.startProcess(
"order_approval", // 流程定义编码
"订单审批 - " + order.getOrderNo(),
order.getId().toString(), // businessKey
"order", // businessType
"/order/detail/" + order.getId(), // businessUrl
"/api/v1/order/callback", // callbackUrl
userId, userName,
Map.of("amount", order.getAmount())
);
}
}
财务 SPI
业务模块通过财务 SPI 与 scaffold-finance 模块解耦集成。由 scaffold-finance 提供真实实现,模块不存在时使用空操作。
FinanceEvent -- 财务事件接口,业务模块构造事件对象:
FinanceEvent event = new FinanceEvent() {
@Override public String getSourceType() { return "pay_order"; }
@Override public Long getSourceId() { return orderId; }
@Override public FinanceEventType getEventType() { return FinanceEventType.INCOME; }
@Override public Long getAmount() { return 10000L; } // 单位:分
@Override public String getSummary() { return "订单收款"; }
@Override public LocalDateTime getEventTime() { return LocalDateTime.now(); }
};
FinanceEventPublisher -- 事件发布器:
@Autowired
private FinanceEventPublisher publisher;
publisher.publish(event);
publisher.publishBatch(events);
FinanceSubjectMapper -- 科目映射 SPI(可选实现):
@Component
public class PayOrderSubjectMapper implements FinanceSubjectMapper {
@Override
public String supportsSourceType() { return "pay_order"; }
@Override
public Map<String, String> mapSubjects(FinanceEvent event) {
return Map.of("debit", "1002", "credit", "6001");
}
}
FinanceVoucherGenerator -- 凭证分录生成 SPI(可选实现):
@Component
public class PayOrderVoucherGenerator implements FinanceVoucherGenerator {
@Override
public String supportsSourceType() { return "pay_order"; }
@Override
public List<VoucherEntryDTO> generateEntries(FinanceEvent event) {
return List.of(
VoucherEntryDTO.builder()
.subjectCode("1002").subjectName("银行存款")
.debit(event.getAmount()).credit(0L)
.summary(event.getSummary()).build(),
VoucherEntryDTO.builder()
.subjectCode("6001").subjectName("主营业务收入")
.debit(0L).credit(event.getAmount())
.summary(event.getSummary()).build()
);
}
}
自动配置说明
本模块通过 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 注册以下自动配置类:
| 配置类 | 说明 |
|---|---|
CommonAutoConfiguration |
Jackson 日期格式化、链路追踪过滤器注册 |
JacksonConfig |
Long 序列化为 String、灵活的 LocalDateTime 反序列化(同时支持 ISO 和空格分隔格式) |
MyBatisPlusConfig |
MyBatis-Plus 拦截器链(分页、乐观锁、防全表更新删除)、全局配置(雪花ID、逻辑删除、审计字段填充) |
GlobalExceptionHandler |
全局异常处理器,统一返回 Result 格式 |
异常码规范
| 范围 | 分类 | 示例 |
|---|---|---|
| 0 | 成功 | SUCCESS(0, "操作成功") |
| 1000~1999 | 系统错误 | SYSTEM_ERROR(1000), PARAM_ERROR(1001), METHOD_NOT_SUPPORTED(1002), MEDIA_TYPE_NOT_SUPPORTED(1003), SERVICE_UNAVAILABLE(1004) |
| 2000~2999 | 认证授权 | UNAUTHORIZED(2000), TOKEN_EXPIRED(2001), FORBIDDEN(2002), ACCOUNT_PASSWORD_ERROR(2003), ACCOUNT_LOCKED(2004), TOKEN_INVALID(2005) |
| 3000~3999 | 业务错误 | BUSINESS_ERROR(3000), DATA_NOT_FOUND(3001), DATA_EXISTS(3002), DATA_DELETED(3003), OPERATION_FAILED(3004) |
| 4000~4999 | 第三方服务 | THIRD_PARTY_ERROR(4000), FILE_UPLOAD_FAILED(4001), SMS_SEND_FAILED(4002), EMAIL_SEND_FAILED(4003) |
全局异常处理
GlobalExceptionHandler 自动处理以下异常类型,无需在 Controller/Service 中手动捕获:
| 异常类型 | 错误码 | 说明 |
|---|---|---|
BusinessException |
自定义 | 业务异常,直接使用异常中的 code 和 message |
MethodArgumentNotValidException |
1001 | @Valid 校验失败,拼接所有字段错误 |
BindException |
1001 | 参数绑定失败 |
ConstraintViolationException |
1001 | @Validated 校验失败 |
MissingServletRequestParameterException |
1001 | 缺少必要请求参数 |
MethodArgumentTypeMismatchException |
1001 | 参数类型不匹配 |
HttpMessageNotReadableException |
1001 | 请求体格式错误 |
HttpRequestMethodNotSupportedException |
1002 | 请求方式不支持 |
NoHandlerFoundException |
3001 | 请求路径不存在 |
Exception |
1000 | 兜底处理,记录 ERROR 日志 |
常量
CommonConstant 定义了系统常用常量:
布尔语义统一规范
核心规则:1 = 正向值,0 = 负向值
适用于项目中所有 status、visible、enabled、is_xxx 类型的 Integer 字段:
| 正向值 (1) | 负向值 (0) | 语义 |
|---|---|---|
| 正常 | 停用 | status 字段 |
| 显示 | 隐藏 | visible 字段 |
| 启用 | 禁用 | enabled 字段 |
| 成功 | 失败 | 日志 status |
| 是 | 否 | is_xxx 字段 |
注意:多态状态机(如任务状态、发送状态等)不遵循此规范,使用独立的枚举值定义。
常量一览
| 常量 | 值 | 说明 |
|---|---|---|
YES |
1 | 布尔正向值(是/正常/启用) |
NO |
0 | 布尔负向值(否/异常/禁用) |
STATUS_NORMAL |
1 | 正常状态 |
STATUS_DISABLED |
0 | 停用状态 |
VISIBLE_YES |
1 | 显示 |
VISIBLE_NO |
0 | 隐藏 |
ENABLED_YES |
1 | 启用 |
ENABLED_NO |
0 | 禁用 |
DELETED_NO |
0 | 未删除 |
DELETED_YES |
1 | 已删除 |
DEFAULT_PAGE_SIZE |
10 | 默认每页条数 |
MAX_PAGE_SIZE |
500 | 最大每页条数 |
DATE_FORMAT_DATETIME |
yyyy-MM-dd HH:mm:ss | 日期时间格式 |
DATE_FORMAT_DATE |
yyyy-MM-dd | 日期格式 |
DATE_FORMAT_TIME |
HH:mm:ss | 时间格式 |