feat(sys_oper_log): 新增操作日志备注字段

main
zangch@mesnac.com 3 months ago
parent 1532a9dac9
commit 5b010d32fb

@ -45,4 +45,11 @@ public @interface Log {
*/
String[] excludeParamNames() default {};
/**
* SpEL
* : "模块标题 - 操作类型"
* : "新增能源类型【#bo.energyName}】"
*/
String remark() default "";
}

@ -13,6 +13,7 @@ import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.utils.ServletUtils;
@ -21,14 +22,26 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessStatus;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.log.event.OperLogEvent;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.http.HttpMethod;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
@ -45,6 +58,16 @@ public class LogAspect {
*/
private static final ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>();
/**
* SpEL
*/
private static final ExpressionParser SPEL_PARSER = new SpelExpressionParser();
/**
*
*/
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
/**
*
*/
@ -131,6 +154,8 @@ public class LogAspect {
operLog.setTitle(log.title());
// 设置操作人类别
operLog.setOperatorType(log.operatorType().ordinal());
// 设置操作备注
setOperRemark(joinPoint, log, operLog);
// 是否需要保存request参数和值
if (log.isSaveRequestData()) {
// 获取参数的信息,传入到数据库中。
@ -142,6 +167,123 @@ public class LogAspect {
}
}
/**
* remark SpEL #param.field} #param
* } remark "新增了xx【#bo.name}】"
*/
private static final Pattern REMARK_SPEL_PATTERN = Pattern.compile("#([\\w.]+)}?");
/**
*
* remark #param.field SpEL
* remark "模块标题 - 操作类型"
*/
private void setOperRemark(JoinPoint joinPoint, Log log, OperLogEvent operLog) {
String remark = log.remark();
if (StringUtils.isNotBlank(remark)) {
try {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
EvaluationContext context = new MethodBasedEvaluationContext(
joinPoint.getTarget(), method, joinPoint.getArgs(), PARAMETER_NAME_DISCOVERER
);
// 正则提取 #param.field 模式,逐个求值后替换回原文
String result = resolveSpelTemplate(remark, context);
operLog.setOperRemark(StringUtils.substring(result, 0, 500));
} catch (Exception e) {
// 这里显式取类级 logger避免被方法参数 log 注解对象遮蔽,导致整个模块无法编译。
org.slf4j.LoggerFactory.getLogger(LogAspect.class).warn("SpEL表达式解析失败使用原始备注: {}", remark);
operLog.setOperRemark(StringUtils.substring(remark, 0, 500));
}
} else {
// 自动兜底:模块标题 - 操作类型
String title = log.title();
String desc = log.businessType().getDescription();
operLog.setOperRemark(title + " - " + desc);
}
}
/**
* remark SpEL
* <p>
* {@code "新增了能源类型【#bo.energyName}】"}
* {@code #bo.energyName} SpEL }
* <p>
*
* 1. {@code #paramName.fieldName}
* 2. # SpEL {@code bo.energyName}
* 3. SpEL {@link MethodBasedEvaluationContext}
* 4.
* <p>
*
*
*
* @param template SpEL
* @param context SpEL
* @return
*/
private String resolveSpelTemplate(String template, EvaluationContext context) {
// 用预编译正则匹配模板中所有 SpEL 占位符
// 正则 #([\w.]+)}? 能匹配:
// - #bo.energyName → 捕获 "bo.energyName",不匹配 }
// - #objIds} → 捕获 "objIds",同时消费掉闭合的 }
// 这样 "删除了xx【#objIds}】" 替换后变为 "删除了xx【1,2,3】",多余的 } 不会残留
Matcher matcher = REMARK_SPEL_PATTERN.matcher(template);
// StringBuffer 用于逐步拼接替换结果Matcher.appendReplacement 要求 StringBuffer
StringBuffer sb = new StringBuffer();
// 逐个处理模板中匹配到的占位符
while (matcher.find()) {
// 捕获组1去掉 # 前缀后的纯表达式,如 "bo.energyName" 或 "objIds"
// 这正是 SpEL 可以直接解析的表达式(引用方法参数名.属性路径)
String spelExpr = matcher.group(1);
try {
// 用 SpEL 解析器求值
// 例如 spelExpr="bo.energyName"context 中 bo 对应方法参数,
// SpEL 会调用 bo.getEnergyName() 取值
Object value = SPEL_PARSER.parseExpression(spelExpr).getValue(context);
String strValue;
if (value == null) {
// 属性值为 null替换为空串避免日志中出现 "null" 字样
strValue = "";
} else if (value.getClass().isArray()) {
// 数组类型处理DELETE 操作的参数通常是 Long[] ids需要转为可读的逗号分隔形式
// 例如 Long[]{1, 2, 3} → "1,2,3"
// 使用 java.lang.reflect.Array 统一处理基本类型数组和对象数组
int len = Array.getLength(value);
StringBuilder arrStr = new StringBuilder();
for (int i = 0; i < len; i++) {
if (i > 0) arrStr.append(",");
arrStr.append(Array.get(value, i));
}
strValue = arrStr.toString();
} else {
// 普通对象:直接 toString()
// 例如 String "电"、BigDecimal "12.50" 等
strValue = value.toString();
}
// 将求值结果写入拼接缓冲区
// quoteReplacement 防止替换值中含 $ 或 \ 导致 Matcher.appendReplacement 异常
matcher.appendReplacement(sb, Matcher.quoteReplacement(strValue));
} catch (Exception ex) {
// 单个表达式解析失败时的降级处理:
// 不抛异常中断整个流程,仅保留原始占位符文本
// 这样即使某个字段名拼写错误,其他占位符仍能正常解析
matcher.appendReplacement(sb, Matcher.quoteReplacement(matcher.group(0)));
}
}
// 将模板中最后一个占位符之后的剩余文本追加到缓冲区
matcher.appendTail(sb);
// 返回完整的替换结果
return sb.toString();
}
/**
* log
*

@ -112,4 +112,9 @@ public class OperLogEvent implements Serializable {
*
*/
private Long costTime;
/**
* 便
*/
private String operRemark;
}

@ -48,7 +48,7 @@ public class LuggageSystemPlcBufferBatteryLifecycleController extends EmsBaseCon
* PLC
*/
@SaCheckPermission("ems/info:plcBufferBatteryLifecycle:export")
@Log(title = "行李系统PLC缓冲电池生命周期", businessType = BusinessType.EXPORT)
@Log(title = "行李系统PLC缓冲电池生命周期", businessType = BusinessType.EXPORT, remark = "导出了行李系统PLC缓冲电池生命周期数据")
@PostMapping("/export")
public void export(HttpServletResponse response, LuggageSystemPlcBufferBatteryLifecycle luggageSystemPlcBufferBatteryLifecycle)
{
@ -70,7 +70,7 @@ public class LuggageSystemPlcBufferBatteryLifecycleController extends EmsBaseCon
* PLC
*/
@SaCheckPermission("ems/info:plcBufferBatteryLifecycle:add")
@Log(title = "行李系统PLC缓冲电池生命周期", businessType = BusinessType.INSERT)
@Log(title = "行李系统PLC缓冲电池生命周期", businessType = BusinessType.INSERT, remark = "新增了行李系统PLC缓冲电池生命周期【#luggageSystemPlcBufferBatteryLifecycle.installationCabinetName}】")
@PostMapping
public R<?> add(@RequestBody LuggageSystemPlcBufferBatteryLifecycle luggageSystemPlcBufferBatteryLifecycle)
{
@ -81,7 +81,7 @@ public class LuggageSystemPlcBufferBatteryLifecycleController extends EmsBaseCon
* PLC
*/
@SaCheckPermission("ems/info:plcBufferBatteryLifecycle:edit")
@Log(title = "行李系统PLC缓冲电池生命周期", businessType = BusinessType.UPDATE)
@Log(title = "行李系统PLC缓冲电池生命周期", businessType = BusinessType.UPDATE, remark = "修改了行李系统PLC缓冲电池生命周期【#luggageSystemPlcBufferBatteryLifecycle.installationCabinetName}】")
@PutMapping
public R<?> edit(@RequestBody LuggageSystemPlcBufferBatteryLifecycle luggageSystemPlcBufferBatteryLifecycle)
{
@ -92,7 +92,7 @@ public class LuggageSystemPlcBufferBatteryLifecycleController extends EmsBaseCon
* PLC
*/
@SaCheckPermission("ems/info:plcBufferBatteryLifecycle:remove")
@Log(title = "行李系统PLC缓冲电池生命周期", businessType = BusinessType.DELETE)
@Log(title = "行李系统PLC缓冲电池生命周期", businessType = BusinessType.DELETE, remark = "删除了行李系统PLC缓冲电池生命周期ID【#objids}】")
@DeleteMapping("/{objids}")
public R<?> remove(@PathVariable Long[] objids)
{
@ -103,7 +103,7 @@ public class LuggageSystemPlcBufferBatteryLifecycleController extends EmsBaseCon
* PLC
*/
@SaCheckPermission("ems/info:plcBufferBatteryLifecycle:add")
@Log(title = "行李系统PLC缓冲电池生命周期", businessType = BusinessType.IMPORT)
@Log(title = "行李系统PLC缓冲电池生命周期", businessType = BusinessType.IMPORT, remark = "导入了行李系统PLC缓冲电池生命周期数据")
@PostMapping("/importData")
public R<?> importData(MultipartFile file, @RequestParam(value = "updateSupport", defaultValue = "false") boolean updateSupport) throws Exception
{

@ -2,6 +2,7 @@ package ${packageName}.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import ${packageName}.domain.${ClassName};
import ${packageName}.domain.vo.${ClassName}Vo;

Loading…
Cancel
Save