datai/datai-scenes/datai-scene-salesforce/docs/design/2026-02-03-002-06-日志记录-设计.md

29 KiB
Raw Permalink Blame History

设计文档 - 日志记录功能

元数据

  • 需求编号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 实例
  • 固定使用 source org 类型
  • 连接管理由 SessionManager 负责

日志 API 调用

  • SoapConnection.getLog(logId) - 获取日志信息
  • SoapConnection.setDebuggingHeader(logCategories, logType) - 设置调试头部
  • SoapConnection.clearDebuggingHeader() - 清除调试头部
  • SoapConnection.executeAnonymous(code) - 执行匿名代码

数据类型映射

  • LogInfoLogInfoVo
  • LogType → 枚举或字符串
  • LogCategory → 枚举或字符串
  • LogCategoryLevel → 枚举或字符串

2. 与若依框架集成

权限控制

  • 使用 @PreAuthorize 注解进行权限控制
  • 权限标识:apex:log:queryapex: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_logApex 日志表)

表名datai_apex_log

字段说明

字段名 类型 长度 必填 默认值 说明
id BIGINT - AUTO_INCREMENT 主键 ID
log_id VARCHAR 50 - 日志 IDSalesforce 日志 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_categoryApex 日志分类表)

表名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_logdatai_apex_log_category 是一对多关系
  • 一个日志可以包含多个日志分类
  • 删除日志时,级联删除对应的日志分类

实体类设计

1. DataiApexLogApex 日志实体)

类路径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. DataiApexLogCategoryApex 日志分类实体)

类路径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. 获取日志信息

实现步骤

  1. 校验 logId 参数是否为空
  2. 通过 ApexConnectionFactory 获取 SoapConnection 实例
  3. 调用 SoapConnection.getLog(logId) 获取日志信息
  4. 将 LogInfo 转换为 LogInfoVo
  5. 保存日志信息到数据库(如果不存在)
  6. 返回日志信息

代码示例

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. 设置调试头部

实现步骤

  1. 校验 logCategories 和 logType 参数
  2. 使用 ReentrantLock 保证多线程安全
  3. 将 LogCategoryDto 转换为 LogInfo[]
  4. 通过 ApexConnectionFactory 获取 SoapConnection 实例
  5. 调用 SoapConnection.setDebuggingHeader(logCategories, logType)
  6. 释放锁

代码示例

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. 执行匿名代码并获取日志

实现步骤

  1. 校验 code 参数
  2. 如果提供了 logCategories先设置调试头部
  3. 通过 ApexConnectionFactory 获取 SoapConnection 实例
  4. 调用 SoapConnection.executeAnonymous(code) 执行代码
  5. 获取执行结果和 apexLogId
  6. 如果 apexLogId 不为空,调用 getLog(apexLogId) 获取日志信息
  7. 保存日志信息到数据库
  8. 返回执行结果和日志信息

代码示例

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
  • 缓存 keyapex:log:{logId}
  • 缓存过期时间30 分钟

3. 批量操作优化

  • 批量保存日志分类信息
  • 使用 MyBatis Plus 的批量插入功能

安全设计

1. 参数校验

  • 使用 @Valid 注解进行参数校验
  • 使用 @NotBlank@NotNull 等注解校验必填字段
  • 自定义校验器校验日志分类和级别

2. 权限控制

  • 使用 @PreAuthorize 注解进行权限控制
  • 权限标识:apex:log:queryapex:log:manageapex:log:execute
  • 数据权限:根据部门 ID 过滤日志数据

3. SQL 注入防护

  • 使用 MyBatis Plus 的参数化查询
  • 禁止使用字符串拼接 SQL
  • 使用 MyBatis Plus 的条件构造器

4. XSS 攻击防护

  • 对用户输入进行转义
  • 使用 Spring Security 的 XSS 防护功能
  • 对日志内容进行过滤

相关文档