参数校验
This commit is contained in:
parent
d84021f3db
commit
bce338ee19
7
pom.xml
7
pom.xml
|
@ -187,6 +187,13 @@
|
||||||
<version>3.14.0</version>
|
<version>3.14.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!--JSON 处理工具-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>fastjson</artifactId>
|
||||||
|
<version>1.2.62</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.greenorange.promotion.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
/**
|
||||||
|
* 自定义注解记录系统操作日志
|
||||||
|
*/
|
||||||
|
//Target注解决定 MyLog 注解可以加在哪些成分上,如加在类身上,或者属性身上,或者方法身上等成分
|
||||||
|
@Target({ ElementType.PARAMETER, ElementType.METHOD })
|
||||||
|
//Retention注解括号中的"RetentionPolicy.RUNTIME"意思是让 MyLog 这个注解的生命周期一直程序运行时都存在
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface MyLog {
|
||||||
|
/**
|
||||||
|
* 模块标题
|
||||||
|
*/
|
||||||
|
String title() default "";
|
||||||
|
/**
|
||||||
|
* 日志内容
|
||||||
|
*/
|
||||||
|
String content() default "";
|
||||||
|
}
|
280
src/main/java/com/greenorange/promotion/aop/OperLogAspect.java
Normal file
280
src/main/java/com/greenorange/promotion/aop/OperLogAspect.java
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
package com.greenorange.promotion.aop;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.greenorange.promotion.annotation.MyLog;
|
||||||
|
import com.greenorange.promotion.model.entity.SysOperLog;
|
||||||
|
import com.greenorange.promotion.service.log.SysOperLogService;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.*;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切面处理类,记录操作日志到数据库
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
public class OperLogAspect {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SysOperLogService sysOperLogService;
|
||||||
|
|
||||||
|
// 为了记录方法的执行时间
|
||||||
|
ThreadLocal<Long> startTime = new ThreadLocal<>();
|
||||||
|
|
||||||
|
|
||||||
|
@Before("@annotation(myLog)")
|
||||||
|
public void beforeMethod(JoinPoint joinPoint, MyLog myLog){
|
||||||
|
startTime.set(System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
|
||||||
|
* @param joinPoint 切入点
|
||||||
|
* @param result 返回结果
|
||||||
|
*/
|
||||||
|
@AfterReturning(value = "@annotation(myLog)", returning = "result")
|
||||||
|
public void saveOperLog(JoinPoint joinPoint, MyLog myLog, Object result) {
|
||||||
|
// 获取RequestAttributes
|
||||||
|
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||||
|
// 从获取RequestAttributes中获取HttpServletRequest的信息
|
||||||
|
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
|
||||||
|
try {
|
||||||
|
// 从切面织入点处通过反射机制获取织入点处的方法
|
||||||
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||||
|
// 获取切入点所在的方法
|
||||||
|
Method method = signature.getMethod();
|
||||||
|
// 获取操作
|
||||||
|
SysOperLog sysOperLog = new SysOperLog();
|
||||||
|
sysOperLog.setTitle(myLog.title());//设置模块名称
|
||||||
|
sysOperLog.setContent(myLog.content());//设置日志内容
|
||||||
|
// 将入参转换成json
|
||||||
|
String params = argsArrayToString(joinPoint.getArgs());
|
||||||
|
// 获取请求的类名
|
||||||
|
String className = joinPoint.getTarget().getClass().getName();
|
||||||
|
// 获取请求的方法名
|
||||||
|
String methodName = method.getName();
|
||||||
|
methodName = className + "." + methodName + "()";
|
||||||
|
sysOperLog.setMethod(methodName); //设置请求方法
|
||||||
|
sysOperLog.setRequestMethod(request.getMethod());//设置请求方式
|
||||||
|
sysOperLog.setRequestParam(params); // 请求参数
|
||||||
|
sysOperLog.setResponseResult(JSON.toJSONString(result)); // 返回结果
|
||||||
|
// 从request中取出账号
|
||||||
|
String userAccount = (String) request.getAttribute("userAccount");
|
||||||
|
sysOperLog.setOperName(userAccount); // 获取用户名(真实环境中,肯定有工具类获取当前登录者的账号或ID的,或者从token中解析而来)
|
||||||
|
String ip = getIp(request);
|
||||||
|
sysOperLog.setIp(ip); // IP地址
|
||||||
|
String ipLocation = getIpLocation(ip);
|
||||||
|
sysOperLog.setIpLocation(ipLocation); // IP归属地(真是环境中可以调用第三方API根据IP地址,查询归属地)
|
||||||
|
sysOperLog.setRequestUrl(request.getRequestURI()); // 请求URI
|
||||||
|
sysOperLog.setOperTime(new Date()); // 时间
|
||||||
|
sysOperLog.setStatus(0);//操作状态(0正常 1异常)
|
||||||
|
Long takeTime = System.currentTimeMillis() - startTime.get();//记录方法执行耗时时间(单位:毫秒)
|
||||||
|
sysOperLog.setTakeTime(takeTime);
|
||||||
|
//插入数据库
|
||||||
|
sysOperLogService.save(sysOperLog);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置操作异常切入点记录异常日志 扫描所有controller包下操作
|
||||||
|
* 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
|
||||||
|
*/
|
||||||
|
@AfterThrowing(pointcut = "execution(* com.greenorange.promotion.controller..*.*(..))", throwing = "e")
|
||||||
|
public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
|
||||||
|
// 获取RequestAttributes
|
||||||
|
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||||
|
// 从获取RequestAttributes中获取HttpServletRequest的信息
|
||||||
|
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
|
||||||
|
|
||||||
|
SysOperLog sysOperLog = new SysOperLog();
|
||||||
|
try {
|
||||||
|
// 从切面织入点处通过反射机制获取织入点处的方法
|
||||||
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||||
|
// 获取切入点所在的方法
|
||||||
|
Method method = signature.getMethod();
|
||||||
|
// 获取请求的类名
|
||||||
|
String className = joinPoint.getTarget().getClass().getName();
|
||||||
|
// 获取请求的方法名
|
||||||
|
String methodName = method.getName();
|
||||||
|
methodName = className + "." + methodName + "()";
|
||||||
|
// 获取操作
|
||||||
|
MyLog myLog = method.getAnnotation(MyLog.class);
|
||||||
|
if (myLog != null) {
|
||||||
|
sysOperLog.setTitle(myLog.title());//设置模块名称
|
||||||
|
sysOperLog.setContent(myLog.content());//设置日志内容
|
||||||
|
}
|
||||||
|
// 将入参转换成json
|
||||||
|
String params = argsArrayToString(joinPoint.getArgs());
|
||||||
|
sysOperLog.setMethod(methodName); //设置请求方法
|
||||||
|
sysOperLog.setRequestMethod(request.getMethod());//设置请求方式
|
||||||
|
sysOperLog.setRequestParam(params); // 请求参数
|
||||||
|
// 从request中取出账号
|
||||||
|
String userAccount = (String) request.getAttribute("userAccount");
|
||||||
|
sysOperLog.setOperName(userAccount); // 获取用户名(真实环境中,肯定有工具类获取当前登录者的账号或ID的,或者从token中解析而来)
|
||||||
|
String ip = getIp(request);
|
||||||
|
sysOperLog.setIp(ip); // IP地址
|
||||||
|
String ipLocation = getIpLocation(ip);
|
||||||
|
sysOperLog.setIpLocation(ipLocation); // IP归属地(真是环境中可以调用第三方API根据IP地址,查询归属地)
|
||||||
|
sysOperLog.setRequestUrl(request.getRequestURI()); // 请求URI
|
||||||
|
sysOperLog.setOperTime(new Date()); // 时间
|
||||||
|
sysOperLog.setStatus(1);//操作状态(0正常 1异常)
|
||||||
|
sysOperLog.setErrorMsg(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));//记录异常信息
|
||||||
|
//插入数据库
|
||||||
|
sysOperLogService.save(sysOperLog);
|
||||||
|
} catch (Exception e2) {
|
||||||
|
e2.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换异常信息为字符串
|
||||||
|
*/
|
||||||
|
public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
|
||||||
|
StringBuffer strbuff = new StringBuffer();
|
||||||
|
for (StackTraceElement stet : elements) {
|
||||||
|
strbuff.append(stet + "\n");
|
||||||
|
}
|
||||||
|
String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
|
||||||
|
message = substring(message,0 ,2000);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数拼装
|
||||||
|
*/
|
||||||
|
private String argsArrayToString(Object[] paramsArray)
|
||||||
|
{
|
||||||
|
String params = "";
|
||||||
|
if (paramsArray != null && paramsArray.length > 0) {
|
||||||
|
for (Object o : paramsArray) {
|
||||||
|
if (o != null) {
|
||||||
|
try {
|
||||||
|
Object jsonObj = JSON.toJSON(o);
|
||||||
|
params += jsonObj.toString() + " ";
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
//字符串截取
|
||||||
|
public static String substring(String str, int start, int end) {
|
||||||
|
if (str == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
if (end < 0) {
|
||||||
|
end += str.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < 0) {
|
||||||
|
start += str.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end > str.length()) {
|
||||||
|
end = str.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start > end) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
if (start < 0) {
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end < 0) {
|
||||||
|
end = 0;
|
||||||
|
}
|
||||||
|
return str.substring(start, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换request 请求参数
|
||||||
|
* @param paramMap request获取的参数数组
|
||||||
|
*/
|
||||||
|
public Map<String, String> converMap(Map<String, String[]> paramMap) {
|
||||||
|
Map<String, String> returnMap = new HashMap<>();
|
||||||
|
for (String key : paramMap.keySet()) {
|
||||||
|
returnMap.put(key, paramMap.get(key)[0]);
|
||||||
|
}
|
||||||
|
return returnMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据HttpServletRequest获取访问者的IP地址
|
||||||
|
public String getIp(HttpServletRequest request) {
|
||||||
|
String ip = request.getHeader("x-forwarded-for");
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getRemoteAddr();
|
||||||
|
}
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public String getIpLocation(String ip) {
|
||||||
|
try {
|
||||||
|
// 发送请求到ip-api服务
|
||||||
|
String url = "http://ip-api.com/json/" + ip + "?lang=zh-CN"; // 获取中文结果
|
||||||
|
URL obj = new URL(url);
|
||||||
|
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
|
||||||
|
con.setRequestMethod("GET");
|
||||||
|
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
|
||||||
|
String inputLine;
|
||||||
|
StringBuffer response = new StringBuffer();
|
||||||
|
|
||||||
|
while ((inputLine = in.readLine()) != null) {
|
||||||
|
response.append(inputLine);
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
|
||||||
|
// 解析JSON返回结果
|
||||||
|
JSONObject jsonResponse = JSONObject.parseObject(response.toString());
|
||||||
|
String region = jsonResponse.getString("regionName"); // 省
|
||||||
|
String city = jsonResponse.getString("city"); // 城市
|
||||||
|
String country = jsonResponse.getString("country"); // 国家
|
||||||
|
|
||||||
|
return city + ", " + region + ", " + country; // 返回位置
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "未知";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -66,6 +66,8 @@ public class PermissionCheck {
|
||||||
DecodedJWT decodedJWT = jwtUtils.verify(token);
|
DecodedJWT decodedJWT = jwtUtils.verify(token);
|
||||||
String userAccount = decodedJWT.getClaim("userAccount").asString();
|
String userAccount = decodedJWT.getClaim("userAccount").asString();
|
||||||
String userPassword = decodedJWT.getClaim("userPassword").asString();
|
String userPassword = decodedJWT.getClaim("userPassword").asString();
|
||||||
|
// 将账号存入request,用于记录日志
|
||||||
|
request.setAttribute("userAccount", userAccount);
|
||||||
// 打印token的过期时间
|
// 打印token的过期时间
|
||||||
Date expiresAt = decodedJWT.getExpiresAt();
|
Date expiresAt = decodedJWT.getExpiresAt();
|
||||||
String formatExpiresAt = DateUtil.format(expiresAt, "yyyy-MM-dd HH:mm:ss");
|
String formatExpiresAt = DateUtil.format(expiresAt, "yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
|
@ -6,6 +6,8 @@ import com.greenorange.promotion.common.ResultUtils;
|
||||||
import io.swagger.v3.oas.annotations.Hidden;
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.sf.jsqlparser.util.validation.ValidationError;
|
import net.sf.jsqlparser.util.validation.ValidationError;
|
||||||
|
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
|
@ -16,6 +18,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -60,7 +63,6 @@ public class GlobalExceptionHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 处理业务异常
|
// 处理业务异常
|
||||||
@ExceptionHandler(BusinessException.class)
|
@ExceptionHandler(BusinessException.class)
|
||||||
public BaseResponse<?> businessExceptionHandler(BusinessException e) {
|
public BaseResponse<?> businessExceptionHandler(BusinessException e) {
|
||||||
|
@ -69,11 +71,14 @@ public class GlobalExceptionHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// // 处理运行时异常
|
// 处理 SQL 语法错误
|
||||||
// @ExceptionHandler(RuntimeException.class)
|
@ExceptionHandler(SQLException.class)
|
||||||
// public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {
|
public BaseResponse<?> handleSQLException(SQLException ex) {
|
||||||
// log.error("RuntimeException", e);
|
// 记录详细的 SQL 错误信息
|
||||||
// return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统错误");
|
log.error("SQL 错误: " + ex.getMessage());
|
||||||
// }
|
return ResultUtils.error(ErrorCode.DATABASE_ERROR, "SQL语法错误,详细信息:" + ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.greenorange.promotion.mapper;
|
||||||
|
|
||||||
|
import com.greenorange.promotion.model.entity.SysOperLog;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 35880
|
||||||
|
* @description 针对表【sys_oper_log(操作日志记录)】的数据库操作Mapper
|
||||||
|
* @createDate 2025-05-02 20:24:46
|
||||||
|
* @Entity com.greenorange.promotion.model.entity.SysOperLog
|
||||||
|
*/
|
||||||
|
public interface SysOperLogMapper extends BaseMapper<SysOperLog> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package com.greenorange.promotion.model.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作日志记录
|
||||||
|
* @TableName sys_oper_log
|
||||||
|
*/
|
||||||
|
@TableName(value ="sys_oper_log")
|
||||||
|
@Data
|
||||||
|
public class SysOperLog implements Serializable {
|
||||||
|
/**
|
||||||
|
* 日志主键
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法名称
|
||||||
|
*/
|
||||||
|
private String method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求方式
|
||||||
|
*/
|
||||||
|
private String requestMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作人员
|
||||||
|
*/
|
||||||
|
private String operName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求URL
|
||||||
|
*/
|
||||||
|
private String requestUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求IP地址
|
||||||
|
*/
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP归属地
|
||||||
|
*/
|
||||||
|
private String ipLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求参数
|
||||||
|
*/
|
||||||
|
private String requestParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法响应参数
|
||||||
|
*/
|
||||||
|
private String responseResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作状态(0正常 1异常)
|
||||||
|
*/
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误消息
|
||||||
|
*/
|
||||||
|
private String errorMsg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作时间
|
||||||
|
*/
|
||||||
|
private Date operTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法执行耗时(单位:毫秒)
|
||||||
|
*/
|
||||||
|
private Long takeTime;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.greenorange.promotion.service.log;
|
||||||
|
|
||||||
|
import com.greenorange.promotion.model.entity.SysOperLog;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 35880
|
||||||
|
* @description 针对表【sys_oper_log(操作日志记录)】的数据库操作Service
|
||||||
|
* @createDate 2025-05-02 20:24:46
|
||||||
|
*/
|
||||||
|
public interface SysOperLogService extends IService<SysOperLog> {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.greenorange.promotion.service.log.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.greenorange.promotion.model.entity.SysOperLog;
|
||||||
|
import com.greenorange.promotion.service.log.SysOperLogService;
|
||||||
|
import com.greenorange.promotion.mapper.SysOperLogMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 35880
|
||||||
|
* @description 针对表【sys_oper_log(操作日志记录)】的数据库操作Service实现
|
||||||
|
* @createDate 2025-05-02 20:24:46
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class SysOperLogServiceImpl extends ServiceImpl<SysOperLogMapper, SysOperLog>
|
||||||
|
implements SysOperLogService{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
32
src/main/resources/mapper/SysOperLogMapper.xml
Normal file
32
src/main/resources/mapper/SysOperLogMapper.xml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.greenorange.promotion.mapper.SysOperLogMapper">
|
||||||
|
|
||||||
|
<resultMap id="BaseResultMap" type="com.greenorange.promotion.model.entity.SysOperLog">
|
||||||
|
<id property="id" column="id" jdbcType="BIGINT"/>
|
||||||
|
<result property="title" column="title" jdbcType="VARCHAR"/>
|
||||||
|
<result property="content" column="content" jdbcType="VARCHAR"/>
|
||||||
|
<result property="method" column="method" jdbcType="VARCHAR"/>
|
||||||
|
<result property="requestMethod" column="requestMethod" jdbcType="VARCHAR"/>
|
||||||
|
<result property="operName" column="operName" jdbcType="VARCHAR"/>
|
||||||
|
<result property="requestUrl" column="requestUrl" jdbcType="VARCHAR"/>
|
||||||
|
<result property="ip" column="ip" jdbcType="VARCHAR"/>
|
||||||
|
<result property="ipLocation" column="ipLocation" jdbcType="VARCHAR"/>
|
||||||
|
<result property="requestParam" column="requestParam" jdbcType="VARCHAR"/>
|
||||||
|
<result property="responseResult" column="responseResult" jdbcType="VARCHAR"/>
|
||||||
|
<result property="status" column="status" jdbcType="INTEGER"/>
|
||||||
|
<result property="errorMsg" column="errorMsg" jdbcType="VARCHAR"/>
|
||||||
|
<result property="operTime" column="operTime" jdbcType="TIMESTAMP"/>
|
||||||
|
<result property="takeTime" column="takeTime" jdbcType="BIGINT"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="Base_Column_List">
|
||||||
|
id,title,content,
|
||||||
|
method,requestMethod,operName,
|
||||||
|
requestUrl,ip,ipLocation,
|
||||||
|
requestParam,responseResult,status,
|
||||||
|
errorMsg,operTime,takeTime
|
||||||
|
</sql>
|
||||||
|
</mapper>
|
138
src/test/java/com/greenorange/promotion/Client.java
Normal file
138
src/test/java/com/greenorange/promotion/Client.java
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package com.greenorange.promotion;
|
||||||
|
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
|
||||||
|
// Command 接口
|
||||||
|
interface Command {
|
||||||
|
void execute(); // 执行命令
|
||||||
|
void undo(); // 撤销命令
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receiver 类,厨师执行具体的菜品制作命令
|
||||||
|
class Chef {
|
||||||
|
public void makeBurger() {
|
||||||
|
System.out.println("制作汉堡...");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void makeFriedRice() {
|
||||||
|
System.out.println("制作炒饭...");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void makeFriedChicken() {
|
||||||
|
System.out.println("制作炸鸡...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 具体命令类
|
||||||
|
class MakeBurgerCommand implements Command {
|
||||||
|
private Chef chef;
|
||||||
|
|
||||||
|
public MakeBurgerCommand(Chef chef) {
|
||||||
|
this.chef = chef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
chef.makeBurger(); // 执行命令,制作汉堡
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo() {
|
||||||
|
System.out.println("撤销汉堡制作命令...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MakeFriedRiceCommand implements Command {
|
||||||
|
private Chef chef;
|
||||||
|
|
||||||
|
public MakeFriedRiceCommand(Chef chef) {
|
||||||
|
this.chef = chef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
chef.makeFriedRice(); // 执行命令,制作炒饭
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo() {
|
||||||
|
System.out.println("撤销炒饭制作命令...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MakeFriedChickenCommand implements Command {
|
||||||
|
private Chef chef;
|
||||||
|
|
||||||
|
public MakeFriedChickenCommand(Chef chef) {
|
||||||
|
this.chef = chef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
chef.makeFriedChicken(); // 执行命令,制作炸鸡
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo() {
|
||||||
|
System.out.println("撤销炸鸡制作命令...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 服务员类,接收并执行命令
|
||||||
|
class Waiter {
|
||||||
|
private Stack<Command> commandHistory = new Stack<>(); // 用栈来保存历史命令,方便撤销
|
||||||
|
|
||||||
|
public void takeOrder(Command command) {
|
||||||
|
command.execute(); // 执行命令
|
||||||
|
commandHistory.push(command); // 将命令存入历史记录
|
||||||
|
}
|
||||||
|
|
||||||
|
public void undoLastOrder() {
|
||||||
|
if (!commandHistory.isEmpty()) {
|
||||||
|
Command lastCommand = commandHistory.pop(); // 获取并移除栈顶命令
|
||||||
|
lastCommand.undo(); // 执行撤销操作
|
||||||
|
} else {
|
||||||
|
System.out.println("没有可撤销的命令!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 宏命令:一次性下多个菜品
|
||||||
|
public void placeMultipleOrders(Command[] commands) {
|
||||||
|
for (Command command : commands) {
|
||||||
|
takeOrder(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 客户端模拟点餐过程
|
||||||
|
public class Client {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 创建厨师
|
||||||
|
Chef chef = new Chef();
|
||||||
|
|
||||||
|
// 创建具体命令
|
||||||
|
Command makeBurger = new MakeBurgerCommand(chef);
|
||||||
|
Command makeFriedRice = new MakeFriedRiceCommand(chef);
|
||||||
|
Command makeFriedChicken = new MakeFriedChickenCommand(chef);
|
||||||
|
|
||||||
|
// 创建服务员
|
||||||
|
Waiter waiter = new Waiter();
|
||||||
|
|
||||||
|
// 用户下单
|
||||||
|
waiter.takeOrder(makeBurger);
|
||||||
|
waiter.takeOrder(makeFriedRice);
|
||||||
|
|
||||||
|
// 用户撤销上一条订单(炒饭)
|
||||||
|
waiter.undoLastOrder();
|
||||||
|
|
||||||
|
// 用户一次性下多个菜品(宏命令)
|
||||||
|
Command[] orders = {makeBurger, makeFriedChicken};
|
||||||
|
waiter.placeMultipleOrders(orders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
48
src/test/java/com/greenorange/promotion/IpDemo.java
Normal file
48
src/test/java/com/greenorange/promotion/IpDemo.java
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package com.greenorange.promotion;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class IpDemo {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
getIpLocation("123.167.57.119");
|
||||||
|
}
|
||||||
|
public static String getIpLocation(String ip) {
|
||||||
|
try {
|
||||||
|
// 发送请求到ip-api服务
|
||||||
|
String url = "http://ip-api.com/json/" + ip + "?lang=zh-CN"; // 获取中文结果
|
||||||
|
URL obj = new URL(url);
|
||||||
|
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
|
||||||
|
con.setRequestMethod("GET");
|
||||||
|
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
|
||||||
|
String inputLine;
|
||||||
|
StringBuffer response = new StringBuffer();
|
||||||
|
|
||||||
|
while ((inputLine = in.readLine()) != null) {
|
||||||
|
response.append(inputLine);
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
|
||||||
|
// 解析JSON返回结果
|
||||||
|
JSONObject jsonResponse = JSONObject.parseObject(response.toString());
|
||||||
|
String country = jsonResponse.getString("country"); // 国家
|
||||||
|
String region = jsonResponse.getString("regionName"); // 省
|
||||||
|
String city = jsonResponse.getString("city"); // 城市
|
||||||
|
System.out.println(jsonResponse);
|
||||||
|
System.out.println(country + "-" + region + "省" + "-" + city);
|
||||||
|
return country + "-" + region + "省" + "-" + city; // 返回位置
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "未知";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user