perf(backend+frontend): 列表API响应体积优化 3.1MB→145KB (↓95.4%)

- 后端: JPQL构造器投影排除LONGTEXT大字段(uploadedImages/videoReferenceImages)
- 后端: DTO层过滤非分镜图类型的base64内联resultUrl
- 前端: 列表缩略图从video改为img loading=lazy,消除172并发请求
- 前端: download函数增加resultUrl懒加载(详情接口兜底)
- 文档: 新增性能优化报告 docs/performance-optimization-report.md
This commit is contained in:
blandarebiter
2026-04-10 18:46:37 +08:00
commit 90b5118e45
280 changed files with 92468 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
package com.example.demo.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 在线统计服务
* 通过IP地址统计当天访问的独立用户数
*/
@Service
public class OnlineStatsService {
private static final Logger logger = LoggerFactory.getLogger(OnlineStatsService.class);
// 存储当天访问过的IP地址使用Set去重
private final Set<String> todayVisitors = ConcurrentHashMap.newKeySet();
// 记录当前日期,用于判断是否需要重置
private volatile LocalDate currentDate = LocalDate.now();
// 系统启动时间
private final LocalDateTime startTime = LocalDateTime.now();
/**
* 记录访问IP
* @param ip 访问者IP地址
*/
public void recordVisit(String ip) {
if (ip == null || ip.isEmpty()) {
return;
}
// 检查是否需要重置(新的一天)
checkAndResetIfNewDay();
// 添加IP到今日访客列表
todayVisitors.add(ip);
}
/**
* 获取当天在线人数独立IP数
* @return 当天访问的独立IP数量
*/
public int getTodayVisitorCount() {
checkAndResetIfNewDay();
return todayVisitors.size();
}
/**
* 检查是否是新的一天,如果是则重置统计
*/
private void checkAndResetIfNewDay() {
LocalDate today = LocalDate.now();
if (!today.equals(currentDate)) {
synchronized (this) {
if (!today.equals(currentDate)) {
logger.info("新的一天开始,重置在线统计。昨日访客数: {}", todayVisitors.size());
todayVisitors.clear();
currentDate = today;
}
}
}
}
/**
* 每天凌晨0点重置统计
*/
@Scheduled(cron = "0 0 0 * * ?")
public void resetDailyStats() {
logger.info("定时任务:重置每日在线统计。昨日访客数: {}", todayVisitors.size());
todayVisitors.clear();
currentDate = LocalDate.now();
}
/**
* 获取系统运行时间(格式化为 X小时X分
* @return 运行时间字符串
*/
public String getUptime() {
Duration duration = Duration.between(startTime, LocalDateTime.now());
long hours = duration.toHours();
long minutes = duration.toMinutes() % 60;
if (hours > 0) {
return hours + "小时" + minutes + "";
} else {
return minutes + "分钟";
}
}
/**
* 获取统计信息
* @return 包含统计数据的Map
*/
public Map<String, Object> getStats() {
checkAndResetIfNewDay();
Map<String, Object> stats = new ConcurrentHashMap<>();
stats.put("todayVisitors", todayVisitors.size());
stats.put("date", currentDate.toString());
stats.put("uptime", getUptime());
return stats;
}
}