From bce338ee1982a96070bcce57f75156ffc920b642 Mon Sep 17 00:00:00 2001 From: chen-xin-zhi <3588068430@qq.com> Date: Sat, 3 May 2025 10:20:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 + .../promotion/annotation/MyLog.java | 20 ++ .../promotion/aop/OperLogAspect.java | 280 ++++++++++++++++++ .../promotion/aop/PermissionCheck.java | 2 + .../exception/GlobalExceptionHandler.java | 19 +- .../promotion/mapper/SysOperLogMapper.java | 18 ++ .../promotion/model/entity/SysOperLog.java | 96 ++++++ .../service/log/SysOperLogService.java | 13 + .../log/impl/SysOperLogServiceImpl.java | 22 ++ .../resources/mapper/SysOperLogMapper.xml | 32 ++ .../com/greenorange/promotion/Client.java | 138 +++++++++ .../com/greenorange/promotion/IpDemo.java | 48 +++ 12 files changed, 688 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/greenorange/promotion/annotation/MyLog.java create mode 100644 src/main/java/com/greenorange/promotion/aop/OperLogAspect.java create mode 100644 src/main/java/com/greenorange/promotion/mapper/SysOperLogMapper.java create mode 100644 src/main/java/com/greenorange/promotion/model/entity/SysOperLog.java create mode 100644 src/main/java/com/greenorange/promotion/service/log/SysOperLogService.java create mode 100644 src/main/java/com/greenorange/promotion/service/log/impl/SysOperLogServiceImpl.java create mode 100644 src/main/resources/mapper/SysOperLogMapper.xml create mode 100644 src/test/java/com/greenorange/promotion/Client.java create mode 100644 src/test/java/com/greenorange/promotion/IpDemo.java diff --git a/pom.xml b/pom.xml index 2ecd2c2..0af57de 100644 --- a/pom.xml +++ b/pom.xml @@ -187,6 +187,13 @@ 3.14.0 + + + com.alibaba + fastjson + 1.2.62 + + diff --git a/src/main/java/com/greenorange/promotion/annotation/MyLog.java b/src/main/java/com/greenorange/promotion/annotation/MyLog.java new file mode 100644 index 0000000..0a28fc6 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/annotation/MyLog.java @@ -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 ""; +} \ No newline at end of file diff --git a/src/main/java/com/greenorange/promotion/aop/OperLogAspect.java b/src/main/java/com/greenorange/promotion/aop/OperLogAspect.java new file mode 100644 index 0000000..af6f8a6 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/aop/OperLogAspect.java @@ -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 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 converMap(Map paramMap) { + Map 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 "未知"; + } + +} \ No newline at end of file diff --git a/src/main/java/com/greenorange/promotion/aop/PermissionCheck.java b/src/main/java/com/greenorange/promotion/aop/PermissionCheck.java index 66ff732..3675e3e 100644 --- a/src/main/java/com/greenorange/promotion/aop/PermissionCheck.java +++ b/src/main/java/com/greenorange/promotion/aop/PermissionCheck.java @@ -66,6 +66,8 @@ public class PermissionCheck { DecodedJWT decodedJWT = jwtUtils.verify(token); String userAccount = decodedJWT.getClaim("userAccount").asString(); String userPassword = decodedJWT.getClaim("userPassword").asString(); + // 将账号存入request,用于记录日志 + request.setAttribute("userAccount", userAccount); // 打印token的过期时间 Date expiresAt = decodedJWT.getExpiresAt(); String formatExpiresAt = DateUtil.format(expiresAt, "yyyy-MM-dd HH:mm:ss"); diff --git a/src/main/java/com/greenorange/promotion/exception/GlobalExceptionHandler.java b/src/main/java/com/greenorange/promotion/exception/GlobalExceptionHandler.java index 051d6c0..a5c1491 100644 --- a/src/main/java/com/greenorange/promotion/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/greenorange/promotion/exception/GlobalExceptionHandler.java @@ -6,6 +6,8 @@ import com.greenorange.promotion.common.ResultUtils; import io.swagger.v3.oas.annotations.Hidden; import lombok.extern.slf4j.Slf4j; 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.converter.HttpMessageNotReadableException; 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.method.annotation.MethodArgumentTypeMismatchException; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -60,7 +63,6 @@ public class GlobalExceptionHandler { } - // 处理业务异常 @ExceptionHandler(BusinessException.class) public BaseResponse businessExceptionHandler(BusinessException e) { @@ -69,11 +71,14 @@ public class GlobalExceptionHandler { } -// // 处理运行时异常 -// @ExceptionHandler(RuntimeException.class) -// public BaseResponse runtimeExceptionHandler(RuntimeException e) { -// log.error("RuntimeException", e); -// return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统错误"); -// } + // 处理 SQL 语法错误 + @ExceptionHandler(SQLException.class) + public BaseResponse handleSQLException(SQLException ex) { + // 记录详细的 SQL 错误信息 + log.error("SQL 错误: " + ex.getMessage()); + return ResultUtils.error(ErrorCode.DATABASE_ERROR, "SQL语法错误,详细信息:" + ex.getMessage()); + } + + } diff --git a/src/main/java/com/greenorange/promotion/mapper/SysOperLogMapper.java b/src/main/java/com/greenorange/promotion/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..14e8402 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/mapper/SysOperLogMapper.java @@ -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 { + +} + + + + diff --git a/src/main/java/com/greenorange/promotion/model/entity/SysOperLog.java b/src/main/java/com/greenorange/promotion/model/entity/SysOperLog.java new file mode 100644 index 0000000..b7657a6 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/model/entity/SysOperLog.java @@ -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; +} \ No newline at end of file diff --git a/src/main/java/com/greenorange/promotion/service/log/SysOperLogService.java b/src/main/java/com/greenorange/promotion/service/log/SysOperLogService.java new file mode 100644 index 0000000..898aa74 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/service/log/SysOperLogService.java @@ -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 { + +} diff --git a/src/main/java/com/greenorange/promotion/service/log/impl/SysOperLogServiceImpl.java b/src/main/java/com/greenorange/promotion/service/log/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..eab9c4a --- /dev/null +++ b/src/main/java/com/greenorange/promotion/service/log/impl/SysOperLogServiceImpl.java @@ -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 + implements SysOperLogService{ + +} + + + + diff --git a/src/main/resources/mapper/SysOperLogMapper.xml b/src/main/resources/mapper/SysOperLogMapper.xml new file mode 100644 index 0000000..1fe1c91 --- /dev/null +++ b/src/main/resources/mapper/SysOperLogMapper.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + id,title,content, + method,requestMethod,operName, + requestUrl,ip,ipLocation, + requestParam,responseResult,status, + errorMsg,operTime,takeTime + + diff --git a/src/test/java/com/greenorange/promotion/Client.java b/src/test/java/com/greenorange/promotion/Client.java new file mode 100644 index 0000000..057a4c6 --- /dev/null +++ b/src/test/java/com/greenorange/promotion/Client.java @@ -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 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); + } +} + diff --git a/src/test/java/com/greenorange/promotion/IpDemo.java b/src/test/java/com/greenorange/promotion/IpDemo.java new file mode 100644 index 0000000..1939e8c --- /dev/null +++ b/src/test/java/com/greenorange/promotion/IpDemo.java @@ -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 "未知"; + } + + +}