Fix Bug #550: AI修复
This commit is contained in:
@@ -1,165 +0,0 @@
|
||||
package com.openhis.common.aspectj;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.core.common.utils.DictUtils;
|
||||
import com.openhis.common.annotation.Dict;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
public class DictAspect {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DictAspect.class);
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate; // 使用 JdbcTemplate 执行 SQL
|
||||
|
||||
@Around("@annotation(org.springframework.web.bind.annotation.GetMapping) || "
|
||||
+ "@annotation(org.springframework.web.bind.annotation.PostMapping)")
|
||||
public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
Object result = joinPoint.proceed(); // 执行原方法
|
||||
|
||||
if (result instanceof R) {
|
||||
// 如果返回值是 R<?>,提取其中的数据
|
||||
R<?> response = (R<?>)result;
|
||||
Object data = response.getData(); // 获取 R<?> 中的实际数据
|
||||
|
||||
if (data instanceof Page) {
|
||||
// 如果数据是 Page 类型,处理分页数据
|
||||
Page<?> page = (Page<?>)data;
|
||||
List<?> records = page.getRecords();
|
||||
if (!records.isEmpty()) {
|
||||
for (Object obj : records) {
|
||||
processDict(obj); // 处理每个 DTO 对象
|
||||
}
|
||||
}
|
||||
} else if (data instanceof List) {
|
||||
// 如果数据是 List 类型,处理列表数据
|
||||
List<?> list = (List<?>)data;
|
||||
if (!list.isEmpty()) {
|
||||
for (Object obj : list) {
|
||||
processDict(obj); // 处理每个 DTO 对象
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果数据是单个 DTO 对象,直接处理
|
||||
processDict(data);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private <T> void processDict(T dto) throws IllegalAccessException {
|
||||
if (dto == null) {
|
||||
return;
|
||||
}
|
||||
// 检查对象是否是 DTO 类(即是否有 @Dict 注解的字段)
|
||||
boolean isDto = false;
|
||||
for (Field field : dto.getClass().getDeclaredFields()) {
|
||||
if (field.isAnnotationPresent(Dict.class)) {
|
||||
isDto = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 如果不是 DTO 类,直接返回
|
||||
if (!isDto) {
|
||||
return;
|
||||
}
|
||||
// 获取 DTO 类的所有字段
|
||||
for (Field field : dto.getClass().getDeclaredFields()) {
|
||||
field.setAccessible(true); // 设置字段可访问
|
||||
Object fieldValue = field.get(dto); // 获取字段值
|
||||
|
||||
if (fieldValue == null) {
|
||||
continue; // 如果字段值为空,跳过
|
||||
}
|
||||
// 如果字段是 List 类型,递归处理其中的每个元素
|
||||
if (fieldValue instanceof List) {
|
||||
List<?> list = (List<?>)fieldValue;
|
||||
for (Object item : list) {
|
||||
processDict(item); // 递归处理 List 中的每个元素
|
||||
}
|
||||
} else if (field.isAnnotationPresent(Dict.class)) {
|
||||
// 如果字段带有 @Dict 注解,处理字典值
|
||||
Dict dictAnnotation = field.getAnnotation(Dict.class);
|
||||
String dictCode = dictAnnotation.dictCode();
|
||||
String dictText = dictAnnotation.dictText();
|
||||
String dictTable = dictAnnotation.dictTable();
|
||||
String deleteFlag = dictAnnotation.deleteFlag();
|
||||
|
||||
// 检查 _dictText 字段是否已被手动填充(如控制器方法中预先查询设置)
|
||||
// 如果已非空则跳过 SQL 查询,避免覆盖有效值
|
||||
String textFieldName = field.getName() + "_dictText";
|
||||
try {
|
||||
Field existingTextField = dto.getClass().getDeclaredField(textFieldName);
|
||||
existingTextField.setAccessible(true);
|
||||
Object existingValue = existingTextField.get(dto);
|
||||
if (existingValue != null && !existingValue.toString().isEmpty()) {
|
||||
continue; // _dictText 已有值,跳过
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
// _dictText 字段不存在,继续正常流程
|
||||
}
|
||||
|
||||
// 查询字典值
|
||||
String dictLabel = queryDictLabel(dictTable, dictCode, dictText, deleteFlag, fieldValue.toString());
|
||||
if (dictLabel != null && !dictLabel.isEmpty()) {
|
||||
// 动态生成 _dictText 字段名
|
||||
try {
|
||||
Field textField = dto.getClass().getDeclaredField(textFieldName);
|
||||
textField.setAccessible(true);
|
||||
textField.set(dto, dictLabel); // 设置 _dictText 字段的值
|
||||
} catch (NoSuchFieldException e) {
|
||||
// 如果 _dictText 字段不存在,忽略错误
|
||||
log.debug("字段 {} 不存在,跳过字典翻译", textFieldName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
processDict(fieldValue); // 递归处理 Dto 中的每个元素
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String queryDictLabel(String dictTable, String dictCode, String dictText, String deleteFlag, String dictValue) {
|
||||
if (!StringUtils.hasText(dictTable)) {
|
||||
// 场景 1:默认字典走DictUtils缓存(dictTable 为空时)
|
||||
return DictUtils.getDictLabel(dictCode, dictValue);
|
||||
} else {
|
||||
// 场景 2:查询指定表(dictTable 有值时)
|
||||
// 必须同时有 dictTable 和 dictText 才能执行 SQL 查询
|
||||
if (!StringUtils.hasText(dictText)) {
|
||||
// 如果 dictText 为空,回退到字典缓存查询
|
||||
return DictUtils.getDictLabel(dictCode, dictValue);
|
||||
}
|
||||
// 构建SQL,支持 delete_flag 过滤
|
||||
StringBuilder sqlBuilder = new StringBuilder();
|
||||
sqlBuilder.append(String.format("SELECT %s FROM %s WHERE %s::varchar = ?", dictText, dictTable, dictCode));
|
||||
// 如果指定了 deleteFlag 字段名,添加过滤条件
|
||||
if (StringUtils.hasText(deleteFlag)) {
|
||||
sqlBuilder.append(String.format(" AND %s = '0'", deleteFlag));
|
||||
}
|
||||
sqlBuilder.append(" LIMIT 1");
|
||||
String sql = sqlBuilder.toString();
|
||||
try {
|
||||
return jdbcTemplate.queryForObject(sql, String.class, dictValue);
|
||||
} catch (DataAccessException e) {
|
||||
// 如果查询结果为空,返回 空字符串
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
|
||||
package com.openhis.common.aspectj;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.core.system.domain.SysOperLog;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
public class OperLogAspect {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
// 线程局部变量,用于在方法调用前后传递数据
|
||||
private final ThreadLocal<SysOperLog> operLogThreadLocal = new ThreadLocal<>();
|
||||
private final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 定义切点:所有Controller包下的方法,排除login接口
|
||||
*/
|
||||
@Pointcut("execution(* com.openhis.web..controller..*.*(..)) "
|
||||
+ "&& !execution(* com.openhis.web..controller..*.login(..)) ")
|
||||
public void operLogPointCut() {}
|
||||
|
||||
/**
|
||||
* 前置通知:在方法执行前记录请求信息
|
||||
*/
|
||||
@Before("operLogPointCut()")
|
||||
public void doBefore(JoinPoint joinPoint) {
|
||||
try {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
|
||||
// 创建操作日志对象
|
||||
SysOperLog operLog = new SysOperLog();
|
||||
operLog.setOperTime(new Date());
|
||||
operLog.setOperUrl(request.getRequestURI());
|
||||
operLog.setRequestMethod(request.getMethod());
|
||||
|
||||
// 获取请求参数
|
||||
String operParam = getRequestParams(joinPoint, request);
|
||||
operLog.setOperParam(operParam);
|
||||
|
||||
// 设置操作方法名
|
||||
String className = joinPoint.getTarget().getClass().getName();
|
||||
String methodName = method.getName();
|
||||
operLog.setMethod(className + "." + methodName + "()");
|
||||
|
||||
// 保存到线程局部变量
|
||||
operLogThreadLocal.set(operLog);
|
||||
|
||||
// 记录开始时间
|
||||
startTimeThreadLocal.set(System.currentTimeMillis());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("操作日志前置通知异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回通知:在方法成功执行后记录结果
|
||||
*/
|
||||
@AfterReturning(pointcut = "operLogPointCut()", returning = "result")
|
||||
public void doAfterReturning(JoinPoint joinPoint, Object result) {
|
||||
try {
|
||||
SysOperLog operLog = operLogThreadLocal.get();
|
||||
if (operLog == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算消耗时间
|
||||
Long startTime = startTimeThreadLocal.get();
|
||||
if (startTime != null) {
|
||||
Long costTime = System.currentTimeMillis() - startTime;
|
||||
operLog.setCostTime(costTime);
|
||||
}
|
||||
|
||||
// 设置操作结果
|
||||
if (result instanceof R) {
|
||||
R<?> r = (R<?>)result;
|
||||
operLog.setJsonResult(JSON.toJSONString(r));
|
||||
// 根据R的code判断操作状态
|
||||
if (r.getCode() != 200) { // 假设200是成功状态码
|
||||
operLog.setStatus(1); // 失败
|
||||
operLog.setErrorMsg(r.getMsg());
|
||||
}
|
||||
} else {
|
||||
operLog.setJsonResult(result != null ? JSON.toJSONString(result) : "null");
|
||||
}
|
||||
// 插入数据库
|
||||
insertOperLog(operLog);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("操作日志返回通知异常", e);
|
||||
} finally {
|
||||
// 清理线程局部变量
|
||||
operLogThreadLocal.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
*/
|
||||
private String getRequestParams(JoinPoint joinPoint, HttpServletRequest request) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
|
||||
// 添加基本请求信息
|
||||
params.put("url", request.getRequestURL().toString());
|
||||
params.put("method", request.getMethod());
|
||||
|
||||
// 获取Query参数
|
||||
Map<String, String[]> parameterMap = request.getParameterMap();
|
||||
if (!parameterMap.isEmpty()) {
|
||||
Map<String, Object> queryParams = new HashMap<>();
|
||||
parameterMap.forEach((key, values) -> {
|
||||
if (values.length == 1) {
|
||||
queryParams.put(key, values[0]);
|
||||
} else {
|
||||
queryParams.put(key, values);
|
||||
}
|
||||
});
|
||||
params.put("query", queryParams);
|
||||
}
|
||||
|
||||
// 获取Body参数(排除文件上传和响应对象)
|
||||
Object[] args = joinPoint.getArgs();
|
||||
if (args.length > 0) {
|
||||
for (Object arg : args) {
|
||||
if (isValidRequestBody(arg)) {
|
||||
params.put("body", arg);
|
||||
break; // 通常只有一个RequestBody
|
||||
}
|
||||
}
|
||||
}
|
||||
return JSON.toJSONString(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为有效的请求体参数
|
||||
*/
|
||||
private boolean isValidRequestBody(Object arg) {
|
||||
return arg != null && !(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)
|
||||
&& !(arg instanceof MultipartFile) && !(arg instanceof MultipartFile[]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入操作日志到数据库
|
||||
*/
|
||||
private void insertOperLog(SysOperLog operLog) {
|
||||
String username = SecurityUtils.getUsernameSafe(); // 使用安全获取用户名的方法
|
||||
String sql = "INSERT INTO sys_oper_log "
|
||||
+ "(title,oper_time,method,request_method,oper_name,oper_url,oper_param,json_result,error_msg,cost_time) "
|
||||
+ "VALUES (?, ?, ?,?, ?, ?, ?, ?,?, ?)";
|
||||
try {
|
||||
jdbcTemplate.update(sql, "OperLogAspect切面生成", operLog.getOperTime(), operLog.getMethod(),
|
||||
operLog.getRequestMethod(), username, operLog.getOperUrl(), operLog.getOperParam(),
|
||||
operLog.getJsonResult(), operLog.getErrorMsg(), operLog.getCostTime());
|
||||
} catch (Exception e) {
|
||||
log.error("插入操作日志失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user