原神愿望模拟器大聪明版
62.05M · 2026-04-23
若依框架的日志管理系统提供:
记录业务操作,包含:
记录登录信息,包含:
┌─────────────────────────────────────────────────────────┐
│ Controller层 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ @Log注解标记需要记录日志的方法 │ │
│ └──────────────────────────────────────────────────┘ │
└───────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ AOP切面层 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ LogAspect (操作日志切面) │ │
│ │ - @Before: 记录开始时间 │ │
│ │ - @AfterReturning: 正常返回处理 │ │
│ │ - @AfterThrowing: 异常处理 │ │
│ └──────────────────────────────────────────────────┘ │
└───────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 异步任务管理层 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ AsyncManager (异步任务管理器) │ │
│ │ - 单例模式 │ │
│ │ - 延迟10ms执行 │ │
│ └──────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ AsyncFactory (异步工厂) │ │
│ │ - recordOper(): 创建操作日志任务 │ │
│ │ - recordLogininfor(): 创建登录日志任务 │ │
│ └──────────────────────────────────────────────────┘ │
└───────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 数据持久层 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Service层 → Mapper层 → 数据库 │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
AOP:AspectJ异步处理:ScheduledExecutorService数据持久化:MyBatisJSON处理:FastJSON2IP地址解析:AddressUtils@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
/** 模块 */
public String title() default "";
/** 功能 */
public BusinessType businessType() default BusinessType.OTHER;
/** 操作人类别 */
public OperatorType operatorType() default OperatorType.MANAGE;
/** 是否保存请求的参数 */
public boolean isSaveRequestData() default true;
/** 是否保存响应的参数 */
public boolean isSaveResponseData() default true;
/** 排除指定的请求参数 */
public String[] excludeParamNames() default {};
}
参数说明:
title:操作模块名称businessType:业务类型(INSERT/UPDATE/DELETE/QUERY/EXPORT等)operatorType:操作人类别(MANAGE/MOBILE/OTHER)isSaveRequestData:是否保存请求参数isSaveResponseData:是否保存响应参数excludeParamNames:排除的参数名(如密码字段)核心类:com.construct.framework.aspectj.LogAspect
LogAspect.java Lines 54-58
@Before(value = "@annotation(controllerLog)")
public void boBefore(JoinPoint joinPoint, Log controllerLog)
{
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
EXCLUDE_PROPERTIES:默认排除的敏感字段(password、oldPassword等)TIME_THREADLOCAL:ThreadLocal,记录方法开始时间boBefore():方法执行前ThreadLocaldoAfterReturning():方法正常返回后handleLog()处理日志doAfterThrowing():方法抛出异常后handleLog(),传入异常信息handleLog():统一日志处理LogAspect.java Lines 83-130
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult){
try {
// 获取当前的用户
LoginUser loginUser = SecurityUtils.getLoginUser();
// *========数据库日志=========*//
SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 请求的地址
String ip = IpUtils.getIpAddr();
operLog.setOperIp(ip);
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
if (loginUser != null)
{
operLog.setOperName(loginUser.getUser().getNickName());
}
if (e != null)
{
operLog.setStatus(BusinessStatus.FAIL.ordinal());
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
}
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operLog.setMethod(className + "." + methodName + "()");
// 设置请求方式
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
// 设置消耗时间
operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
// 保存数据库
AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
}
catch (Exception exp)
{
// 记录本地异常日志
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
finally
{
TIME_THREADLOCAL.remove();
}
}
处理流程:
(1) 获取当前登录用户
(2) 创建SysOperLog对象
(3) 设置IP、URL、操作人
(4) 处理异常信息(如有)
(5) 设置方法名、请求方式
(6) 解析注解参数
(7) 计算耗时
(8) 异步保存日志
getControllerMethodDescription():解析注解参数LogAspect.java ines 139-158
public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception
{
// 设置action动作
operLog.setBusinessType(log.businessType().ordinal());
// 设置标题
operLog.setTitle(log.title());
// 设置操作人类别
operLog.setOperatorType(log.operatorType().ordinal());
// 是否需要保存request,参数和值
if (log.isSaveRequestData())
{
// 获取参数的信息,传入到数据库中。
setRequestValue(joinPoint, operLog, log.excludeParamNames());
}
// 是否需要保存response,参数和值
if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
{
operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
}
}
setRequestValue():获取并处理请求参数LogAspect.java Lines 166-180
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception
{
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
String requestMethod = operLog.getRequestMethod();
if (StringUtils.isEmpty(paramsMap)
&& (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)))
{
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
}
else
{
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
}
}
isFilterObject():过滤不需要记录的对象LogAspect.java Lines 222-249
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o)
{
Class<?> clazz = o.getClass();
if (clazz.isArray())
{
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
}
else if (Collection.class.isAssignableFrom(clazz))
{
Collection collection = (Collection) o;
for (Object value : collection)
{
return value instanceof MultipartFile;
}
}
else if (Map.class.isAssignableFrom(clazz))
{
Map map = (Map) o;
for (Object value : map.entrySet())
{
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
过滤对象:
MultipartFile(文件上传)HttpServletRequest、HttpServletResponseBindingResult(参数校验结果)AsyncManager.java Lines 15-56
public class AsyncManager
{
/**
* 操作延迟10毫秒
*/
private final int OPERATE_DELAY_TIME = 10;
/**
* 异步操作任务调度线程池
*/
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
/**
* 单例模式
*/
private AsyncManager(){}
private static AsyncManager me = new AsyncManager();
public static AsyncManager me()
{
return me;
}
/**
* 执行任务
*
* @param task 任务
*/
public void execute(TimerTask task)
{
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
}
/**
* 停止任务线程池
*/
public void shutdown()
{
Threads.shutdownAndAwaitTermination(executor);
}
}
特点:
10ms执行,避免影响主流程ScheduledExecutorService执行任务AsyncFactory.java Lines 99-111
public static TimerTask recordOper(final SysOperLog operLog)
{
return new TimerTask()
{
@Override
public void run()
{
// 远程查询操作地点
operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);
}
};
}
功能:
recordOper():创建操作日志任务
Service保存日志recordLogininfor():创建登录日志任务
User-Agent获取浏览器和操作系统ThreadPoolConfig.java Lines 45-62
/**
* 执行周期性或定时任务
*/
@Bean(name = "scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService()
{
return new ScheduledThreadPoolExecutor(corePoolSize,
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
new ThreadPoolExecutor.CallerRunsPolicy())
{
@Override
protected void afterExecute(Runnable r, Throwable t)
{
super.afterExecute(r, t);
Threads.printException(r, t);
}
};
}
配置参数:
schedule-pool-%dCallerRunsPolicy主要字段:
operId:日志主键title:操作模块businessType:业务类型(0其它 1新增 2修改 3删除等)method:请求方法(类名.方法名)requestMethod:请求方式(GET/POST等)operatorType:操作类别(0其它 1后台用户 2手机端用户)operName:操作人员operUrl:请求URLoperIp:操作IPoperLocation:操作地点operParam:请求参数(JSON,最大2000字符)jsonResult:返回参数(JSON,最大2000字符)status:操作状态(0正常 1异常)errorMsg:错误消息(最大2000字符)operTime:操作时间costTime:消耗时间(毫秒)1. Controller方法执行
↓
2. @Log注解触发AOP切面
↓
3. LogAspect.boBefore()
- 记录开始时间到ThreadLocal
↓
4. 业务方法执行
↓
5. LogAspect.doAfterReturning() 或 doAfterThrowing()
↓
6. LogAspect.handleLog()
- 获取用户信息
- 创建SysOperLog对象
- 设置基本信息(IP、URL、方法名等)
- 解析@Log注解参数
- 处理请求参数和响应结果
- 计算执行耗时
↓
7. AsyncManager.execute()
- 延迟10ms执行
↓
8. AsyncFactory.recordOper()
- 根据IP查询操作地点
- 调用Service保存日志
↓
9. SysOperLogService.insertOperlog()
- 保存到数据库
1. 用户登录/登出
↓
2. 调用AsyncManager.execute(AsyncFactory.recordLogininfor(...))
↓
3. AsyncFactory.recordLogininfor()
- 解析User-Agent获取浏览器和操作系统
- 获取IP地址
- 根据IP查询登录地点
- 创建SysLogininfor对象
↓
4. SysLogininforService.insertLogininfor()
- 保存到数据库
1. 检查是否需要保存请求参数(isSaveRequestData)
↓
2. 获取请求参数
- GET请求:从request.getParameterMap()获取
- POST/PUT请求:从方法参数获取
↓
3. 过滤敏感字段
- 默认过滤:password、oldPassword、newPassword、confirmPassword
- 注解指定过滤:excludeParamNames
↓
4. 过滤不需要的对象
- MultipartFile、HttpServletRequest、HttpServletResponse、BindingResult
↓
5. 转换为JSON字符串(最大2000字符)
↓
6. 保存到operParam字段
在Controller方法上添加@Log注解:
@RestController
@RequestMapping("/system/user")
public class SysUserController {
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysUser user) {
// 业务逻辑
return AjaxResult.success();
}
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody SysUser user) {
// 业务逻辑
return AjaxResult.success();
}
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
public AjaxResult remove(@PathVariable Long[] userIds) {
// 业务逻辑
return AjaxResult.success();
}
@Log(title = "用户管理", businessType = BusinessType.QUERY)
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
// 业务逻辑
return getDataTable(list);
}
}
@Log(
title = "用户管理",
businessType = BusinessType.UPDATE,
excludeParamNames = {"password", "secretKey"}
)
@PutMapping
public AjaxResult updateUser(@RequestBody SysUser user) {
// 业务逻辑
}
@Log(
title = "用户管理",
businessType = BusinessType.QUERY,
isSaveRequestData = false
)
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
// 业务逻辑
}
@Log(
title = "用户管理",
businessType = BusinessType.QUERY,
isSaveResponseData = false
)
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
// 业务逻辑
}
@Log(
title = "移动端用户管理",
businessType = BusinessType.INSERT,
operatorType = OperatorType.MOBILE
)
@PostMapping
public AjaxResult add(@RequestBody SysUser user) {
// 业务逻辑
}
public enum BusinessType {
OTHER, // 其它
INSERT, // 新增
UPDATE, // 修改
DELETE, // 删除
GRANT, // 授权
EXPORT, // 导出
IMPORT, // 导入
FORCE, // 强退
GENCODE, // 生成代码
CLEAN, // 清空数据
QUERY // 查询
}
public enum OperatorType {
OTHER, // 其它
MANAGE, // 后台用户
MOBILE // 手机端用户
}
@Aspect和@Component声明切面@Before、@AfterReturning、@AfterThrowing定义切点@annotation(controllerLog),拦截带@Log注解的方法NamedThreadLocal记录方法开始时间finally中清理,防止内存泄漏ScheduledExecutorService实现异步10ms执行,不阻塞主流程PropertyPreExcludeFilter实现JSON序列化过滤IpUtils.getIpAddr()获取真实IPAddressUtils.getRealAddressByIP()查询地理位置StringUtils.substring()截断status为FAIL,记录errorMsg除了LogAspect,还有一个ControllerLogsAspect用于开发调试:
@Component
@Aspect
@Order(1)
@Slf4j
public class ControllerLogsAspect {
@Pointcut("execution(public * com.construct.*.controller..*.*(..))")
public void pointCut() {}
@Before("pointCut()")
public void doBeforeInServiceLayer(JoinPoint joinPoint) {
// 记录开始时间
}
@AfterReturning(pointcut = "pointCut()", returning = "returnValue")
public void log(JoinPoint joinPoint, Object returnValue) {
// 打印调试日志
}
}
功能:
Controller方法系统还提供了业务日志扩展(如PubOperateLog、DmsOperateLog),用于记录业务数据变更:特点:
系统提供了日志查询和管理功能:
/monitor/operlog/list/monitor/logininfor/listLogAspect.java - 操作日志切面ControllerLogsAspect.java - 控制器日志切面(调试用)Log.java - 日志注解定义AsyncManager.java - 异步任务管理器AsyncFactory.java - 异步工厂ThreadPoolConfig.java - 线程池配置SysOperLog.java - 操作日志实体SysLogininfor.java - 登录日志实体ISysOperLogService.java - 操作日志服务接口SysOperLogServiceImpl.java - 操作日志服务实现ISysLogininforService.java - 登录日志服务接口SysLogininforServiceImpl.java - 登录日志服务实现SysOperLogMapper.java - 操作日志Mapper接口SysOperLogMapper.xml - 操作日志Mapper XMLSysLogininforMapper.java - 登录日志Mapper接口SysLogininforMapper.xml - 登录日志Mapper XMLSysOperlogController.java - 操作日志管理控制器SysLogininforController.java - 登录日志管理控制器