refactor: 后端核心模块功能增强与代码优化

- 完善 User/Product/Order 实体字段和关联关系
- 更新 DTO 适配新增字段
- 增强 Service 层业务逻辑和 Repository 查询方法
- 优化控制器接口,完善管理后台 API
- 新增请求监控过滤器和指标服务
This commit is contained in:
2026-03-10 23:18:08 +08:00
parent 977db8f333
commit 6788fcd5ea
21 changed files with 1194 additions and 120 deletions

View File

@@ -3,10 +3,15 @@ package com.org.flashsalesystem.service;
import com.org.flashsalesystem.dto.UserDTO;
import com.org.flashsalesystem.entity.Order;
import com.org.flashsalesystem.entity.Product;
import com.org.flashsalesystem.entity.ProductReview;
import com.org.flashsalesystem.entity.User;
import com.org.flashsalesystem.entity.UserFavorite;
import com.org.flashsalesystem.repository.FlashSaleRepository;
import com.org.flashsalesystem.repository.OrderItemRepository;
import com.org.flashsalesystem.repository.OrderRepository;
import com.org.flashsalesystem.repository.ProductRepository;
import com.org.flashsalesystem.repository.ProductReviewRepository;
import com.org.flashsalesystem.repository.UserFavoriteRepository;
import com.org.flashsalesystem.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
@@ -17,6 +22,9 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.io.File;
import java.lang.management.ManagementFactory;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -40,12 +48,27 @@ public class AdminService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private OrderItemRepository orderItemRepository;
@Autowired
private ProductReviewRepository productReviewRepository;
@Autowired
private UserFavoriteRepository userFavoriteRepository;
@Autowired
private FlashSaleRepository flashSaleRepository;
@Autowired
private RedisService redisService;
@Autowired
private DataSource dataSource;
@Autowired
private RequestMetricsService requestMetricsService;
/**
* 获取仪表盘统计数据
*/
@@ -315,6 +338,7 @@ public class AdminService {
productMap.put("id", product.getId());
productMap.put("name", product.getName());
productMap.put("price", product.getPrice());
productMap.put("category", product.getCategory());
productMap.put("stock", product.getStock());
productMap.put("sales", 0); // 暂时设为0后续可以添加销量统计
return productMap;
@@ -347,6 +371,8 @@ public class AdminService {
BeanUtils.copyProperties(user, dto);
dto.setPassword(null); // 不返回密码
dto.setIsOnline(redisService.sIsMember("online_users", user.getId().toString()));
dto.setRole(user.getRole() == null ? ("admin".equalsIgnoreCase(user.getUsername()) ? "ADMIN" : "USER") : user.getRole());
dto.setAvatar("");
return dto;
}).collect(Collectors.toList());
@@ -425,21 +451,19 @@ public class AdminService {
/**
* 获取商品列表
*/
public Object getProducts(int page, int size, String keyword, Integer status) {
public Object getProducts(int page, int size, String keyword, String category, Integer status) {
try {
Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createdAt"));
Page<Product> productPage;
if (keyword != null && !keyword.trim().isEmpty() && status != null) {
// 同时按关键词和状态筛选
productPage = productRepository.findByNameContainingAndStatus(keyword, status, pageable);
} else if (keyword != null && !keyword.trim().isEmpty()) {
productPage = productRepository.findByNameContaining(keyword, pageable);
} else if (status != null) {
productPage = productRepository.findByStatus(status, pageable);
} else {
productPage = productRepository.findAll(pageable);
}
productPage = productRepository.searchProducts(
status,
keyword != null && !keyword.trim().isEmpty() ? keyword.trim() : null,
category != null && !category.trim().isEmpty() ? category.trim() : null,
null,
null,
pageable
);
// 转换为DTO
List<Map<String, Object>> productList = productPage.getContent().stream().map(product -> {
@@ -447,6 +471,7 @@ public class AdminService {
productMap.put("id", product.getId());
productMap.put("name", product.getName());
productMap.put("price", product.getPrice());
productMap.put("category", product.getCategory());
productMap.put("stock", product.getStock());
productMap.put("status", product.getStatus());
productMap.put("description", product.getDescription());
@@ -482,27 +507,49 @@ public class AdminService {
try {
Map<String, Object> systemStatus = new HashMap<>();
// 获取JVM内存使用情况
Runtime runtime = Runtime.getRuntime();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
double memoryUsage = (double) usedMemory / totalMemory * 100;
double memoryUsage = totalMemory == 0 ? 0 : (double) usedMemory / totalMemory * 100;
// 获取可用处理器数量模拟CPU使用率
int availableProcessors = runtime.availableProcessors();
double cpuUsage = Math.random() * 30 + 20; // 模拟20-50%的CPU使用率
double cpuUsage = 0;
java.lang.management.OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
if (osBean instanceof com.sun.management.OperatingSystemMXBean) {
cpuUsage = ((com.sun.management.OperatingSystemMXBean) osBean).getSystemCpuLoad() * 100;
if (cpuUsage < 0) {
cpuUsage = 0;
}
}
// 模拟磁盘使用率
double diskUsage = Math.random() * 40 + 10; // 模拟10-50%的磁盘使用率
File root = new File("/");
long totalSpace = root.getTotalSpace();
long usableSpace = root.getUsableSpace();
long usedSpace = totalSpace - usableSpace;
double diskUsage = totalSpace == 0 ? 0 : (double) usedSpace / totalSpace * 100;
systemStatus.put("status", "正常");
boolean dbHealthy = false;
try (java.sql.Connection connection = dataSource.getConnection()) {
dbHealthy = connection.isValid(2);
}
String redisPing = redisService.ping();
boolean redisHealthy = "PONG".equalsIgnoreCase(redisPing);
String requestCountKey = "request_count:" + LocalDate.now().format(java.time.format.DateTimeFormatter.BASIC_ISO_DATE);
Object requestCountValue = redisService.get(requestCountKey);
long requestCountToday = requestCountValue == null ? requestMetricsService.getTotalRequests() : Long.parseLong(requestCountValue.toString());
systemStatus.put("status", dbHealthy && redisHealthy ? "正常" : "异常");
systemStatus.put("cpuUsage", Math.round(cpuUsage));
systemStatus.put("memoryUsage", Math.round(memoryUsage));
systemStatus.put("diskUsage", Math.round(diskUsage));
systemStatus.put("availableProcessors", availableProcessors);
systemStatus.put("availableProcessors", runtime.availableProcessors());
systemStatus.put("totalMemory", totalMemory / 1024 / 1024 + "MB");
systemStatus.put("usedMemory", usedMemory / 1024 / 1024 + "MB");
systemStatus.put("dbStatus", dbHealthy ? "正常" : "异常");
systemStatus.put("redisStatus", redisHealthy ? "正常" : "异常");
systemStatus.put("requestCountToday", requestCountToday);
return systemStatus;
} catch (Exception e) {
@@ -512,6 +559,9 @@ public class AdminService {
errorStatus.put("cpuUsage", 0);
errorStatus.put("memoryUsage", 0);
errorStatus.put("diskUsage", 0);
errorStatus.put("dbStatus", "异常");
errorStatus.put("redisStatus", "异常");
errorStatus.put("requestCountToday", requestMetricsService.getTotalRequests());
return errorStatus;
}
}
@@ -522,21 +572,16 @@ public class AdminService {
public Object getRedisStatus() {
try {
List<Map<String, Object>> redisNodes = new ArrayList<>();
Properties memoryInfo = redisService.info("memory");
Properties clientInfo = redisService.info("clients");
String ping = redisService.ping();
// 模拟Redis集群节点状态
String[] nodes = {
"42.192.62.91:7000", "42.192.62.91:7001", "42.192.62.91:7002",
"42.192.62.91:7003", "42.192.62.91:7004", "42.192.62.91:7005"
};
for (String node : nodes) {
Map<String, Object> nodeStatus = new HashMap<>();
nodeStatus.put("node", node);
nodeStatus.put("status", "正常");
nodeStatus.put("memory", (200 + (int) (Math.random() * 100)) + "MB");
nodeStatus.put("connections", 30 + (int) (Math.random() * 30));
redisNodes.add(nodeStatus);
}
Map<String, Object> nodeStatus = new HashMap<>();
nodeStatus.put("node", "default");
nodeStatus.put("status", "PONG".equalsIgnoreCase(ping) ? "正常" : "异常");
nodeStatus.put("memory", memoryInfo.getProperty("used_memory_human", "unknown"));
nodeStatus.put("connections", Integer.parseInt(clientInfo.getProperty("connected_clients", "0")));
redisNodes.add(nodeStatus);
return redisNodes;
} catch (Exception e) {
@@ -564,11 +609,16 @@ public class AdminService {
productMap.put("createdAt", product.getCreatedAt());
productMap.put("updatedAt", product.getUpdatedAt());
// 添加统计信息(模拟数据,实际应该从统计表获取)
productMap.put("totalSales", 0); // 总销量
productMap.put("totalRevenue", 0.0); // 总收入
productMap.put("viewCount", 0); // 浏览次数
productMap.put("rating", 0.0); // 平均评分
long totalSales = orderItemRepository.countByProductId(product.getId());
java.math.BigDecimal totalRevenue = orderItemRepository.sumSubtotalByProductId(product.getId());
Double averageRating = productReviewRepository.findAverageRatingByProductId(product.getId());
long reviewCount = productReviewRepository.countByProductId(product.getId());
productMap.put("totalSales", totalSales);
productMap.put("totalRevenue", totalRevenue == null ? 0.0 : totalRevenue);
productMap.put("viewCount", 0);
productMap.put("rating", averageRating == null ? 0.0 : averageRating);
productMap.put("reviewCount", reviewCount);
return productMap;
} else {
@@ -598,6 +648,9 @@ public class AdminService {
if (productData.containsKey("stock")) {
product.setStock(Integer.parseInt(productData.get("stock").toString()));
}
if (productData.containsKey("category")) {
product.setCategory((String) productData.get("category"));
}
if (productData.containsKey("status")) {
product.setStatus(Integer.parseInt(productData.get("status").toString()));
}
@@ -643,6 +696,7 @@ public class AdminService {
Product product = new Product();
product.setName((String) productData.get("name"));
product.setPrice(new BigDecimal(productData.get("price").toString()));
product.setCategory((String) productData.getOrDefault("category", "默认分类"));
product.setStock(Integer.parseInt(productData.get("stock").toString()));
product.setStatus(Integer.parseInt(productData.get("status").toString()));
product.setDescription((String) productData.get("description"));
@@ -656,6 +710,7 @@ public class AdminService {
result.put("id", savedProduct.getId());
result.put("name", savedProduct.getName());
result.put("price", savedProduct.getPrice());
result.put("category", savedProduct.getCategory());
result.put("stock", savedProduct.getStock());
result.put("status", savedProduct.getStatus());
result.put("description", savedProduct.getDescription());
@@ -668,4 +723,133 @@ public class AdminService {
throw new RuntimeException("添加商品失败: " + e.getMessage());
}
}
public Map<String, Object> getReviewStats() {
Map<String, Object> stats = new HashMap<>();
List<ProductReview> reviews = productReviewRepository.findAll();
LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
long todayReviews = reviews.stream().filter(item -> item.getCreatedAt() != null && item.getCreatedAt().isAfter(startOfDay)).count();
double averageRating = reviews.isEmpty() ? 0.0 : reviews.stream().mapToInt(ProductReview::getRating).average().orElse(0.0);
stats.put("totalReviews", reviews.size());
stats.put("todayReviews", todayReviews);
stats.put("averageRating", averageRating);
stats.put("fiveStarReviews", reviews.stream().filter(item -> item.getRating() == 5).count());
return stats;
}
public Map<String, Object> getFavoriteStats() {
Map<String, Object> stats = new HashMap<>();
List<UserFavorite> favorites = userFavoriteRepository.findAll();
stats.put("totalFavorites", favorites.size());
stats.put("favoriteUsers", favorites.stream().map(UserFavorite::getUserId).distinct().count());
stats.put("favoriteProducts", favorites.stream().map(UserFavorite::getProductId).distinct().count());
stats.put("todayFavorites", favorites.stream().filter(item -> item.getCreatedAt() != null && item.getCreatedAt().isAfter(LocalDate.now().atStartOfDay())).count());
return stats;
}
public Map<String, Object> getReviews(int page, int size, String keyword) {
List<Map<String, Object>> rows = productReviewRepository.findAll(Sort.by(Sort.Direction.DESC, "createdAt"))
.stream()
.map(review -> {
Map<String, Object> item = new HashMap<>();
item.put("id", review.getId());
item.put("productId", review.getProductId());
item.put("userId", review.getUserId());
item.put("orderId", review.getOrderId());
item.put("rating", review.getRating());
item.put("content", review.getContent());
item.put("status", review.getStatus());
item.put("statusText", review.getStatus() != null && review.getStatus() == 1 ? "显示" : "隐藏");
item.put("adminReply", review.getAdminReply());
item.put("repliedAt", review.getRepliedAt());
item.put("createdAt", review.getCreatedAt());
Product product = productRepository.findById(review.getProductId()).orElse(null);
User user = userRepository.findById(review.getUserId()).orElse(null);
item.put("productName", product != null ? product.getName() : "未知商品");
item.put("username", user != null ? user.getUsername() : "未知用户");
return item;
})
.filter(item -> {
if (keyword == null || keyword.trim().isEmpty()) return true;
String value = keyword.trim().toLowerCase();
return String.valueOf(item.get("productName")).toLowerCase().contains(value)
|| String.valueOf(item.get("username")).toLowerCase().contains(value)
|| String.valueOf(item.get("content")).toLowerCase().contains(value);
})
.collect(Collectors.toList());
return paginate(rows, page, size, "reviews");
}
public Object updateReview(Long id, com.org.flashsalesystem.dto.ProductReviewDTO.UpdateDTO updateDTO) {
ProductReview review = productReviewRepository.findById(id)
.orElseThrow(() -> new RuntimeException("评价不存在"));
if (updateDTO.getStatus() != null) {
review.setStatus(updateDTO.getStatus());
}
if (updateDTO.getAdminReply() != null) {
review.setAdminReply(updateDTO.getAdminReply());
review.setRepliedAt(java.time.LocalDateTime.now());
}
review = productReviewRepository.save(review);
Map<String, Object> result = new HashMap<>();
result.put("id", review.getId());
result.put("status", review.getStatus());
result.put("statusText", review.getStatus() != null && review.getStatus() == 1 ? "显示" : "隐藏");
result.put("adminReply", review.getAdminReply());
result.put("repliedAt", review.getRepliedAt());
return result;
}
public void deleteReview(Long id) {
productReviewRepository.deleteById(id);
}
public Map<String, Object> getFavorites(int page, int size, String keyword) {
List<Map<String, Object>> rows = userFavoriteRepository.findAll(Sort.by(Sort.Direction.DESC, "createdAt"))
.stream()
.map(favorite -> {
Map<String, Object> item = new HashMap<>();
item.put("id", favorite.getId());
item.put("userId", favorite.getUserId());
item.put("productId", favorite.getProductId());
item.put("createdAt", favorite.getCreatedAt());
Product product = productRepository.findById(favorite.getProductId()).orElse(null);
User user = userRepository.findById(favorite.getUserId()).orElse(null);
item.put("productName", product != null ? product.getName() : "未知商品");
item.put("productCategory", product != null ? product.getCategory() : "默认分类");
item.put("username", user != null ? user.getUsername() : "未知用户");
return item;
})
.filter(item -> {
if (keyword == null || keyword.trim().isEmpty()) return true;
String value = keyword.trim().toLowerCase();
return String.valueOf(item.get("productName")).toLowerCase().contains(value)
|| String.valueOf(item.get("username")).toLowerCase().contains(value);
})
.collect(Collectors.toList());
return paginate(rows, page, size, "favorites");
}
public void deleteFavorite(Long id) {
userFavoriteRepository.deleteById(id);
}
private Map<String, Object> paginate(List<Map<String, Object>> rows, int page, int size, String key) {
int currentPage = Math.max(page, 1);
int pageSize = Math.max(size, 1);
int fromIndex = Math.min((currentPage - 1) * pageSize, rows.size());
int toIndex = Math.min(fromIndex + pageSize, rows.size());
Map<String, Object> result = new HashMap<>();
result.put(key, rows.subList(fromIndex, toIndex));
result.put("total", rows.size());
result.put("totalPages", (int) Math.ceil(rows.size() * 1.0 / pageSize));
result.put("currentPage", currentPage);
result.put("size", pageSize);
return result;
}
}