From e9747e6af23b944f6f1beeb26dcb9dbdb9fcad6d Mon Sep 17 00:00:00 2001 From: Leo Date: Sat, 3 Jan 2026 12:20:09 +0800 Subject: [PATCH] 1 --- .../publicapi/CommentPublicController.java | 105 +++++++++++- .../jarvis/domain/dto/CommentCallHistory.java | 21 +++ .../ruoyi/jarvis/service/ICommentService.java | 16 ++ .../service/impl/CommentServiceImpl.java | 152 ++++++++++++++++++ 4 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/dto/CommentCallHistory.java diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/publicapi/CommentPublicController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/publicapi/CommentPublicController.java index f02b80a..84d26de 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/publicapi/CommentPublicController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/publicapi/CommentPublicController.java @@ -3,14 +3,17 @@ package com.ruoyi.web.controller.publicapi; import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.ruoyi.common.utils.http.HttpUtils; +import com.ruoyi.jarvis.domain.dto.CommentCallHistory; import com.ruoyi.jarvis.service.ICommentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; import java.util.*; /** @@ -66,9 +69,10 @@ public class CommentPublicController extends BaseController { * 入参:productType(型号/类型) */ @PostMapping("/generate") - public AjaxResult generate(@RequestBody Map body) { + public AjaxResult generate(@RequestBody Map body, HttpServletRequest request) { boolean success = false; String productType = null; + String clientIp = getClientIp(request); try { String url = getJdBase() + "/comment/generate"; JSONObject param = new JSONObject(); @@ -84,13 +88,110 @@ public class CommentPublicController extends BaseController { } catch (Exception e) { return AjaxResult.error("generate failed: " + e.getMessage()); } finally { - // 记录接口调用统计 + // 记录接口调用统计和历史 if (commentService != null && productType != null) { commentService.recordApiCall("jd", productType, success); + commentService.recordApiCallHistory(productType, clientIp); } } } + /** + * 获取当前IP地址 + */ + @GetMapping("/ip") + public AjaxResult getIp(HttpServletRequest request) { + try { + String ip = getClientIp(request); + Map result = new HashMap<>(); + result.put("ip", ip); + return AjaxResult.success(result); + } catch (Exception e) { + return AjaxResult.error("获取IP失败: " + e.getMessage()); + } + } + + /** + * 获取使用统计(今天/7天/30天/累计) + */ + @GetMapping("/usage-statistics") + public AjaxResult getUsageStatistics() { + try { + if (commentService != null) { + Map statistics = commentService.getUsageStatistics(); + return AjaxResult.success(statistics); + } else { + Map statistics = new HashMap<>(); + statistics.put("today", 0L); + statistics.put("last7Days", 0L); + statistics.put("last30Days", 0L); + statistics.put("total", 0L); + return AjaxResult.success(statistics); + } + } catch (Exception e) { + return AjaxResult.error("获取使用统计失败: " + e.getMessage()); + } + } + + /** + * 获取历史记录 + */ + @GetMapping("/history") + public TableDataInfo getHistory(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum, + @RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) { + try { + if (commentService != null) { + List historyList = commentService.getApiCallHistory(pageNum, pageSize); + TableDataInfo dataTable = new TableDataInfo(); + dataTable.setCode(200); + dataTable.setMsg("查询成功"); + dataTable.setRows(historyList); + dataTable.setTotal(historyList.size()); // 注意:这里返回的是当前页的数量,实际总数可能需要单独查询 + return dataTable; + } else { + TableDataInfo dataTable = new TableDataInfo(); + dataTable.setCode(200); + dataTable.setMsg("查询成功"); + dataTable.setRows(new ArrayList<>()); + dataTable.setTotal(0); + return dataTable; + } + } catch (Exception e) { + TableDataInfo dataTable = new TableDataInfo(); + dataTable.setCode(500); + dataTable.setMsg("获取历史记录失败: " + e.getMessage()); + return dataTable; + } + } + + /** + * 获取客户端真实IP地址 + * 考虑代理和负载均衡的情况 + */ + private String getClientIp(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("HTTP_CLIENT_IP"); + } + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + // 对于通过多个代理的情况,第一个IP为客户端真实IP + if (ip != null && ip.contains(",")) { + ip = ip.substring(0, ip.indexOf(",")).trim(); + } + return ip; + } + } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/dto/CommentCallHistory.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/dto/CommentCallHistory.java new file mode 100644 index 0000000..806c44f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/dto/CommentCallHistory.java @@ -0,0 +1,21 @@ +package com.ruoyi.jarvis.domain.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * 评论接口调用历史记录 + */ +@Data +public class CommentCallHistory { + /** 产品类型 */ + private String productType; + + /** IP地址 */ + private String ip; + + /** 创建时间 */ + private Date createTime; +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ICommentService.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ICommentService.java index eaaf328..40456b1 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ICommentService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ICommentService.java @@ -3,6 +3,7 @@ package com.ruoyi.jarvis.service; import com.ruoyi.jarvis.domain.Comment; import com.ruoyi.jarvis.domain.dto.CommentStatistics; import com.ruoyi.jarvis.domain.dto.CommentApiStatistics; +import com.ruoyi.jarvis.domain.dto.CommentCallHistory; import java.util.List; import java.util.Map; @@ -47,6 +48,21 @@ public interface ICommentService { */ void recordApiCall(String apiType, String productType, boolean success); + /** + * 记录接口调用历史(带IP) + */ + void recordApiCallHistory(String productType, String ip); + + /** + * 获取接口调用历史记录 + */ + List getApiCallHistory(int pageNum, int pageSize); + + /** + * 获取使用统计(今天/7天/30天/累计) + */ + Map getUsageStatistics(); + /** * 获取接口调用统计 */ diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/CommentServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/CommentServiceImpl.java index baa5b49..417e3b6 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/CommentServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/CommentServiceImpl.java @@ -3,8 +3,10 @@ package com.ruoyi.jarvis.service.impl; import com.ruoyi.jarvis.domain.Comment; import com.ruoyi.jarvis.domain.dto.CommentApiStatistics; import com.ruoyi.jarvis.domain.dto.CommentStatistics; +import com.ruoyi.jarvis.domain.dto.CommentCallHistory; import com.ruoyi.jarvis.mapper.CommentMapper; import com.ruoyi.jarvis.service.ICommentService; +import com.alibaba.fastjson2.JSON; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +29,8 @@ public class CommentServiceImpl implements ICommentService { private static final String PRODUCT_TYPE_MAP_PREFIX_TB = "product_type_map_tb"; private static final String API_CALL_STAT_PREFIX = "comment:api:stat:"; private static final String API_CALL_TODAY_PREFIX = "comment:api:today:"; + private static final String API_CALL_HISTORY_KEY = "comment:api:history:list"; + private static final int MAX_HISTORY_SIZE = 1000; // 最多保留1000条历史记录 @Autowired private CommentMapper commentMapper; @@ -278,5 +282,153 @@ public class CommentServiceImpl implements ICommentService { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); return sdf.format(new Date()); } + + @Override + public void recordApiCallHistory(String productType, String ip) { + if (stringRedisTemplate == null) { + return; + } + + try { + CommentCallHistory history = new CommentCallHistory(); + history.setProductType(productType); + history.setIp(ip); + history.setCreateTime(new Date()); + + String historyJson = JSON.toJSONString(history); + + // 使用List存储历史记录,从左侧推入 + stringRedisTemplate.opsForList().leftPush(API_CALL_HISTORY_KEY, historyJson); + + // 限制列表大小,只保留最近MAX_HISTORY_SIZE条 + stringRedisTemplate.opsForList().trim(API_CALL_HISTORY_KEY, 0, MAX_HISTORY_SIZE - 1); + } catch (Exception e) { + log.error("记录接口调用历史失败", e); + } + } + + @Override + public List getApiCallHistory(int pageNum, int pageSize) { + List historyList = new ArrayList<>(); + + if (stringRedisTemplate == null) { + return historyList; + } + + try { + long start = (pageNum - 1) * pageSize; + long end = start + pageSize - 1; + + List jsonList = stringRedisTemplate.opsForList().range(API_CALL_HISTORY_KEY, start, end); + if (jsonList != null) { + for (String json : jsonList) { + try { + CommentCallHistory history = JSON.parseObject(json, CommentCallHistory.class); + historyList.add(history); + } catch (Exception e) { + log.warn("解析历史记录失败: " + json, e); + } + } + } + } catch (Exception e) { + log.error("获取接口调用历史失败", e); + } + + return historyList; + } + + @Override + public Map getUsageStatistics() { + Map statistics = new HashMap<>(); + statistics.put("today", 0L); + statistics.put("last7Days", 0L); + statistics.put("last30Days", 0L); + statistics.put("total", 0L); + + if (stringRedisTemplate == null) { + return statistics; + } + + try { + String today = getTodayDate(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + Date todayDate = sdf.parse(today); + Calendar calendar = Calendar.getInstance(); + + // 统计今天 + long todayCount = 0; + String todayPattern = API_CALL_TODAY_PREFIX + "jd:*:" + today; + Set todayKeys = stringRedisTemplate.keys(todayPattern); + if (todayKeys != null) { + for (String key : todayKeys) { + String count = stringRedisTemplate.opsForValue().get(key); + if (count != null) { + todayCount += Long.parseLong(count); + } + } + } + statistics.put("today", todayCount); + + // 统计近7天 + long last7DaysCount = 0; + calendar.setTime(todayDate); + for (int i = 0; i < 7; i++) { + String dateStr = sdf.format(calendar.getTime()); + String pattern = API_CALL_TODAY_PREFIX + "jd:*:" + dateStr; + Set keys = stringRedisTemplate.keys(pattern); + if (keys != null) { + for (String key : keys) { + String count = stringRedisTemplate.opsForValue().get(key); + if (count != null) { + last7DaysCount += Long.parseLong(count); + } + } + } + calendar.add(Calendar.DAY_OF_MONTH, -1); + } + statistics.put("last7Days", last7DaysCount); + + // 统计近30天 + long last30DaysCount = 0; + calendar.setTime(todayDate); + for (int i = 0; i < 30; i++) { + String dateStr = sdf.format(calendar.getTime()); + String pattern = API_CALL_TODAY_PREFIX + "jd:*:" + dateStr; + Set keys = stringRedisTemplate.keys(pattern); + if (keys != null) { + for (String key : keys) { + String count = stringRedisTemplate.opsForValue().get(key); + if (count != null) { + last30DaysCount += Long.parseLong(count); + } + } + } + calendar.add(Calendar.DAY_OF_MONTH, -1); + } + statistics.put("last30Days", last30DaysCount); + + // 统计累计(从统计key获取) + long totalCount = 0; + String totalPattern = API_CALL_STAT_PREFIX + "jd:*"; + Set totalKeys = stringRedisTemplate.keys(totalPattern); + if (totalKeys != null) { + for (String key : totalKeys) { + // 排除success和fail后缀的key + if (!key.endsWith(":success") && !key.endsWith(":fail")) { + String count = stringRedisTemplate.opsForValue().get(key); + if (count != null) { + totalCount += Long.parseLong(count); + } + } + } + } + statistics.put("total", totalCount); + + } catch (Exception e) { + log.error("获取使用统计失败", e); + } + + return statistics; + } }