当前位置: 首页 > wzjs >正文

动易做网站如何代写文案的软件

动易做网站如何,代写文案的软件,湛江网站定制,什么的网站策划记录用户的操作日志,方便排查问题。 解决方案: 数据库表设计 CREATE TABLE sys_log (id int NOT NULL AUTO_INCREMENT COMMENT 日志主键ID,module varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 所属模块,title …

记录用户的操作日志,方便排查问题。

解决方案:

  1. 数据库表设计

    CREATE TABLE `sys_log` (`id` int NOT NULL AUTO_INCREMENT COMMENT '日志主键ID',`module` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '所属模块',`title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '日志标题',`user_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户ID',`user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户名称',`mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '手机号',`remote_addr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求IP',`request_uri` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求URI',`class_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '方法类名',`method_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '方法名称',`params` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求参数',`time` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求耗时',`browser` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '浏览器名称',`os` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作系统',`ex_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '错误类型',`ex_msg` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '错误信息',`create_time` datetime DEFAULT NULL COMMENT '创建日期',PRIMARY KEY (`id`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='日志信息表';
    
  2. 切面注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface SysLogAop {/*** 模块名称*/String module() default "";/*** 日志标题*/String title() default "";/*** 排除记录的参数*/String[] excludeParam() default {};/*** 是否记录请求参数*/boolean includeParam() default true;/*** 是否记录返回结果*/boolean includeResult() default false;
    }
    
  3. 切面

    @Aspect
    @Component
    public class SysLogAspect {private static final Logger LOGGER = LoggerFactory.getLogger(SysLogAspect.class);private final ThreadLocal<Long> startTime = new ThreadLocal<>();@Autowiredprivate AsyncSysLogService asyncSysLogService;@Autowiredprivate ObjectMapper objectMapper;@Pointcut("@annotation(sysLogAop)")public void logPointcut(SysLogAop sysLogAop) {}@Before("logPointcut(sysLogAop)")public void before(JoinPoint joinPoint, SysLogAop sysLogAop) {startTime.set(System.currentTimeMillis());}@AfterReturning(pointcut = "logPointcut(sysLogAop)", returning = "result")public void afterReturning(JoinPoint joinPoint, SysLogAop sysLogAop, Object result) {saveLog(joinPoint, sysLogAop, null, result);}@AfterThrowing(pointcut = "logPointcut(sysLogAop)", throwing = "exception")public void afterThrowing(JoinPoint joinPoint, SysLogAop sysLogAop, Exception exception) {saveLog(joinPoint, sysLogAop, exception, null);}/*** 记录操作日志** @param joinPoint 切点* @param sysLogAop 切面* @param exception 异常* @param result    返回结果*/private void saveLog(JoinPoint joinPoint, SysLogAop sysLogAop, Exception exception, Object result) {try {SysLog sysLog = new SysLog();// 设置模块和标题sysLog.setModule(sysLogAop.module());sysLog.setTitle(sysLogAop.title());// 设置用户信息SysUser currentUser = getCurrentUser();if (currentUser != null) {sysLog.setUserId(currentUser.getUserId());sysLog.setUserName(currentUser.getUserName());sysLog.setMobile(formatMobile(currentUser.getPhonenumber()));}// 获取请求信息ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (attributes != null) {HttpServletRequest request = attributes.getRequest();sysLog.setRemoteAddr(getClientIpAddr(request));sysLog.setRequestUri(request.getRequestURI());sysLog.setBrowser(getBrowser(request));sysLog.setOs(getOs(request));// 记录请求参数(完整的JSON格式,支持嵌套对象)if (sysLogAop.includeParam() && joinPoint.getArgs() != null) {String params = getCompleteParamsJson(joinPoint.getArgs(), sysLogAop);sysLog.setParams(params.length() > 2000 ? params.substring(0, 2000) + "...[截断]" : params);}}// 设置方法信息String className = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();sysLog.setClassName(className);sysLog.setMethodName(methodName);// 计算执行时间(毫秒)Long time = System.currentTimeMillis() - startTime.get();sysLog.setTime(String.valueOf(time));// 处理异常信息if (exception != null) {sysLog.setExCode(exception.getClass().getSimpleName());String errorMsg = exception.getMessage();sysLog.setExMsg(errorMsg != null && errorMsg.length() > 2000 ?errorMsg.substring(0, 2000) : errorMsg);}// 设置返回值(完整的JSON格式,支持嵌套对象)if (sysLogAop.includeResult() && result != null && exception == null) {try {Object filteredResult = filterSensitiveFields(result, sysLogAop);String resultJson = objectMapper.writeValueAsString(filteredResult);// 限制返回值长度,避免数据过大if (resultJson.length() > 5000) {sysLog.setResult(resultJson.substring(0, 5000) + "...[截断]");} else {sysLog.setResult(resultJson);}} catch (Exception e) {sysLog.setResult("返回值序列化失败:" + e.getMessage());LOGGER.error("返回值序列化失败", e);}}// 设置创建时间sysLog.setCreateTime(new Date());// 异步保存日志asyncSysLogService.saveLogAsync(sysLog);} catch (Exception e) {// 记录日志失败不影响正常业务LOGGER.error("记录操作日志失败 {}", e.getMessage());} finally {// 清理ThreadLocal,防止内存泄漏startTime.remove();}}/*** 获取完整的参数JSON(支持嵌套对象)*/private String getCompleteParamsJson(Object[] args, SysLogAop sysLogAop) {try {List<Object> filteredArgs = new ArrayList<>();for (Object arg : args) {if (shouldSkipParameter(arg)) {filteredArgs.add("[" + arg.getClass().getSimpleName() + "]");} else {filteredArgs.add(filterSensitiveFields(arg, sysLogAop));}}return objectMapper.writeValueAsString(filteredArgs);} catch (Exception e) {LOGGER.error("参数序列化失败", e);return "参数序列化失败:" + e.getMessage();}}/*** 检查是否应该跳过某些参数*/private boolean shouldSkipParameter(Object arg) {return arg instanceof HttpServletRequest ||arg instanceof HttpServletResponse ||arg instanceof MultipartFile ||(arg instanceof Collection && ((Collection<?>) arg).stream().anyMatch(item -> item instanceof MultipartFile));}/*** 过滤敏感字段(支持递归处理嵌套对象)*/private Object filterSensitiveFields(Object obj, SysLogAop sysLogAop) {return filterSensitiveFields(obj, new HashSet<>(), sysLogAop);}/*** 过滤敏感字段(递归处理,防止循环引用)*/private Object filterSensitiveFields(Object obj, Set<Object> visited, SysLogAop sysLogAop) {if (obj == null) {return null;}// 防止循环引用if (visited.contains(obj)) {return "[循环引用: " + obj.getClass().getSimpleName() + "]";}// 基本类型和字符串直接返回if (isBasicType(obj)) {return obj;}// Date类型特殊处理if (obj instanceof Date) {return obj;}// 数组处理if (obj.getClass().isArray()) {visited.add(obj);Object[] array = (Object[]) obj;Object[] filteredArray = new Object[array.length];for (int i = 0; i < array.length; i++) {filteredArray[i] = filterSensitiveFields(array[i], visited, sysLogAop);}visited.remove(obj);return filteredArray;}// Collection处理if (obj instanceof Collection) {visited.add(obj);Collection<?> collection = (Collection<?>) obj;List<Object> filteredList = new ArrayList<>();for (Object item : collection) {filteredList.add(filterSensitiveFields(item, visited, sysLogAop));}visited.remove(obj);return filteredList;}// Map处理if (obj instanceof Map) {visited.add(obj);Map<?, ?> map = (Map<?, ?>) obj;Map<Object, Object> filteredMap = new LinkedHashMap<>();for (Map.Entry<?, ?> entry : map.entrySet()) {String key = String.valueOf(entry.getKey());boolean isSensitive = Arrays.stream(sysLogAop.excludeParam()).anyMatch(prop -> prop.equalsIgnoreCase(key));if (isSensitive) {filteredMap.put(entry.getKey(), "***");} else {filteredMap.put(entry.getKey(), filterSensitiveFields(entry.getValue(), visited, sysLogAop));}}visited.remove(obj);return filteredMap;}// 自定义对象处理try {visited.add(obj);// 创建一个Map来存储过滤后的字段,而不是创建新的对象实例Map<String, Object> filteredObject = new LinkedHashMap<>();// 获取所有字段,包括父类字段List<Field> allFields = getAllFields(obj.getClass());for (Field field : allFields) {field.setAccessible(true);String fieldName = field.getName();// 跳过一些特殊字段if (fieldName.equals("serialVersionUID") || fieldName.contains("$")) {continue;}// 检查是否是敏感字段boolean isSensitive = Arrays.stream(sysLogAop.excludeParam()).anyMatch(prop -> prop.equalsIgnoreCase(fieldName));try {Object value = field.get(obj);if (isSensitive && value instanceof String) {filteredObject.put(fieldName, "***");} else {filteredObject.put(fieldName, filterSensitiveFields(value, visited, sysLogAop));}} catch (IllegalAccessException e) {// 如果无法访问字段,记录为无法访问filteredObject.put(fieldName, "[无法访问]");}}visited.remove(obj);return filteredObject;} catch (Exception e) {visited.remove(obj);LOGGER.warn("过滤对象字段失败: {}", obj.getClass().getName(), e);return "[对象序列化失败: " + obj.getClass().getSimpleName() + "]";}}/*** 获取所有字段,包括父类字段*/private List<Field> getAllFields(Class<?> clazz) {List<Field> allFields = new ArrayList<>();while (clazz != null && clazz != Object.class) {allFields.addAll(Arrays.asList(clazz.getDeclaredFields()));clazz = clazz.getSuperclass();}return allFields;}/*** 判断是否是基本类型*/private boolean isBasicType(Object obj) {return obj instanceof String ||obj instanceof Number ||obj instanceof Boolean ||obj instanceof Character ||obj.getClass().isPrimitive() ||obj.getClass().isEnum();}/*** 格式化手机号,去掉国际区号*/private String formatMobile(String mobile) {if (mobile == null || mobile.isEmpty()) {return mobile;}// 去掉+86前缀if (mobile.startsWith("+86")) {return mobile.substring(3);}// 去掉86前缀(没有+号的情况)if (mobile.startsWith("86") && mobile.length() > 11) {return mobile.substring(2);}return mobile;}/*** 获取当前登陆人*/private SysUser getCurrentUser() {try {LoginUser loginUser = SecurityUtils.getLoginUser();if (loginUser == null) {return null;}return loginUser.getSysUser();} catch (Exception e) {return null;}}/*** 获取客户端IP地址*/private String getClientIpAddr(HttpServletRequest request) {String ip = request.getHeader("X-Forwarded-For");if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("X-Real-IP");}if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip;}/*** 获取浏览器信息*/private String getBrowser(HttpServletRequest request) {String userAgent = request.getHeader("User-Agent");if (userAgent == null) return "Unknown";if (userAgent.contains("Chrome")) return "Chrome";if (userAgent.contains("Firefox")) return "Firefox";if (userAgent.contains("Safari")) return "Safari";if (userAgent.contains("Edge")) return "Edge";if (userAgent.contains("Opera")) return "Opera";return "Other";}/*** 获取操作系统信息*/private String getOs(HttpServletRequest request) {String userAgent = request.getHeader("User-Agent");if (userAgent == null) return "Unknown";if (userAgent.contains("Windows")) return "Windows";if (userAgent.contains("Mac")) return "macOS";if (userAgent.contains("Linux")) return "Linux";if (userAgent.contains("Android")) return "Android";if (userAgent.contains("iPhone")) return "iOS";return "Other";}
    }
    
  4. 异步记录操作日志,这里可以使用消息队列或者异步线程,本系统是要把操作日志同步到远程数据仓库,采用异步线程发送http请求的方式

    @Service
    public class AsyncSysLogService {private static final Logger LOGGER = LoggerFactory.getLogger(AsyncSysLogService.class);private ThreadPoolExecutor logThreadPool;@Autowiredprivate RemoteSysLogService remoteSysLogService;@PostConstructpublic void init() {logThreadPool = new ThreadPoolExecutor(2, 5, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000),new ThreadFactory() {private int count = 1;@Overridepublic Thread newThread(@NonNull Runnable r) {return new Thread(r, "SysLogPool-" + count++);}},new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略);}/*** 异步保存操作日志到数据库中** @param sysLog 操作日志信息*/public void saveLogAsync(SysLog sysLog) {logThreadPool.execute(() -> {LOGGER.info("保存操作日志:{}", sysLog);try {remoteSysLogService.saveLog(sysLog, SecurityConstants.INNER);} catch (Exception e) {LOGGER.error("保存操作日志失败:{}", e.getMessage());}});}
    }
    
http://www.dtcms.com/wzjs/418065.html

相关文章:

  • 建设部网站 挂证链接交换公司
  • 网站建设报告论文seo优化什么意思
  • 开发一个网站的步骤廊坊网站建设优化
  • 网站开发ppt模板win7运行速度提高90%
  • 深圳做电商平台网站建设百度seo收录软件
  • 网站开发支付宝支付网站seo公司哪家好
  • 温州网站制作设计网店推广的作用
  • 电子商务网站建设需求说明书微信营销是什么
  • node.js做的网站成都网站推广哪家专业
  • 免费学校网站模板html武汉seo推广优化公司
  • 做动漫网站的小说百度人气榜排名
  • 武汉网站建设 媒体超市网短视频推广渠道有哪些
  • 做网站用百度地图和天地图如何做公司网站推广
  • 我做的网站打开慢怎么处理东莞疫情最新消息今天中高风险区
  • 如何创建一个新网站排名函数rank怎么用
  • 5118网站如何做推广呢
  • 网站建设步骤 教 程网络推广运营公司
  • 庆阳网站设计师招聘百度官网下载安装到桌面上
  • 网站设计中遇到的问题江门seo
  • 贵阳网站建设电话互联网怎么赚钱
  • 哪些网站做夜场女孩多营销案例100例
  • wordpress主页制作搜索引擎排名优化包括哪些方面
  • 毕业论文美食网站开发青岛谷歌推广
  • 免费足网站重庆网站排名
  • 门户网站上的广告怎么做如何推广一个网站
  • java开发门户网站百度seo排名优化技巧分享
  • 湖南怀化市住房城乡建设局网站百度推广怎么做最好
  • 南宁制作网站多少钱深圳竞价托管公司
  • wordpress媒体库上限简阳seo排名优化课程
  • 部落冲突做弊器网站seo是指搜索引擎优化