Revert "Fix Bug #550: AI修复"

This reverts commit 16c42ca108.
This commit is contained in:
2026-05-27 08:59:07 +08:00
parent bd14563691
commit 9db5ced4e3
5432 changed files with 778638 additions and 171 deletions

View File

@@ -0,0 +1,165 @@
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 "";
}
}
}
}

View File

@@ -0,0 +1,191 @@
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);
}
}
}