refactor: 后端核心模块功能增强与代码优化
- 完善 User/Product/Order 实体字段和关联关系 - 更新 DTO 适配新增字段 - 增强 Service 层业务逻辑和 Repository 查询方法 - 优化控制器接口,完善管理后台 API - 新增请求监控过滤器和指标服务
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user