29 KiB
设计文档 - 日志记录功能
元数据
- 需求编号:002-06
- 创建时间:2026-02-03
- 创建人:AI Assistant
- 状态:进行中
设计概述
本设计文档描述 Salesforce Apex 日志记录功能的实现方案。日志记录功能提供日志信息获取、调试头部设置、日志查询、日志详情查询、执行匿名代码并获取日志等功能。本功能基于 Salesforce Apex API 的日志相关接口,使用 apex.jar 中的 LogInfo、LogType、LogCategory、LogCategoryLevel 等类,通过 ApexConnectionFactory 获取 SoapConnection 实例进行日志操作,并将日志信息存储到数据库中。
架构设计
系统架构
┌─────────────────────────────────────────────────────────────┐
│ 前端应用层 (Vue 3) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Controller 层 │
│ ApexLogController.java │
│ - 获取日志信息 │
│ - 设置调试头部 │
│ - 清除调试头部 │
│ - 查询日志列表 │
│ - 获取日志详情 │
│ - 执行匿名代码并获取日志 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Service 层 │
│ IApexLogService.java │
│ ApexLogServiceImpl.java │
│ - 获取日志信息 │
│ - 设置调试头部 │
│ - 清除调试头部 │
│ - 查询日志列表 │
│ - 获取日志详情 │
│ - 执行匿名代码并获取日志 │
│ - 数据转换和格式化 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Mapper 层 │
│ DataiApexLogMapper.java │
│ DataiApexLogCategoryMapper.java │
│ - 数据库访问 │
│ - SQL 映射 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Entity 层 │
│ DataiApexLog.java │
│ DataiApexLogCategory.java │
│ - 数据模型 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 数据库层 │
│ datai_apex_log 表 │
│ datai_apex_log_category 表 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Salesforce Apex API │
│ ApexConnectionFactory │
│ SoapConnection │
│ LogInfo, LogType, LogCategory, LogCategoryLevel │
└─────────────────────────────────────────────────────────────┘
模块架构
模块:datai-salesforce-apex
包结构:
com.datai.apex
├── controller
│ └── ApexLogController.java # 日志控制器
├── service
│ ├── IApexLogService.java # 日志服务接口
│ └── impl
│ └── ApexLogServiceImpl.java # 日志服务实现
├── mapper
│ ├── DataiApexLogMapper.java # 日志 Mapper
│ └── DataiApexLogCategoryMapper.java # 日志分类 Mapper
├── domain
│ ├── DataiApexLog.java # 日志实体
│ └── DataiApexLogCategory.java # 日志分类实体
└── model
├── dto
│ ├── SetDebuggingHeaderDto.java # 设置调试头部请求 DTO
│ ├── QueryLogsDto.java # 查询日志列表请求 DTO
│ ├── ExecuteAndLogDto.java # 执行匿名代码并获取日志请求 DTO
│ └── LogCategoryDto.java # 日志分类 DTO
└── vo
├── LogInfoVo.java # 日志信息 VO
├── LogDetailVo.java # 日志详情 VO
├── LogCategoryVo.java # 日志分类 VO
└── ExecuteAndLogResultVo.java # 执行并获取日志结果 VO
数据流
1. 获取日志信息流程
客户端发送 GET /api/apex/log?logId=xxx
↓
Controller 接收请求,参数校验
↓
Controller 调用 Service.getLog(logId)
↓
Service 通过 ApexConnectionFactory 获取 SoapConnection
↓
Service 调用 SoapConnection.getLog(logId)
↓
Service 将 LogInfo 转换为 LogInfoVo
↓
Service 返回结果给 Controller
↓
Controller 封装统一响应格式返回给客户端
2. 设置调试头部流程
客户端发送 POST /api/apex/log/debug-header
↓
Controller 接收请求,参数校验
↓
Controller 调用 Service.setDebuggingHeader(logCategories, logType)
↓
Service 通过 ApexConnectionFactory 获取 SoapConnection
↓
Service 调用 SoapConnection.setDebuggingHeader(logCategories, logType)
↓
Service 返回成功结果给 Controller
↓
Controller 封装统一响应格式返回给客户端
3. 查询日志列表流程
客户端发送 GET /api/apex/log/list?startTime=xxx&endTime=xxx&page=1&pageSize=20
↓
Controller 接收请求,参数校验
↓
Controller 调用 Service.queryLogs(startTime, endTime, page, pageSize)
↓
Service 通过 Mapper 查询数据库
↓
Service 分页处理
↓
Service 返回结果给 Controller
↓
Controller 封装统一响应格式返回给客户端
4. 执行匿名代码并获取日志流程
客户端发送 POST /api/apex/log/execute-and-log
↓
Controller 接收请求,参数校验
↓
Controller 调用 Service.executeAndLog(code, logCategories, logType)
↓
Service 设置调试头部
↓
Service 通过 ApexConnectionFactory 获取 SoapConnection
↓
Service 调用 SoapConnection.executeAnonymous(code)
↓
Service 获取执行结果和日志 ID
↓
Service 调用 SoapConnection.getLog(apexLogId)
↓
Service 保存日志信息到数据库
↓
Service 返回结果给 Controller
↓
Controller 封装统一响应格式返回给客户端
技术方案
技术选型
| 技术组件 | 版本要求 | 选择理由 |
|---|---|---|
| Spring Boot | 2.7.x | 项目标准版本,提供完整的 Web 开发支持 |
| Spring Security | 2.7.x | 提供认证和授权支持,与若依框架集成 |
| MyBatis Plus | 3.5.x | 提供便捷的数据库操作,支持代码生成 |
| MySQL | 8.0+ | 项目标准数据库,支持事务和索引 |
| Swagger | 3.0 | 提供接口文档,方便前端对接 |
| Lombok | 1.18.x | 简化实体类代码 |
| apex.jar | Salesforce 提供的 Apex API JAR 包 | 提供日志相关 API |
集成方案
1. 与 Salesforce Apex API 集成
连接获取:
- 使用
ApexConnectionFactory获取SoapConnection实例 - 固定使用
sourceorg 类型 - 连接管理由
SessionManager负责
日志 API 调用:
SoapConnection.getLog(logId)- 获取日志信息SoapConnection.setDebuggingHeader(logCategories, logType)- 设置调试头部SoapConnection.clearDebuggingHeader()- 清除调试头部SoapConnection.executeAnonymous(code)- 执行匿名代码
数据类型映射:
LogInfo→LogInfoVoLogType→ 枚举或字符串LogCategory→ 枚举或字符串LogCategoryLevel→ 枚举或字符串
2. 与若依框架集成
权限控制:
- 使用
@PreAuthorize注解进行权限控制 - 权限标识:
apex:log:query、apex:log:manage等
统一响应:
- 使用
AjaxResult封装统一响应格式 - 成功响应:
AjaxResult.success(data) - 失败响应:
AjaxResult.error(message)
分页支持:
- 使用
PageUtils进行分页处理 - 使用
TableDataInfo封装分页结果
异常处理:
- 使用
SalesforceAuthException处理连接异常 - 使用
SalesforceOperationException处理操作异常 - 使用
ServiceException处理业务异常
3. 多线程安全设计
并发控制:
- 使用
ReentrantLock保证多线程安全 - 锁定范围:调试头部设置和清除操作
- 锁的粒度:方法级别
实现示例:
private final ReentrantLock debugHeaderLock = new ReentrantLock();
public void setDebuggingHeader(LogInfo[] logCategories, LogType logType) {
debugHeaderLock.lock();
try {
SoapConnection connection = apexConnectionFactory.getConnection("source");
connection.setDebuggingHeader(logCategories, logType);
} finally {
debugHeaderLock.unlock();
}
}
数据模型
数据库表设计
1. datai_apex_log(Apex 日志表)
表名:datai_apex_log
字段说明:
| 字段名 | 类型 | 长度 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | - | 是 | AUTO_INCREMENT | 主键 ID |
| log_id | VARCHAR | 50 | 是 | - | 日志 ID(Salesforce 日志 ID) |
| application | VARCHAR | 255 | 否 | - | 应用程序 |
| duration_milliseconds | INT | - | 否 | 0 | 持续时间(毫秒) |
| location | VARCHAR | 500 | 否 | - | 位置 |
| log_length | INT | - | 否 | 0 | 日志长度 |
| operation | VARCHAR | 255 | 否 | - | 操作 |
| start_time | DATETIME | - | 否 | - | 开始时间 |
| status | VARCHAR | 50 | 否 | - | 状态 |
| user_id | VARCHAR | 50 | 否 | - | 用户 ID |
| dept_id | BIGINT | - | 否 | - | 部门 ID |
| create_by | VARCHAR | 64 | 是 | - | 创建人 |
| create_time | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 |
| update_by | VARCHAR | 64 | 是 | - | 更新人 |
| update_time | DATETIME | - | 是 | CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
| del_flag | TINYINT | 1 | 否 | 0 | 删除标志(0 正常 1 删除) |
索引设计:
- 主键索引:
PRIMARY KEY (id) - 唯一索引:
UNIQUE KEY uk_log_id (log_id) - 普通索引:
idx_user_id (user_id) - 普通索引:
idx_dept_id (dept_id) - 普通索引:
idx_start_time (start_time) - 普通索引:
idx_create_time (create_time) - 普通索引:
idx_application (application) - 普通索引:
idx_status (status)
2. datai_apex_log_category(Apex 日志分类表)
表名:datai_apex_log_category
字段说明:
| 字段名 | 类型 | 长度 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | - | 是 | AUTO_INCREMENT | 主键 ID |
| log_id | BIGINT | - | 是 | - | 日志 ID(外键,关联 datai_apex_log.id) |
| category | VARCHAR | 50 | 是 | - | 分类(DB、APEX_CODE、VALIDATION、WORKFLOW、CALLOUT、SYSTEM 等) |
| level | VARCHAR | 20 | 是 | - | 级别(None、Fine、Finer、Finest、Debug、Info、Warn、Error) |
| create_time | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 |
索引设计:
- 主键索引:
PRIMARY KEY (id) - 普通索引:
idx_log_id (log_id) - 普通索引:
idx_category (category) - 普通索引:
idx_level (level) - 外键约束:
CONSTRAINT fk_log_category_log FOREIGN KEY (log_id) REFERENCES datai_apex_log (id) ON DELETE CASCADE
表关系:
datai_apex_log与datai_apex_log_category是一对多关系- 一个日志可以包含多个日志分类
- 删除日志时,级联删除对应的日志分类
实体类设计
1. DataiApexLog(Apex 日志实体)
类路径:com.datai.apex.domain.DataiApexLog
类定义:
package com.datai.apex.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.datai.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("datai_apex_log")
public class DataiApexLog extends BaseEntity {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("log_id")
private String logId;
@TableField("application")
private String application;
@TableField("duration_milliseconds")
private Integer durationMilliseconds;
@TableField("location")
private String location;
@TableField("log_length")
private Integer logLength;
@TableField("operation")
private String operation;
@TableField("start_time")
private Date startTime;
@TableField("status")
private String status;
@TableField("user_id")
private String userId;
@TableField("dept_id")
private Long deptId;
@TableLogic
@TableField("del_flag")
private Integer delFlag;
}
2. DataiApexLogCategory(Apex 日志分类实体)
类路径:com.datai.apex.domain.DataiApexLogCategory
类定义:
package com.datai.apex.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
@Data
@TableName("datai_apex_log_category")
public class DataiApexLogCategory {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("log_id")
private Long logId;
@TableField("category")
private String category;
@TableField("level")
private String level;
@TableField(value = "create_time", fill = FieldFill.INSERT)
private Date createTime;
}
数据字典设计
1. 日志分类字典
字典类型:apex_log_category
字典数据:
| 字典标签 | 字典值 | 排序 | 状态 |
|---|---|---|---|
| 数据库 | DB | 1 | 正常 |
| Apex 代码 | APEX_CODE | 2 | 正常 |
| 验证规则 | VALIDATION | 3 | 正常 |
| 工作流 | WORKFLOW | 4 | 正常 |
| 外部调用 | CALLOUT | 5 | 正常 |
| 系统 | SYSTEM | 6 | 正常 |
2. 日志级别字典
字典类型:apex_log_level
字典数据:
| 字典标签 | 字典值 | 排序 | 状态 |
|---|---|---|---|
| 无 | None | 1 | 正常 |
| 细粒度 | Fine | 2 | 正常 |
| 更细粒度 | Finer | 3 | 正常 |
| 最细粒度 | Finest | 4 | 正常 |
| 调试 | Debug | 5 | 正常 |
| 信息 | Info | 6 | 正常 |
| 警告 | Warn | 7 | 正常 |
| 错误 | Error | 8 | 正常 |
3. 日志类型字典
字典类型:apex_log_type
字典数据:
| 字典标签 | 字典值 | 排序 | 状态 |
|---|---|---|---|
| Apex 代码 | ApexCode | 1 | 正常 |
| Apex 性能分析 | ApexProfiling | 2 | 正常 |
| 外部调用 | Callout | 3 | 正常 |
| 数据库 | Database | 4 | 正常 |
| 系统 | System | 5 | 正常 |
接口设计
RESTful API 设计
1. 获取日志信息
接口:GET /api/apex/log
功能:获取指定的日志信息
请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| logId | String | 是 | 日志 ID |
响应示例:
{
"code": 200,
"message": "获取日志成功",
"data": {
"id": "07Lxx0000003DHb2AAG",
"application": "ApexCode",
"durationMilliseconds": 1234,
"location": "AccountTrigger",
"logLength": 1024,
"operation": "CREATE",
"startTime": "2026-02-03T10:00:00.000Z",
"status": "Completed",
"success": true
}
}
权限:@PreAuthorize("@ss.hasPermi('apex:log:query')")
2. 设置调试头部
接口:POST /api/apex/log/debug-header
功能:设置调试头部信息,用于收集详细的调试日志
请求参数:
{
"logCategories": [
{
"category": "DB",
"level": "FINE"
},
{
"category": "APEX_CODE",
"level": "DEBUG"
},
{
"category": "VALIDATION",
"level": "INFO"
},
{
"category": "WORKFLOW",
"level": "WARN"
}
],
"logType": "PROFILING"
}
响应示例:
{
"code": 200,
"message": "设置调试头部成功",
"data": {
"success": true
}
}
权限:@PreAuthorize("@ss.hasPermi('apex:log:manage')")
3. 清除调试头部
接口:DELETE /api/apex/log/debug-header
功能:清除调试头部信息
请求参数:无
响应示例:
{
"code": 200,
"message": "清除调试头部成功",
"data": {
"success": true
}
}
权限:@PreAuthorize("@ss.hasPermi('apex:log:manage')")
4. 查询日志列表
接口:GET /api/apex/log/list
功能:查询日志列表
请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| startTime | String | 否 | 开始时间(yyyy-MM-dd HH:mm:ss) |
| endTime | String | 否 | 结束时间(yyyy-MM-dd HH:mm:ss) |
| application | String | 否 | 应用程序 |
| status | String | 否 | 状态 |
| page | Integer | 否 | 页码,默认为 1 |
| pageSize | Integer | 否 | 每页大小,默认为 20 |
响应示例:
{
"code": 200,
"message": "查询成功",
"data": {
"total": 50,
"page": 1,
"pageSize": 20,
"records": [
{
"id": 1,
"logId": "07Lxx0000003DHb2AAG",
"application": "ApexCode",
"durationMilliseconds": 1234,
"location": "AccountTrigger",
"logLength": 1024,
"operation": "CREATE",
"startTime": "2026-02-03 10:00:00",
"status": "Completed",
"userId": "005xx0000001Gw2EAS",
"createTime": "2026-02-03 10:00:00"
}
]
}
}
权限:@PreAuthorize("@ss.hasPermi('apex:log:query')")
5. 获取日志详情
接口:GET /api/apex/log/detail
功能:获取日志详情(包括日志分类信息)
请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| logId | String | 是 | 日志 ID |
响应示例:
{
"code": 200,
"message": "查询成功",
"data": {
"id": 1,
"logId": "07Lxx0000003DHb2AAG",
"application": "ApexCode",
"durationMilliseconds": 1234,
"location": "AccountTrigger",
"logLength": 1024,
"operation": "CREATE",
"startTime": "2026-02-03 10:00:00",
"status": "Completed",
"userId": "005xx0000001Gw2EAS",
"createTime": "2026-02-03 10:00:00",
"categories": [
{
"id": 1,
"logId": 1,
"category": "DB",
"level": "FINE",
"createTime": "2026-02-03 10:00:00"
},
{
"id": 2,
"logId": 1,
"category": "APEX_CODE",
"level": "DEBUG",
"createTime": "2026-02-03 10:00:00"
}
]
}
}
权限:@PreAuthorize("@ss.hasPermi('apex:log:query')")
6. 执行匿名代码并获取日志
接口:POST /api/apex/log/execute-and-log
功能:执行匿名 Apex 代码并获取执行日志
请求参数:
{
"code": "System.debug('Hello World');",
"logCategories": [
{
"category": "DB",
"level": "FINE"
},
{
"category": "APEX_CODE",
"level": "DEBUG"
}
],
"logType": "PROFILING"
}
响应示例:
{
"code": 200,
"message": "执行成功",
"data": {
"success": true,
"compiled": true,
"compileProblem": null,
"exceptionMessage": null,
"exceptionStackTrace": null,
"line": 0,
"column": 0,
"apexLogId": "07Lxx0000003DHb2AAG",
"logInfo": {
"id": "07Lxx0000003DHb2AAG",
"application": "ApexCode",
"durationMilliseconds": 1234,
"location": "executeAnonymous",
"logLength": 1024,
"operation": "EXECUTE",
"startTime": "2026-02-03T10:00:00.000Z",
"status": "Completed"
}
}
}
权限:@PreAuthorize("@ss.hasPermi('apex:log:execute')")
接口权限设计
| 接口 | 权限标识 | 说明 |
|---|---|---|
| GET /api/apex/log | apex:log:query | 查询日志信息 |
| POST /api/apex/log/debug-header | apex:log:manage | 管理调试头部 |
| DELETE /api/apex/log/debug-header | apex:log:manage | 管理调试头部 |
| GET /api/apex/log/list | apex:log:query | 查询日志列表 |
| GET /api/apex/log/detail | apex:log:query | 查询日志详情 |
| POST /api/apex/log/execute-and-log | apex:log:execute | 执行匿名代码并获取日志 |
实现要点
关键实现逻辑
1. 获取日志信息
实现步骤:
- 校验 logId 参数是否为空
- 通过 ApexConnectionFactory 获取 SoapConnection 实例
- 调用 SoapConnection.getLog(logId) 获取日志信息
- 将 LogInfo 转换为 LogInfoVo
- 保存日志信息到数据库(如果不存在)
- 返回日志信息
代码示例:
public LogInfoVo getLog(String logId) {
if (StringUtils.isEmpty(logId)) {
throw new ServiceException("日志 ID 不能为空");
}
try {
SoapConnection connection = apexConnectionFactory.getConnection("source");
LogInfo logInfo = connection.getLog(logId);
if (logInfo == null) {
throw new ServiceException("日志不存在");
}
LogInfoVo vo = convertToLogInfoVo(logInfo);
saveLogToDatabase(vo);
return vo;
} catch (ConnectionException e) {
throw new SalesforceAuthException("连接失败: " + e.getMessage());
} catch (Exception e) {
throw new SalesforceOperationException("获取日志失败: " + e.getMessage());
}
}
2. 设置调试头部
实现步骤:
- 校验 logCategories 和 logType 参数
- 使用 ReentrantLock 保证多线程安全
- 将 LogCategoryDto 转换为 LogInfo[]
- 通过 ApexConnectionFactory 获取 SoapConnection 实例
- 调用 SoapConnection.setDebuggingHeader(logCategories, logType)
- 释放锁
代码示例:
public void setDebuggingHeader(List<LogCategoryDto> logCategories, String logType) {
if (CollectionUtils.isEmpty(logCategories)) {
throw new ServiceException("日志分类不能为空");
}
debugHeaderLock.lock();
try {
LogInfo[] categories = convertToLogInfoArray(logCategories);
LogType type = LogType.valueOf(logType);
SoapConnection connection = apexConnectionFactory.getConnection("source");
connection.setDebuggingHeader(categories, type);
} finally {
debugHeaderLock.unlock();
}
}
3. 执行匿名代码并获取日志
实现步骤:
- 校验 code 参数
- 如果提供了 logCategories,先设置调试头部
- 通过 ApexConnectionFactory 获取 SoapConnection 实例
- 调用 SoapConnection.executeAnonymous(code) 执行代码
- 获取执行结果和 apexLogId
- 如果 apexLogId 不为空,调用 getLog(apexLogId) 获取日志信息
- 保存日志信息到数据库
- 返回执行结果和日志信息
代码示例:
public ExecuteAndLogResultVo executeAndLog(String code, List<LogCategoryDto> logCategories, String logType) {
if (StringUtils.isEmpty(code)) {
throw new ServiceException("代码不能为空");
}
try {
if (CollectionUtils.isNotEmpty(logCategories)) {
setDebuggingHeader(logCategories, logType);
}
SoapConnection connection = apexConnectionFactory.getConnection("source");
ExecuteAnonymousResult result = connection.executeAnonymous(code);
ExecuteAndLogResultVo vo = convertToExecuteAndLogResultVo(result);
if (StringUtils.isNotEmpty(result.getApexLogId())) {
LogInfoVo logInfo = getLog(result.getApexLogId());
vo.setLogInfo(logInfo);
}
return vo;
} catch (ConnectionException e) {
throw new SalesforceAuthException("连接失败: " + e.getMessage());
} catch (Exception e) {
throw new SalesforceOperationException("执行代码失败: " + e.getMessage());
}
}
异常处理设计
1. 异常类型
| 异常类型 | 使用场景 | HTTP 状态码 |
|---|---|---|
| SalesforceAuthException | 连接失败、认证失败 | 401 |
| SalesforceOperationException | API 调用失败 | 500 |
| ServiceException | 业务逻辑异常 | 400 |
| IllegalArgumentException | 参数校验失败 | 400 |
2. 异常处理示例
try {
SoapConnection connection = apexConnectionFactory.getConnection("source");
LogInfo logInfo = connection.getLog(logId);
return convertToLogInfoVo(logInfo);
} catch (ConnectionException e) {
log.error("连接失败: {}", e.getMessage(), e);
throw new SalesforceAuthException("APEX_LOG_004", "连接失败: " + e.getMessage());
} catch (Exception e) {
log.error("获取日志失败: {}", e.getMessage(), e);
throw new SalesforceOperationException("APEX_LOG_005", "获取日志失败: " + e.getMessage());
}
性能优化设计
1. 数据库查询优化
- 使用索引优化查询性能
- 分页查询避免全表扫描
- 使用 MyBatis Plus 的分页插件
2. 缓存策略
- 日志信息可以缓存到 Redis
- 缓存 key:
apex:log:{logId} - 缓存过期时间:30 分钟
3. 批量操作优化
- 批量保存日志分类信息
- 使用 MyBatis Plus 的批量插入功能
安全设计
1. 参数校验
- 使用
@Valid注解进行参数校验 - 使用
@NotBlank、@NotNull等注解校验必填字段 - 自定义校验器校验日志分类和级别
2. 权限控制
- 使用
@PreAuthorize注解进行权限控制 - 权限标识:
apex:log:query、apex:log:manage、apex:log:execute - 数据权限:根据部门 ID 过滤日志数据
3. SQL 注入防护
- 使用 MyBatis Plus 的参数化查询
- 禁止使用字符串拼接 SQL
- 使用 MyBatis Plus 的条件构造器
4. XSS 攻击防护
- 对用户输入进行转义
- 使用 Spring Security 的 XSS 防护功能
- 对日志内容进行过滤