refactor: 后端核心模块功能增强与代码优化
- 完善 User/Product/Order 实体字段和关联关系 - 更新 DTO 适配新增字段 - 增强 Service 层业务逻辑和 Repository 查询方法 - 优化控制器接口,完善管理后台 API - 新增请求监控过滤器和指标服务
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
package com.org.flashsalesystem.config;
|
||||||
|
|
||||||
|
import com.org.flashsalesystem.service.RequestMetricsService;
|
||||||
|
import com.org.flashsalesystem.service.RedisService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class RequestMetricsFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RequestMetricsService requestMetricsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisService redisService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
if (!uri.startsWith("/uploads/") && !uri.startsWith("/images/") && !uri.contains(".") ) {
|
||||||
|
requestMetricsService.increment();
|
||||||
|
String key = "request_count:" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
|
||||||
|
redisService.incr(key);
|
||||||
|
redisService.expire(key, 2, TimeUnit.DAYS);
|
||||||
|
}
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.org.flashsalesystem.controller;
|
|||||||
|
|
||||||
import com.org.flashsalesystem.service.AdminService;
|
import com.org.flashsalesystem.service.AdminService;
|
||||||
import com.org.flashsalesystem.service.FileUploadService;
|
import com.org.flashsalesystem.service.FileUploadService;
|
||||||
|
import com.org.flashsalesystem.service.OrderMigrationService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -28,6 +29,9 @@ public class AdminController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private FileUploadService fileUploadService;
|
private FileUploadService fileUploadService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrderMigrationService orderMigrationService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取仪表盘统计数据
|
* 获取仪表盘统计数据
|
||||||
*/
|
*/
|
||||||
@@ -279,9 +283,10 @@ public class AdminController {
|
|||||||
@RequestParam(defaultValue = "1") int page,
|
@RequestParam(defaultValue = "1") int page,
|
||||||
@RequestParam(defaultValue = "10") int size,
|
@RequestParam(defaultValue = "10") int size,
|
||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) String category,
|
||||||
@RequestParam(required = false) Integer status) {
|
@RequestParam(required = false) Integer status) {
|
||||||
try {
|
try {
|
||||||
Object products = adminService.getProducts(page, size, keyword, status);
|
Object products = adminService.getProducts(page, size, keyword, category, status);
|
||||||
|
|
||||||
Map<String, Object> response = new HashMap<>();
|
Map<String, Object> response = new HashMap<>();
|
||||||
response.put("success", true);
|
response.put("success", true);
|
||||||
@@ -488,4 +493,82 @@ public class AdminController {
|
|||||||
return ResponseEntity.badRequest().body(response);
|
return ResponseEntity.badRequest().body(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/reviews/stats")
|
||||||
|
public ResponseEntity<Map<String, Object>> getReviewStats() {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", "获取评价统计成功");
|
||||||
|
response.put("data", adminService.getReviewStats());
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/favorites/stats")
|
||||||
|
public ResponseEntity<Map<String, Object>> getFavoriteStats() {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", "获取收藏统计成功");
|
||||||
|
response.put("data", adminService.getFavoriteStats());
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/reviews")
|
||||||
|
public ResponseEntity<Map<String, Object>> getReviews(@RequestParam(defaultValue = "1") int page,
|
||||||
|
@RequestParam(defaultValue = "10") int size,
|
||||||
|
@RequestParam(required = false) String keyword) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", "获取评价列表成功");
|
||||||
|
response.put("data", adminService.getReviews(page, size, keyword));
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/reviews/{id}")
|
||||||
|
public ResponseEntity<Map<String, Object>> updateReview(@PathVariable Long id,
|
||||||
|
@RequestBody com.org.flashsalesystem.dto.ProductReviewDTO.UpdateDTO updateDTO) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", "更新评价成功");
|
||||||
|
response.put("data", adminService.updateReview(id, updateDTO));
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/reviews/{id}")
|
||||||
|
public ResponseEntity<Map<String, Object>> deleteReview(@PathVariable Long id) {
|
||||||
|
adminService.deleteReview(id);
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", "删除评价成功");
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/favorites")
|
||||||
|
public ResponseEntity<Map<String, Object>> getFavorites(@RequestParam(defaultValue = "1") int page,
|
||||||
|
@RequestParam(defaultValue = "10") int size,
|
||||||
|
@RequestParam(required = false) String keyword) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", "获取收藏列表成功");
|
||||||
|
response.put("data", adminService.getFavorites(page, size, keyword));
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/favorites/{id}")
|
||||||
|
public ResponseEntity<Map<String, Object>> deleteFavorite(@PathVariable Long id) {
|
||||||
|
adminService.deleteFavorite(id);
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", "删除收藏成功");
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/orders/migrate-items")
|
||||||
|
public ResponseEntity<Map<String, Object>> migrateLegacyOrderItems() {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", "历史订单明细迁移完成");
|
||||||
|
response.put("data", orderMigrationService.migrateLegacyOrderItems());
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,11 +70,12 @@ public class OrderController {
|
|||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public ResponseEntity<Map<String, Object>> getOrder(@PathVariable Long id, HttpServletRequest request) {
|
public ResponseEntity<Map<String, Object>> getOrder(@PathVariable Long id, HttpServletRequest request) {
|
||||||
try {
|
try {
|
||||||
Long userId = getCurrentUserId(request);
|
UserDTO currentUser = getCurrentUser(request);
|
||||||
if (userId == null) {
|
if (currentUser == null) {
|
||||||
return createUnauthorizedResponse();
|
return createUnauthorizedResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Long userId = currentUser.getId();
|
||||||
OrderDTO order = orderService.getOrderById(id);
|
OrderDTO order = orderService.getOrderById(id);
|
||||||
|
|
||||||
if (order == null) {
|
if (order == null) {
|
||||||
@@ -85,7 +86,7 @@ public class OrderController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 验证用户权限(普通用户只能查看自己的订单)
|
// 验证用户权限(普通用户只能查看自己的订单)
|
||||||
if (!order.getUserId().equals(userId)) {
|
if (!order.getUserId().equals(userId) && !"ADMIN".equalsIgnoreCase(currentUser.getRole())) {
|
||||||
Map<String, Object> response = new HashMap<>();
|
Map<String, Object> response = new HashMap<>();
|
||||||
response.put("success", false);
|
response.put("success", false);
|
||||||
response.put("message", "无权限查看此订单");
|
response.put("message", "无权限查看此订单");
|
||||||
@@ -108,6 +109,42 @@ public class OrderController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/group/{groupNo}")
|
||||||
|
public ResponseEntity<Map<String, Object>> getOrdersByGroup(@PathVariable String groupNo, HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
UserDTO currentUser = getCurrentUser(request);
|
||||||
|
if (currentUser == null) {
|
||||||
|
return createUnauthorizedResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
java.util.List<OrderDTO> orders = orderService.getOrdersByGroupNo(groupNo);
|
||||||
|
if (orders.isEmpty()) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", "订单组不存在");
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!orders.get(0).getUserId().equals(currentUser.getId()) && !"ADMIN".equalsIgnoreCase(currentUser.getRole())) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", "无权限查看此订单组");
|
||||||
|
return ResponseEntity.status(403).body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("data", orders);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取订单组详情失败", e);
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", e.getMessage());
|
||||||
|
return ResponseEntity.badRequest().body(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户订单列表
|
* 获取用户订单列表
|
||||||
*/
|
*/
|
||||||
@@ -319,7 +356,7 @@ public class OrderController {
|
|||||||
|
|
||||||
if (paymentSuccess) {
|
if (paymentSuccess) {
|
||||||
// 更新订单状态为已支付
|
// 更新订单状态为已支付
|
||||||
OrderDTO updatedOrder = orderService.updateOrderStatus(id, 2, "模拟支付成功 - " + paymentMethod);
|
OrderDTO updatedOrder = orderService.payOrder(id, userId, paymentMethod);
|
||||||
|
|
||||||
Map<String, Object> response = new HashMap<>();
|
Map<String, Object> response = new HashMap<>();
|
||||||
response.put("success", true);
|
response.put("success", true);
|
||||||
@@ -461,18 +498,50 @@ public class OrderController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前用户ID
|
* 删除订单
|
||||||
*/
|
*/
|
||||||
private Long getCurrentUserId(HttpServletRequest request) {
|
@DeleteMapping("/{id}")
|
||||||
|
public ResponseEntity<Map<String, Object>> deleteOrder(@PathVariable Long id, HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
Long userId = getCurrentUserId(request);
|
||||||
|
if (userId == null) {
|
||||||
|
return createUnauthorizedResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
orderService.deleteOrder(id, userId);
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", "订单删除成功");
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除订单失败", e);
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", e.getMessage());
|
||||||
|
return ResponseEntity.badRequest().body(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserDTO getCurrentUser(HttpServletRequest request) {
|
||||||
HttpSession session = request.getSession(false);
|
HttpSession session = request.getSession(false);
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String token = (String) session.getAttribute("token");
|
String token = (String) session.getAttribute("token");
|
||||||
UserDTO user = userService.getUserByToken(token);
|
return userService.getUserByToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户ID
|
||||||
|
*/
|
||||||
|
private Long getCurrentUserId(HttpServletRequest request) {
|
||||||
|
UserDTO user = getCurrentUser(request);
|
||||||
return user != null ? user.getId() : null;
|
return user != null ? user.getId() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ public class ProductController {
|
|||||||
@RequestParam(defaultValue = "12") int size,
|
@RequestParam(defaultValue = "12") int size,
|
||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
@RequestParam(required = false) String category,
|
@RequestParam(required = false) String category,
|
||||||
|
@RequestParam(required = false) java.math.BigDecimal minPrice,
|
||||||
|
@RequestParam(required = false) java.math.BigDecimal maxPrice,
|
||||||
@RequestParam(defaultValue = "id") String sortBy,
|
@RequestParam(defaultValue = "id") String sortBy,
|
||||||
@RequestParam(defaultValue = "desc") String sortDirection) {
|
@RequestParam(defaultValue = "desc") String sortDirection) {
|
||||||
try {
|
try {
|
||||||
@@ -113,6 +115,8 @@ public class ProductController {
|
|||||||
queryDTO.setSize(size);
|
queryDTO.setSize(size);
|
||||||
queryDTO.setKeyword(keyword);
|
queryDTO.setKeyword(keyword);
|
||||||
queryDTO.setCategory(category);
|
queryDTO.setCategory(category);
|
||||||
|
queryDTO.setMinPrice(minPrice);
|
||||||
|
queryDTO.setMaxPrice(maxPrice);
|
||||||
queryDTO.setSortBy(sortBy);
|
queryDTO.setSortBy(sortBy);
|
||||||
queryDTO.setSortDirection(sortDirection);
|
queryDTO.setSortDirection(sortDirection);
|
||||||
|
|
||||||
@@ -182,6 +186,28 @@ public class ProductController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取商品分类列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/categories")
|
||||||
|
public ResponseEntity<Map<String, Object>> getCategories() {
|
||||||
|
try {
|
||||||
|
List<String> categories = productService.getCategories();
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("data", categories);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取商品分类失败", e);
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", e.getMessage());
|
||||||
|
return ResponseEntity.badRequest().body(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新商品信息
|
* 更新商品信息
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -221,6 +221,81 @@ public class UserController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改用户密码
|
||||||
|
*/
|
||||||
|
@PostMapping("/change-password")
|
||||||
|
public ResponseEntity<Map<String, Object>> changePassword(@Validated @RequestBody UserDTO.ChangePasswordDTO changePasswordDTO,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
HttpSession session = request.getSession(false);
|
||||||
|
if (session == null) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", "用户未登录");
|
||||||
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
String token = (String) session.getAttribute("token");
|
||||||
|
UserDTO currentUser = userService.getUserByToken(token);
|
||||||
|
if (currentUser == null) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", "用户信息已过期,请重新登录");
|
||||||
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
userService.changePassword(currentUser.getId(), changePasswordDTO);
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", "密码修改成功");
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("修改用户密码失败", e);
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", e.getMessage());
|
||||||
|
return ResponseEntity.badRequest().body(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/profile-stats")
|
||||||
|
public ResponseEntity<Map<String, Object>> getProfileStats(HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
HttpSession session = request.getSession(false);
|
||||||
|
if (session == null) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", "用户未登录");
|
||||||
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
String token = (String) session.getAttribute("token");
|
||||||
|
UserDTO currentUser = userService.getUserByToken(token);
|
||||||
|
if (currentUser == null) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", "用户信息已过期,请重新登录");
|
||||||
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("data", userService.getProfileStats(currentUser.getId()));
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取个人中心统计失败", e);
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", e.getMessage());
|
||||||
|
return ResponseEntity.badRequest().body(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取在线用户统计
|
* 获取在线用户统计
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import javax.validation.constraints.Min;
|
|||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 订单数据传输对象
|
* 订单数据传输对象
|
||||||
@@ -19,6 +20,8 @@ import java.time.LocalDateTime;
|
|||||||
public class OrderDTO {
|
public class OrderDTO {
|
||||||
|
|
||||||
private Long id;
|
private Long id;
|
||||||
|
private String orderNo;
|
||||||
|
private String groupNo;
|
||||||
private Long userId;
|
private Long userId;
|
||||||
private String username;
|
private String username;
|
||||||
private Long productId;
|
private Long productId;
|
||||||
@@ -26,6 +29,11 @@ public class OrderDTO {
|
|||||||
private String productImageUrl;
|
private String productImageUrl;
|
||||||
private Integer quantity;
|
private Integer quantity;
|
||||||
private BigDecimal totalPrice;
|
private BigDecimal totalPrice;
|
||||||
|
private String receiverName;
|
||||||
|
private String receiverPhone;
|
||||||
|
private String receiverAddress;
|
||||||
|
private String remark;
|
||||||
|
private String paymentMethod;
|
||||||
private Integer status;
|
private Integer status;
|
||||||
private String statusDescription;
|
private String statusDescription;
|
||||||
private Integer orderType;
|
private Integer orderType;
|
||||||
@@ -37,6 +45,38 @@ public class OrderDTO {
|
|||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime paidAt;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime shippedAt;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime completedAt;
|
||||||
|
private List<OrderItemDTO> items;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class OrderItemDTO {
|
||||||
|
private Long id;
|
||||||
|
private Long orderId;
|
||||||
|
private Long productId;
|
||||||
|
private String productName;
|
||||||
|
private String productImageUrl;
|
||||||
|
private BigDecimal price;
|
||||||
|
private Integer quantity;
|
||||||
|
private BigDecimal subtotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class CreateItemDTO {
|
||||||
|
private Long productId;
|
||||||
|
private Integer quantity;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建订单DTO
|
* 创建订单DTO
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ public class ProductDTO {
|
|||||||
@DecimalMin(value = "0.01", message = "商品价格必须大于0")
|
@DecimalMin(value = "0.01", message = "商品价格必须大于0")
|
||||||
private BigDecimal price;
|
private BigDecimal price;
|
||||||
|
|
||||||
|
private String category;
|
||||||
|
|
||||||
@Min(value = 0, message = "库存不能为负数")
|
@Min(value = 0, message = "库存不能为负数")
|
||||||
private Integer stock = 0;
|
private Integer stock = 0;
|
||||||
|
|
||||||
@@ -94,6 +96,7 @@ public class ProductDTO {
|
|||||||
private String name;
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
private BigDecimal price;
|
private BigDecimal price;
|
||||||
|
private String category;
|
||||||
private Integer stock;
|
private Integer stock;
|
||||||
private String imageUrl;
|
private String imageUrl;
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ public class UserDTO {
|
|||||||
@Schema(description = "是否在线", example = "true")
|
@Schema(description = "是否在线", example = "true")
|
||||||
private Boolean isOnline;
|
private Boolean isOnline;
|
||||||
|
|
||||||
|
@Schema(description = "用户角色", example = "ADMIN")
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
@Schema(description = "头像地址")
|
||||||
|
private String avatar;
|
||||||
|
|
||||||
@Schema(description = "创建时间")
|
@Schema(description = "创建时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
@@ -113,5 +119,25 @@ public class UserDTO {
|
|||||||
|
|
||||||
@Size(max = 20, message = "手机号长度不能超过20个字符")
|
@Size(max = 20, message = "手机号长度不能超过20个字符")
|
||||||
private String phone;
|
private String phone;
|
||||||
|
|
||||||
|
private String avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改密码DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class ChangePasswordDTO {
|
||||||
|
@NotBlank(message = "原密码不能为空")
|
||||||
|
private String oldPassword;
|
||||||
|
|
||||||
|
@NotBlank(message = "新密码不能为空")
|
||||||
|
@Size(min = 6, max = 100, message = "新密码长度必须在6-100个字符之间")
|
||||||
|
private String newPassword;
|
||||||
|
|
||||||
|
@NotBlank(message = "确认密码不能为空")
|
||||||
|
private String confirmPassword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ public class Order {
|
|||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "order_no", nullable = false, unique = true, length = 64)
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
@Column(name = "group_no", length = 64)
|
||||||
|
private String groupNo;
|
||||||
|
|
||||||
@NotNull(message = "用户ID不能为空")
|
@NotNull(message = "用户ID不能为空")
|
||||||
@Column(name = "user_id", nullable = false)
|
@Column(name = "user_id", nullable = false)
|
||||||
private Long userId;
|
private Long userId;
|
||||||
@@ -58,6 +64,30 @@ public class Order {
|
|||||||
@Column(name = "created_at", nullable = false, updatable = false)
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Column(name = "receiver_name", length = 100)
|
||||||
|
private String receiverName;
|
||||||
|
|
||||||
|
@Column(name = "receiver_phone", length = 20)
|
||||||
|
private String receiverPhone;
|
||||||
|
|
||||||
|
@Column(name = "receiver_address", length = 255)
|
||||||
|
private String receiverAddress;
|
||||||
|
|
||||||
|
@Column(name = "remark", length = 255)
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@Column(name = "payment_method", length = 50)
|
||||||
|
private String paymentMethod;
|
||||||
|
|
||||||
|
@Column(name = "paid_at")
|
||||||
|
private LocalDateTime paidAt;
|
||||||
|
|
||||||
|
@Column(name = "shipped_at")
|
||||||
|
private LocalDateTime shippedAt;
|
||||||
|
|
||||||
|
@Column(name = "completed_at")
|
||||||
|
private LocalDateTime completedAt;
|
||||||
|
|
||||||
@Column(name = "updated_at")
|
@Column(name = "updated_at")
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
@@ -73,6 +103,9 @@ public class Order {
|
|||||||
protected void onCreate() {
|
protected void onCreate() {
|
||||||
createdAt = LocalDateTime.now();
|
createdAt = LocalDateTime.now();
|
||||||
updatedAt = LocalDateTime.now();
|
updatedAt = LocalDateTime.now();
|
||||||
|
if (orderNo == null || orderNo.trim().isEmpty()) {
|
||||||
|
orderNo = "FS" + System.currentTimeMillis();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreUpdate
|
@PreUpdate
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ public class Product {
|
|||||||
@Column(nullable = false, precision = 10, scale = 2)
|
@Column(nullable = false, precision = 10, scale = 2)
|
||||||
private BigDecimal price;
|
private BigDecimal price;
|
||||||
|
|
||||||
|
@Column(length = 100)
|
||||||
|
private String category;
|
||||||
|
|
||||||
@Min(value = 0, message = "库存不能为负数")
|
@Min(value = 0, message = "库存不能为负数")
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Integer stock = 0;
|
private Integer stock = 0;
|
||||||
@@ -60,6 +63,9 @@ public class Product {
|
|||||||
|
|
||||||
@PrePersist
|
@PrePersist
|
||||||
protected void onCreate() {
|
protected void onCreate() {
|
||||||
|
if (category == null || category.trim().isEmpty()) {
|
||||||
|
category = "默认分类";
|
||||||
|
}
|
||||||
createdAt = LocalDateTime.now();
|
createdAt = LocalDateTime.now();
|
||||||
updatedAt = LocalDateTime.now();
|
updatedAt = LocalDateTime.now();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,15 @@ public class User {
|
|||||||
@Column(length = 20)
|
@Column(length = 20)
|
||||||
private String phone;
|
private String phone;
|
||||||
|
|
||||||
|
@Column(length = 500)
|
||||||
|
private String avatar;
|
||||||
|
|
||||||
@Column(name = "status", nullable = false)
|
@Column(name = "status", nullable = false)
|
||||||
private Integer status = 1; // 1-正常, 0-禁用
|
private Integer status = 1; // 1-正常, 0-禁用
|
||||||
|
|
||||||
|
@Column(name = "role", nullable = false, length = 20)
|
||||||
|
private String role = "USER";
|
||||||
|
|
||||||
@Column(name = "last_login")
|
@Column(name = "last_login")
|
||||||
private LocalDateTime lastLogin;
|
private LocalDateTime lastLogin;
|
||||||
|
|
||||||
@@ -57,6 +63,9 @@ public class User {
|
|||||||
|
|
||||||
@PrePersist
|
@PrePersist
|
||||||
protected void onCreate() {
|
protected void onCreate() {
|
||||||
|
if (role == null || role.trim().isEmpty()) {
|
||||||
|
role = "USER";
|
||||||
|
}
|
||||||
createdAt = LocalDateTime.now();
|
createdAt = LocalDateTime.now();
|
||||||
updatedAt = LocalDateTime.now();
|
updatedAt = LocalDateTime.now();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,4 +139,11 @@ public interface OrderRepository extends JpaRepository<Order, Long> {
|
|||||||
".username LIKE %:keyword%")
|
".username LIKE %:keyword%")
|
||||||
Page<Order> findByIdContainingOrUserUsernameContaining(@Param("keyword") String keyword1,
|
Page<Order> findByIdContainingOrUserUsernameContaining(@Param("keyword") String keyword1,
|
||||||
@Param("keyword") String keyword2, Pageable pageable);
|
@Param("keyword") String keyword2, Pageable pageable);
|
||||||
|
|
||||||
|
long countByProductId(Long productId);
|
||||||
|
|
||||||
|
@Query("SELECT COALESCE(SUM(o.totalPrice), 0) FROM Order o WHERE o.productId = :productId AND o.status IN (2,3,4)")
|
||||||
|
BigDecimal sumTotalPriceByProductId(@Param("productId") Long productId);
|
||||||
|
|
||||||
|
List<Order> findByGroupNoOrderByCreatedAtAsc(String groupNo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,4 +86,25 @@ public interface ProductRepository extends JpaRepository<Product, Long> {
|
|||||||
* 根据名称模糊查询(忽略大小写)
|
* 根据名称模糊查询(忽略大小写)
|
||||||
*/
|
*/
|
||||||
Page<Product> findByNameContainingIgnoreCase(String name, Pageable pageable);
|
Page<Product> findByNameContainingIgnoreCase(String name, Pageable pageable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品综合搜索
|
||||||
|
*/
|
||||||
|
@Query("SELECT p FROM Product p WHERE (:status IS NULL OR p.status = :status) " +
|
||||||
|
"AND (:keyword IS NULL OR p.name LIKE %:keyword%) " +
|
||||||
|
"AND (:category IS NULL OR p.category = :category) " +
|
||||||
|
"AND (:minPrice IS NULL OR p.price >= :minPrice) " +
|
||||||
|
"AND (:maxPrice IS NULL OR p.price <= :maxPrice)")
|
||||||
|
Page<Product> searchProducts(@Param("status") Integer status,
|
||||||
|
@Param("keyword") String keyword,
|
||||||
|
@Param("category") String category,
|
||||||
|
@Param("minPrice") java.math.BigDecimal minPrice,
|
||||||
|
@Param("maxPrice") java.math.BigDecimal maxPrice,
|
||||||
|
Pageable pageable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分类列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT DISTINCT p.category FROM Product p WHERE p.status = 1 AND p.category IS NOT NULL AND p.category <> '' ORDER BY p.category ASC")
|
||||||
|
List<String> findDistinctCategories();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,15 @@ package com.org.flashsalesystem.service;
|
|||||||
import com.org.flashsalesystem.dto.UserDTO;
|
import com.org.flashsalesystem.dto.UserDTO;
|
||||||
import com.org.flashsalesystem.entity.Order;
|
import com.org.flashsalesystem.entity.Order;
|
||||||
import com.org.flashsalesystem.entity.Product;
|
import com.org.flashsalesystem.entity.Product;
|
||||||
|
import com.org.flashsalesystem.entity.ProductReview;
|
||||||
import com.org.flashsalesystem.entity.User;
|
import com.org.flashsalesystem.entity.User;
|
||||||
|
import com.org.flashsalesystem.entity.UserFavorite;
|
||||||
import com.org.flashsalesystem.repository.FlashSaleRepository;
|
import com.org.flashsalesystem.repository.FlashSaleRepository;
|
||||||
|
import com.org.flashsalesystem.repository.OrderItemRepository;
|
||||||
import com.org.flashsalesystem.repository.OrderRepository;
|
import com.org.flashsalesystem.repository.OrderRepository;
|
||||||
import com.org.flashsalesystem.repository.ProductRepository;
|
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 com.org.flashsalesystem.repository.UserRepository;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
@@ -17,6 +22,9 @@ import org.springframework.data.domain.Pageable;
|
|||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -40,12 +48,27 @@ public class AdminService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private OrderRepository orderRepository;
|
private OrderRepository orderRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrderItemRepository orderItemRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProductReviewRepository productReviewRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserFavoriteRepository userFavoriteRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private FlashSaleRepository flashSaleRepository;
|
private FlashSaleRepository flashSaleRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisService redisService;
|
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("id", product.getId());
|
||||||
productMap.put("name", product.getName());
|
productMap.put("name", product.getName());
|
||||||
productMap.put("price", product.getPrice());
|
productMap.put("price", product.getPrice());
|
||||||
|
productMap.put("category", product.getCategory());
|
||||||
productMap.put("stock", product.getStock());
|
productMap.put("stock", product.getStock());
|
||||||
productMap.put("sales", 0); // 暂时设为0,后续可以添加销量统计
|
productMap.put("sales", 0); // 暂时设为0,后续可以添加销量统计
|
||||||
return productMap;
|
return productMap;
|
||||||
@@ -347,6 +371,8 @@ public class AdminService {
|
|||||||
BeanUtils.copyProperties(user, dto);
|
BeanUtils.copyProperties(user, dto);
|
||||||
dto.setPassword(null); // 不返回密码
|
dto.setPassword(null); // 不返回密码
|
||||||
dto.setIsOnline(redisService.sIsMember("online_users", user.getId().toString()));
|
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;
|
return dto;
|
||||||
}).collect(Collectors.toList());
|
}).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 {
|
try {
|
||||||
Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createdAt"));
|
Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createdAt"));
|
||||||
Page<Product> productPage;
|
Page<Product> productPage;
|
||||||
|
|
||||||
if (keyword != null && !keyword.trim().isEmpty() && status != null) {
|
productPage = productRepository.searchProducts(
|
||||||
// 同时按关键词和状态筛选
|
status,
|
||||||
productPage = productRepository.findByNameContainingAndStatus(keyword, status, pageable);
|
keyword != null && !keyword.trim().isEmpty() ? keyword.trim() : null,
|
||||||
} else if (keyword != null && !keyword.trim().isEmpty()) {
|
category != null && !category.trim().isEmpty() ? category.trim() : null,
|
||||||
productPage = productRepository.findByNameContaining(keyword, pageable);
|
null,
|
||||||
} else if (status != null) {
|
null,
|
||||||
productPage = productRepository.findByStatus(status, pageable);
|
pageable
|
||||||
} else {
|
);
|
||||||
productPage = productRepository.findAll(pageable);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换为DTO
|
// 转换为DTO
|
||||||
List<Map<String, Object>> productList = productPage.getContent().stream().map(product -> {
|
List<Map<String, Object>> productList = productPage.getContent().stream().map(product -> {
|
||||||
@@ -447,6 +471,7 @@ public class AdminService {
|
|||||||
productMap.put("id", product.getId());
|
productMap.put("id", product.getId());
|
||||||
productMap.put("name", product.getName());
|
productMap.put("name", product.getName());
|
||||||
productMap.put("price", product.getPrice());
|
productMap.put("price", product.getPrice());
|
||||||
|
productMap.put("category", product.getCategory());
|
||||||
productMap.put("stock", product.getStock());
|
productMap.put("stock", product.getStock());
|
||||||
productMap.put("status", product.getStatus());
|
productMap.put("status", product.getStatus());
|
||||||
productMap.put("description", product.getDescription());
|
productMap.put("description", product.getDescription());
|
||||||
@@ -482,27 +507,49 @@ public class AdminService {
|
|||||||
try {
|
try {
|
||||||
Map<String, Object> systemStatus = new HashMap<>();
|
Map<String, Object> systemStatus = new HashMap<>();
|
||||||
|
|
||||||
// 获取JVM内存使用情况
|
|
||||||
Runtime runtime = Runtime.getRuntime();
|
Runtime runtime = Runtime.getRuntime();
|
||||||
long totalMemory = runtime.totalMemory();
|
long totalMemory = runtime.totalMemory();
|
||||||
long freeMemory = runtime.freeMemory();
|
long freeMemory = runtime.freeMemory();
|
||||||
long usedMemory = totalMemory - freeMemory;
|
long usedMemory = totalMemory - freeMemory;
|
||||||
double memoryUsage = (double) usedMemory / totalMemory * 100;
|
double memoryUsage = totalMemory == 0 ? 0 : (double) usedMemory / totalMemory * 100;
|
||||||
|
|
||||||
// 获取可用处理器数量(模拟CPU使用率)
|
double cpuUsage = 0;
|
||||||
int availableProcessors = runtime.availableProcessors();
|
java.lang.management.OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
|
||||||
double cpuUsage = Math.random() * 30 + 20; // 模拟20-50%的CPU使用率
|
if (osBean instanceof com.sun.management.OperatingSystemMXBean) {
|
||||||
|
cpuUsage = ((com.sun.management.OperatingSystemMXBean) osBean).getSystemCpuLoad() * 100;
|
||||||
|
if (cpuUsage < 0) {
|
||||||
|
cpuUsage = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 模拟磁盘使用率
|
File root = new File("/");
|
||||||
double diskUsage = Math.random() * 40 + 10; // 模拟10-50%的磁盘使用率
|
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("cpuUsage", Math.round(cpuUsage));
|
||||||
systemStatus.put("memoryUsage", Math.round(memoryUsage));
|
systemStatus.put("memoryUsage", Math.round(memoryUsage));
|
||||||
systemStatus.put("diskUsage", Math.round(diskUsage));
|
systemStatus.put("diskUsage", Math.round(diskUsage));
|
||||||
systemStatus.put("availableProcessors", availableProcessors);
|
systemStatus.put("availableProcessors", runtime.availableProcessors());
|
||||||
systemStatus.put("totalMemory", totalMemory / 1024 / 1024 + "MB");
|
systemStatus.put("totalMemory", totalMemory / 1024 / 1024 + "MB");
|
||||||
systemStatus.put("usedMemory", usedMemory / 1024 / 1024 + "MB");
|
systemStatus.put("usedMemory", usedMemory / 1024 / 1024 + "MB");
|
||||||
|
systemStatus.put("dbStatus", dbHealthy ? "正常" : "异常");
|
||||||
|
systemStatus.put("redisStatus", redisHealthy ? "正常" : "异常");
|
||||||
|
systemStatus.put("requestCountToday", requestCountToday);
|
||||||
|
|
||||||
return systemStatus;
|
return systemStatus;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -512,6 +559,9 @@ public class AdminService {
|
|||||||
errorStatus.put("cpuUsage", 0);
|
errorStatus.put("cpuUsage", 0);
|
||||||
errorStatus.put("memoryUsage", 0);
|
errorStatus.put("memoryUsage", 0);
|
||||||
errorStatus.put("diskUsage", 0);
|
errorStatus.put("diskUsage", 0);
|
||||||
|
errorStatus.put("dbStatus", "异常");
|
||||||
|
errorStatus.put("redisStatus", "异常");
|
||||||
|
errorStatus.put("requestCountToday", requestMetricsService.getTotalRequests());
|
||||||
return errorStatus;
|
return errorStatus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -522,21 +572,16 @@ public class AdminService {
|
|||||||
public Object getRedisStatus() {
|
public Object getRedisStatus() {
|
||||||
try {
|
try {
|
||||||
List<Map<String, Object>> redisNodes = new ArrayList<>();
|
List<Map<String, Object>> redisNodes = new ArrayList<>();
|
||||||
|
Properties memoryInfo = redisService.info("memory");
|
||||||
|
Properties clientInfo = redisService.info("clients");
|
||||||
|
String ping = redisService.ping();
|
||||||
|
|
||||||
// 模拟Redis集群节点状态
|
Map<String, Object> nodeStatus = new HashMap<>();
|
||||||
String[] nodes = {
|
nodeStatus.put("node", "default");
|
||||||
"42.192.62.91:7000", "42.192.62.91:7001", "42.192.62.91:7002",
|
nodeStatus.put("status", "PONG".equalsIgnoreCase(ping) ? "正常" : "异常");
|
||||||
"42.192.62.91:7003", "42.192.62.91:7004", "42.192.62.91:7005"
|
nodeStatus.put("memory", memoryInfo.getProperty("used_memory_human", "unknown"));
|
||||||
};
|
nodeStatus.put("connections", Integer.parseInt(clientInfo.getProperty("connected_clients", "0")));
|
||||||
|
redisNodes.add(nodeStatus);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return redisNodes;
|
return redisNodes;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -564,11 +609,16 @@ public class AdminService {
|
|||||||
productMap.put("createdAt", product.getCreatedAt());
|
productMap.put("createdAt", product.getCreatedAt());
|
||||||
productMap.put("updatedAt", product.getUpdatedAt());
|
productMap.put("updatedAt", product.getUpdatedAt());
|
||||||
|
|
||||||
// 添加统计信息(模拟数据,实际应该从统计表获取)
|
long totalSales = orderItemRepository.countByProductId(product.getId());
|
||||||
productMap.put("totalSales", 0); // 总销量
|
java.math.BigDecimal totalRevenue = orderItemRepository.sumSubtotalByProductId(product.getId());
|
||||||
productMap.put("totalRevenue", 0.0); // 总收入
|
Double averageRating = productReviewRepository.findAverageRatingByProductId(product.getId());
|
||||||
productMap.put("viewCount", 0); // 浏览次数
|
long reviewCount = productReviewRepository.countByProductId(product.getId());
|
||||||
productMap.put("rating", 0.0); // 平均评分
|
|
||||||
|
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;
|
return productMap;
|
||||||
} else {
|
} else {
|
||||||
@@ -598,6 +648,9 @@ public class AdminService {
|
|||||||
if (productData.containsKey("stock")) {
|
if (productData.containsKey("stock")) {
|
||||||
product.setStock(Integer.parseInt(productData.get("stock").toString()));
|
product.setStock(Integer.parseInt(productData.get("stock").toString()));
|
||||||
}
|
}
|
||||||
|
if (productData.containsKey("category")) {
|
||||||
|
product.setCategory((String) productData.get("category"));
|
||||||
|
}
|
||||||
if (productData.containsKey("status")) {
|
if (productData.containsKey("status")) {
|
||||||
product.setStatus(Integer.parseInt(productData.get("status").toString()));
|
product.setStatus(Integer.parseInt(productData.get("status").toString()));
|
||||||
}
|
}
|
||||||
@@ -643,6 +696,7 @@ public class AdminService {
|
|||||||
Product product = new Product();
|
Product product = new Product();
|
||||||
product.setName((String) productData.get("name"));
|
product.setName((String) productData.get("name"));
|
||||||
product.setPrice(new BigDecimal(productData.get("price").toString()));
|
product.setPrice(new BigDecimal(productData.get("price").toString()));
|
||||||
|
product.setCategory((String) productData.getOrDefault("category", "默认分类"));
|
||||||
product.setStock(Integer.parseInt(productData.get("stock").toString()));
|
product.setStock(Integer.parseInt(productData.get("stock").toString()));
|
||||||
product.setStatus(Integer.parseInt(productData.get("status").toString()));
|
product.setStatus(Integer.parseInt(productData.get("status").toString()));
|
||||||
product.setDescription((String) productData.get("description"));
|
product.setDescription((String) productData.get("description"));
|
||||||
@@ -656,6 +710,7 @@ public class AdminService {
|
|||||||
result.put("id", savedProduct.getId());
|
result.put("id", savedProduct.getId());
|
||||||
result.put("name", savedProduct.getName());
|
result.put("name", savedProduct.getName());
|
||||||
result.put("price", savedProduct.getPrice());
|
result.put("price", savedProduct.getPrice());
|
||||||
|
result.put("category", savedProduct.getCategory());
|
||||||
result.put("stock", savedProduct.getStock());
|
result.put("stock", savedProduct.getStock());
|
||||||
result.put("status", savedProduct.getStatus());
|
result.put("status", savedProduct.getStatus());
|
||||||
result.put("description", savedProduct.getDescription());
|
result.put("description", savedProduct.getDescription());
|
||||||
@@ -668,4 +723,133 @@ public class AdminService {
|
|||||||
throw new RuntimeException("添加商品失败: " + e.getMessage());
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -355,42 +355,29 @@ public class CartService {
|
|||||||
.map(CartDTO.CartItemDTO::getSubtotal)
|
.map(CartDTO.CartItemDTO::getSubtotal)
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
|
||||||
// 根据商品数量创建订单
|
|
||||||
if (itemsToOrder.size() == 1) {
|
if (itemsToOrder.size() == 1) {
|
||||||
// 单商品订单
|
|
||||||
CartDTO.CartItemDTO item = itemsToOrder.get(0);
|
CartDTO.CartItemDTO item = itemsToOrder.get(0);
|
||||||
OrderDTO.CreateDTO createDTO = new OrderDTO.CreateDTO();
|
OrderDTO.CreateDTO createDTO = new OrderDTO.CreateDTO();
|
||||||
createDTO.setProductId(item.getProductId());
|
createDTO.setProductId(item.getProductId());
|
||||||
createDTO.setQuantity(item.getQuantity());
|
createDTO.setQuantity(item.getQuantity());
|
||||||
|
|
||||||
OrderDTO order = orderService.createOrder(userId, createDTO);
|
OrderDTO order = orderService.createOrder(userId, createDTO);
|
||||||
|
|
||||||
// 从购物车中移除已下单的商品
|
|
||||||
redisService.hDel(cartKey, item.getProductId().toString());
|
redisService.hDel(cartKey, item.getProductId().toString());
|
||||||
|
|
||||||
log.info("单商品购物车下单成功: 用户ID={}, 订单ID={}", userId, order.getId());
|
log.info("单商品购物车下单成功: 用户ID={}, 订单ID={}", userId, order.getId());
|
||||||
return order;
|
return order;
|
||||||
} else {
|
|
||||||
// 多商品订单 - 创建多个订单
|
|
||||||
List<OrderDTO> orders = new ArrayList<>();
|
|
||||||
for (CartDTO.CartItemDTO item : itemsToOrder) {
|
|
||||||
OrderDTO.CreateDTO createDTO = new OrderDTO.CreateDTO();
|
|
||||||
createDTO.setProductId(item.getProductId());
|
|
||||||
createDTO.setQuantity(item.getQuantity());
|
|
||||||
|
|
||||||
OrderDTO order = orderService.createOrder(userId, createDTO);
|
|
||||||
orders.add(order);
|
|
||||||
|
|
||||||
// 从购物车中移除已下单的商品
|
|
||||||
redisService.hDel(cartKey, item.getProductId().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("多商品购物车下单成功: 用户ID={}, 订单数量={}", userId, orders.size());
|
|
||||||
|
|
||||||
// 返回第一个订单作为代表(实际项目中可能需要创建主订单)
|
|
||||||
OrderDTO firstOrder = orders.get(0);
|
|
||||||
firstOrder.setTotalPrice(orderTotalPrice); // 设置总价为所有订单的总和
|
|
||||||
return firstOrder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<OrderDTO.CreateItemDTO> orderItems = itemsToOrder.stream()
|
||||||
|
.map(item -> new OrderDTO.CreateItemDTO(item.getProductId(), item.getQuantity()))
|
||||||
|
.collect(java.util.stream.Collectors.toList());
|
||||||
|
|
||||||
|
OrderDTO order = orderService.createCompositeOrder(userId, orderItems, null, null, null, null, 1);
|
||||||
|
for (CartDTO.CartItemDTO item : itemsToOrder) {
|
||||||
|
redisService.hDel(cartKey, item.getProductId().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("多商品购物车下单成功: 用户ID={}, 订单ID={}", userId, order.getId());
|
||||||
|
order.setTotalPrice(orderTotalPrice);
|
||||||
|
return order;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1048,12 +1048,16 @@ public class FlashSaleService {
|
|||||||
*/
|
*/
|
||||||
private Order createFlashSaleOrder(Long userId, FlashSale flashSale, FlashSaleDTO.ParticipateDTO participateDTO) {
|
private Order createFlashSaleOrder(Long userId, FlashSale flashSale, FlashSaleDTO.ParticipateDTO participateDTO) {
|
||||||
Order order = new Order();
|
Order order = new Order();
|
||||||
|
order.setOrderNo("FS" + System.currentTimeMillis() + String.format("%03d", new java.util.Random().nextInt(1000)));
|
||||||
order.setUserId(userId);
|
order.setUserId(userId);
|
||||||
order.setProductId(flashSale.getProductId());
|
order.setProductId(flashSale.getProductId());
|
||||||
order.setQuantity(participateDTO.getQuantity());
|
order.setQuantity(participateDTO.getQuantity());
|
||||||
order.setTotalPrice(flashSale.getFlashPrice().multiply(BigDecimal.valueOf(participateDTO.getQuantity())));
|
order.setTotalPrice(flashSale.getFlashPrice().multiply(BigDecimal.valueOf(participateDTO.getQuantity())));
|
||||||
order.setStatus(1); // 待支付
|
order.setStatus(1); // 待支付
|
||||||
order.setOrderType(2); // 秒杀订单
|
order.setOrderType(2); // 秒杀订单
|
||||||
|
order.setReceiverPhone(participateDTO.getPhone());
|
||||||
|
order.setReceiverAddress(participateDTO.getAddress());
|
||||||
|
order.setRemark("秒杀订单");
|
||||||
|
|
||||||
return orderRepository.save(order);
|
return orderRepository.save(order);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ import com.org.flashsalesystem.dto.OrderDTO;
|
|||||||
import com.org.flashsalesystem.dto.ProductDTO;
|
import com.org.flashsalesystem.dto.ProductDTO;
|
||||||
import com.org.flashsalesystem.dto.UserDTO;
|
import com.org.flashsalesystem.dto.UserDTO;
|
||||||
import com.org.flashsalesystem.entity.Order;
|
import com.org.flashsalesystem.entity.Order;
|
||||||
|
import com.org.flashsalesystem.entity.OrderItem;
|
||||||
|
import com.org.flashsalesystem.entity.UserAddress;
|
||||||
|
import com.org.flashsalesystem.repository.OrderItemRepository;
|
||||||
import com.org.flashsalesystem.repository.OrderRepository;
|
import com.org.flashsalesystem.repository.OrderRepository;
|
||||||
import com.org.flashsalesystem.repository.ProductRepository;
|
import com.org.flashsalesystem.repository.ProductRepository;
|
||||||
|
import com.org.flashsalesystem.repository.UserAddressRepository;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -37,6 +41,8 @@ public class OrderService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private OrderRepository orderRepository;
|
private OrderRepository orderRepository;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
private OrderItemRepository orderItemRepository;
|
||||||
|
@Autowired
|
||||||
private ProductRepository productRepository;
|
private ProductRepository productRepository;
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisService redisService;
|
private RedisService redisService;
|
||||||
@@ -44,12 +50,19 @@ public class OrderService {
|
|||||||
private ProductService productService;
|
private ProductService productService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
@Autowired
|
||||||
|
private UserAddressRepository userAddressRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建普通订单
|
* 创建普通订单
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public OrderDTO createOrder(Long userId, OrderDTO.CreateDTO createDTO) {
|
public OrderDTO createOrder(Long userId, OrderDTO.CreateDTO createDTO) {
|
||||||
|
return createOrder(userId, createDTO, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public OrderDTO createOrder(Long userId, OrderDTO.CreateDTO createDTO, String groupNo) {
|
||||||
log.info("创建订单: 用户ID={}, 商品ID={}, 数量={}", userId, createDTO.getProductId(), createDTO.getQuantity());
|
log.info("创建订单: 用户ID={}, 商品ID={}, 数量={}", userId, createDTO.getProductId(), createDTO.getQuantity());
|
||||||
|
|
||||||
// 验证商品
|
// 验证商品
|
||||||
@@ -72,14 +85,19 @@ public class OrderService {
|
|||||||
|
|
||||||
// 创建订单
|
// 创建订单
|
||||||
Order order = new Order();
|
Order order = new Order();
|
||||||
|
order.setOrderNo(generateOrderNo());
|
||||||
|
order.setGroupNo(groupNo);
|
||||||
order.setUserId(userId);
|
order.setUserId(userId);
|
||||||
order.setProductId(createDTO.getProductId());
|
order.setProductId(createDTO.getProductId());
|
||||||
order.setQuantity(createDTO.getQuantity());
|
order.setQuantity(createDTO.getQuantity());
|
||||||
order.setTotalPrice(totalPrice);
|
order.setTotalPrice(totalPrice);
|
||||||
order.setStatus(1); // 待支付
|
order.setStatus(1); // 待支付
|
||||||
order.setOrderType(1); // 普通订单
|
order.setOrderType(1); // 普通订单
|
||||||
|
order.setRemark(createDTO.getRemark());
|
||||||
|
fillOrderAddress(order, userId, createDTO.getReceiverName(), createDTO.getReceiverPhone(), createDTO.getReceiverAddress());
|
||||||
|
|
||||||
order = orderRepository.save(order);
|
order = orderRepository.save(order);
|
||||||
|
createOrderItem(order, product, createDTO.getQuantity());
|
||||||
|
|
||||||
// 扣减库存
|
// 扣减库存
|
||||||
boolean stockUpdated = productService.updateStock(createDTO.getProductId(), createDTO.getQuantity(),
|
boolean stockUpdated = productService.updateStock(createDTO.getProductId(), createDTO.getQuantity(),
|
||||||
@@ -102,6 +120,66 @@ public class OrderService {
|
|||||||
return buildOrderDTO(order);
|
return buildOrderDTO(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public OrderDTO createCompositeOrder(Long userId,
|
||||||
|
List<OrderDTO.CreateItemDTO> items,
|
||||||
|
String receiverName,
|
||||||
|
String receiverPhone,
|
||||||
|
String receiverAddress,
|
||||||
|
String remark,
|
||||||
|
Integer orderType) {
|
||||||
|
if (items == null || items.isEmpty()) {
|
||||||
|
throw new RuntimeException("订单商品不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ProductDTO> products = new ArrayList<>();
|
||||||
|
BigDecimal totalPrice = BigDecimal.ZERO;
|
||||||
|
int totalQuantity = 0;
|
||||||
|
|
||||||
|
for (OrderDTO.CreateItemDTO item : items) {
|
||||||
|
ProductDTO product = productService.getProductById(item.getProductId());
|
||||||
|
if (product == null) {
|
||||||
|
throw new RuntimeException("商品不存在: " + item.getProductId());
|
||||||
|
}
|
||||||
|
if (product.getStatus() != 1) {
|
||||||
|
throw new RuntimeException("商品已下架: " + product.getName());
|
||||||
|
}
|
||||||
|
if (product.getStock() < item.getQuantity()) {
|
||||||
|
throw new RuntimeException("商品库存不足: " + product.getName());
|
||||||
|
}
|
||||||
|
products.add(product);
|
||||||
|
totalPrice = totalPrice.add(product.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())));
|
||||||
|
totalQuantity += item.getQuantity();
|
||||||
|
}
|
||||||
|
|
||||||
|
Order order = new Order();
|
||||||
|
order.setOrderNo(generateOrderNo());
|
||||||
|
order.setUserId(userId);
|
||||||
|
order.setProductId(items.get(0).getProductId());
|
||||||
|
order.setQuantity(totalQuantity);
|
||||||
|
order.setTotalPrice(totalPrice);
|
||||||
|
order.setStatus(1);
|
||||||
|
order.setOrderType(orderType == null ? 1 : orderType);
|
||||||
|
order.setRemark(remark);
|
||||||
|
fillOrderAddress(order, userId, receiverName, receiverPhone, receiverAddress);
|
||||||
|
order = orderRepository.save(order);
|
||||||
|
|
||||||
|
for (int index = 0; index < items.size(); index++) {
|
||||||
|
OrderDTO.CreateItemDTO item = items.get(index);
|
||||||
|
ProductDTO product = products.get(index);
|
||||||
|
createOrderItem(order, product, item.getQuantity());
|
||||||
|
boolean stockUpdated = productService.updateStock(item.getProductId(), item.getQuantity(), "decrease");
|
||||||
|
if (!stockUpdated) {
|
||||||
|
throw new RuntimeException("库存扣减失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheOrderInfo(order);
|
||||||
|
redisService.lPush(ORDER_QUEUE, order.getId());
|
||||||
|
publishOrderStatusChange(order, "created");
|
||||||
|
return buildOrderDTO(order);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据ID获取订单
|
* 根据ID获取订单
|
||||||
*/
|
*/
|
||||||
@@ -129,6 +207,11 @@ public class OrderService {
|
|||||||
// 缓存订单信息
|
// 缓存订单信息
|
||||||
cacheOrderInfo(order);
|
cacheOrderInfo(order);
|
||||||
|
|
||||||
|
if (order.getGroupNo() != null && !order.getGroupNo().trim().isEmpty()) {
|
||||||
|
List<Order> groupOrders = orderRepository.findByGroupNoOrderByCreatedAtAsc(order.getGroupNo());
|
||||||
|
return buildGroupedOrderDTO(groupOrders, order);
|
||||||
|
}
|
||||||
|
|
||||||
return buildOrderDTO(order);
|
return buildOrderDTO(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,9 +233,7 @@ public class OrderService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 转换为DTO
|
// 转换为DTO
|
||||||
List<OrderDTO> orderDTOs = orderPage.getContent().stream()
|
List<OrderDTO> orderDTOs = aggregateOrders(orderPage.getContent());
|
||||||
.map(this::buildOrderDTO)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
result.put("content", orderDTOs);
|
result.put("content", orderDTOs);
|
||||||
@@ -164,6 +245,16 @@ public class OrderService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<OrderDTO> getOrdersByGroupNo(String groupNo) {
|
||||||
|
if (groupNo == null || groupNo.trim().isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return orderRepository.findByGroupNoOrderByCreatedAtAsc(groupNo)
|
||||||
|
.stream()
|
||||||
|
.map(this::buildOrderDTO)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有订单列表(管理员)
|
* 获取所有订单列表(管理员)
|
||||||
*/
|
*/
|
||||||
@@ -186,9 +277,7 @@ public class OrderService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 转换为DTO
|
// 转换为DTO
|
||||||
List<OrderDTO> orderDTOs = orderPage.getContent().stream()
|
List<OrderDTO> orderDTOs = aggregateOrders(orderPage.getContent());
|
||||||
.map(this::buildOrderDTO)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
result.put("content", orderDTOs);
|
result.put("content", orderDTOs);
|
||||||
@@ -222,6 +311,18 @@ public class OrderService {
|
|||||||
|
|
||||||
// 更新状态
|
// 更新状态
|
||||||
order.setStatus(newStatus);
|
order.setStatus(newStatus);
|
||||||
|
if (remark != null && !remark.trim().isEmpty()) {
|
||||||
|
order.setRemark(remark);
|
||||||
|
}
|
||||||
|
if (newStatus == 2 && order.getPaidAt() == null) {
|
||||||
|
order.setPaidAt(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
if (newStatus == 3 && order.getShippedAt() == null) {
|
||||||
|
order.setShippedAt(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
if (newStatus == 4 && order.getCompletedAt() == null) {
|
||||||
|
order.setCompletedAt(LocalDateTime.now());
|
||||||
|
}
|
||||||
order = orderRepository.save(order);
|
order = orderRepository.save(order);
|
||||||
|
|
||||||
// 更新缓存
|
// 更新缓存
|
||||||
@@ -238,6 +339,37 @@ public class OrderService {
|
|||||||
return buildOrderDTO(order);
|
return buildOrderDTO(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付订单
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public OrderDTO payOrder(Long orderId, Long userId, String paymentMethod) {
|
||||||
|
log.info("支付订单: orderId={}, userId={}, paymentMethod={}", orderId, userId, paymentMethod);
|
||||||
|
|
||||||
|
Optional<Order> orderOpt = orderRepository.findById(orderId);
|
||||||
|
if (!orderOpt.isPresent()) {
|
||||||
|
throw new RuntimeException("订单不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Order order = orderOpt.get();
|
||||||
|
if (!order.getUserId().equals(userId)) {
|
||||||
|
throw new RuntimeException("无权限操作此订单");
|
||||||
|
}
|
||||||
|
if (order.getStatus() != 1) {
|
||||||
|
throw new RuntimeException("订单状态不正确,无法支付");
|
||||||
|
}
|
||||||
|
|
||||||
|
order.setStatus(2);
|
||||||
|
order.setPaymentMethod(paymentMethod);
|
||||||
|
order.setPaidAt(LocalDateTime.now());
|
||||||
|
order.setRemark("模拟支付成功 - " + paymentMethod);
|
||||||
|
order = orderRepository.save(order);
|
||||||
|
|
||||||
|
cacheOrderInfo(order);
|
||||||
|
publishOrderStatusChange(order, "paid");
|
||||||
|
return buildOrderDTO(order);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消订单
|
* 取消订单
|
||||||
*/
|
*/
|
||||||
@@ -264,6 +396,7 @@ public class OrderService {
|
|||||||
|
|
||||||
// 更新订单状态为已取消
|
// 更新订单状态为已取消
|
||||||
order.setStatus(5);
|
order.setStatus(5);
|
||||||
|
order.setRemark("用户取消订单");
|
||||||
order = orderRepository.save(order);
|
order = orderRepository.save(order);
|
||||||
|
|
||||||
// 恢复库存
|
// 恢复库存
|
||||||
@@ -328,6 +461,35 @@ public class OrderService {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除订单
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public void deleteOrder(Long orderId, Long userId) {
|
||||||
|
log.info("删除订单: orderId={}, userId={}", orderId, userId);
|
||||||
|
|
||||||
|
Optional<Order> orderOpt = orderRepository.findById(orderId);
|
||||||
|
if (!orderOpt.isPresent()) {
|
||||||
|
throw new RuntimeException("订单不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Order order = orderOpt.get();
|
||||||
|
if (!order.getUserId().equals(userId)) {
|
||||||
|
throw new RuntimeException("无权限删除此订单");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (order.getStatus() != 4 && order.getStatus() != 5) {
|
||||||
|
throw new RuntimeException("只有已完成或已取消的订单允许删除");
|
||||||
|
}
|
||||||
|
|
||||||
|
orderRepository.deleteById(orderId);
|
||||||
|
redisService.delete(ORDER_CACHE_PREFIX + orderId);
|
||||||
|
|
||||||
|
log.info("订单删除成功: {}", orderId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取订单统计信息
|
* 获取订单统计信息
|
||||||
*/
|
*/
|
||||||
@@ -367,19 +529,60 @@ public class OrderService {
|
|||||||
return statistics;
|
return statistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createOrderItem(Order order, ProductDTO product, Integer quantity) {
|
||||||
|
OrderItem orderItem = new OrderItem();
|
||||||
|
orderItem.setOrderId(order.getId());
|
||||||
|
orderItem.setProductId(product.getId());
|
||||||
|
orderItem.setProductName(product.getName());
|
||||||
|
orderItem.setProductImageUrl(product.getImageUrl());
|
||||||
|
orderItem.setPrice(product.getPrice());
|
||||||
|
orderItem.setQuantity(quantity);
|
||||||
|
orderItem.setSubtotal(product.getPrice().multiply(BigDecimal.valueOf(quantity)));
|
||||||
|
orderItemRepository.save(orderItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OrderDTO> aggregateOrders(List<Order> orders) {
|
||||||
|
Map<String, List<Order>> groupedOrders = new LinkedHashMap<>();
|
||||||
|
for (Order order : orders) {
|
||||||
|
String key = order.getGroupNo() != null && !order.getGroupNo().trim().isEmpty() ? order.getGroupNo() : String.valueOf(order.getId());
|
||||||
|
groupedOrders.computeIfAbsent(key, item -> new ArrayList<>()).add(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<OrderDTO> result = new ArrayList<>();
|
||||||
|
for (List<Order> group : groupedOrders.values()) {
|
||||||
|
if (group.size() > 1) {
|
||||||
|
result.add(buildGroupedOrderDTO(group, group.get(0)));
|
||||||
|
} else {
|
||||||
|
result.add(buildOrderDTO(group.get(0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存订单信息
|
* 缓存订单信息
|
||||||
*/
|
*/
|
||||||
private void cacheOrderInfo(Order order) {
|
private void cacheOrderInfo(Order order) {
|
||||||
String cacheKey = ORDER_CACHE_PREFIX + order.getId();
|
String cacheKey = ORDER_CACHE_PREFIX + order.getId();
|
||||||
Map<String, Object> orderMap = new HashMap<>();
|
Map<String, Object> orderMap = new HashMap<>();
|
||||||
|
orderMap.put("orderNo", order.getOrderNo());
|
||||||
|
orderMap.put("groupNo", order.getGroupNo() == null ? "" : order.getGroupNo());
|
||||||
orderMap.put("userId", order.getUserId().toString());
|
orderMap.put("userId", order.getUserId().toString());
|
||||||
orderMap.put("productId", order.getProductId().toString());
|
orderMap.put("productId", order.getProductId().toString());
|
||||||
orderMap.put("quantity", order.getQuantity().toString());
|
orderMap.put("quantity", order.getQuantity().toString());
|
||||||
orderMap.put("totalPrice", order.getTotalPrice().toString());
|
orderMap.put("totalPrice", order.getTotalPrice().toString());
|
||||||
orderMap.put("status", order.getStatus().toString());
|
orderMap.put("status", order.getStatus().toString());
|
||||||
orderMap.put("orderType", order.getOrderType().toString());
|
orderMap.put("orderType", order.getOrderType().toString());
|
||||||
|
orderMap.put("receiverName", order.getReceiverName() == null ? "" : order.getReceiverName());
|
||||||
|
orderMap.put("receiverPhone", order.getReceiverPhone() == null ? "" : order.getReceiverPhone());
|
||||||
|
orderMap.put("receiverAddress", order.getReceiverAddress() == null ? "" : order.getReceiverAddress());
|
||||||
|
orderMap.put("remark", order.getRemark() == null ? "" : order.getRemark());
|
||||||
|
orderMap.put("paymentMethod", order.getPaymentMethod() == null ? "" : order.getPaymentMethod());
|
||||||
orderMap.put("createdAt", order.getCreatedAt().toString());
|
orderMap.put("createdAt", order.getCreatedAt().toString());
|
||||||
|
orderMap.put("updatedAt", order.getUpdatedAt() == null ? "" : order.getUpdatedAt().toString());
|
||||||
|
orderMap.put("paidAt", order.getPaidAt() == null ? "" : order.getPaidAt().toString());
|
||||||
|
orderMap.put("shippedAt", order.getShippedAt() == null ? "" : order.getShippedAt().toString());
|
||||||
|
orderMap.put("completedAt", order.getCompletedAt() == null ? "" : order.getCompletedAt().toString());
|
||||||
|
|
||||||
redisService.hMSet(cacheKey, orderMap);
|
redisService.hMSet(cacheKey, orderMap);
|
||||||
redisService.expire(cacheKey, 24, TimeUnit.HOURS);
|
redisService.expire(cacheKey, 24, TimeUnit.HOURS);
|
||||||
@@ -396,19 +599,35 @@ public class OrderService {
|
|||||||
private OrderDTO buildOrderDTOFromCache(Long orderId, Map<Object, Object> orderMap) {
|
private OrderDTO buildOrderDTOFromCache(Long orderId, Map<Object, Object> orderMap) {
|
||||||
OrderDTO orderDTO = new OrderDTO();
|
OrderDTO orderDTO = new OrderDTO();
|
||||||
orderDTO.setId(orderId);
|
orderDTO.setId(orderId);
|
||||||
|
orderDTO.setOrderNo((String) orderMap.get("orderNo"));
|
||||||
|
orderDTO.setGroupNo((String) orderMap.get("groupNo"));
|
||||||
orderDTO.setUserId(Long.valueOf((String) orderMap.get("userId")));
|
orderDTO.setUserId(Long.valueOf((String) orderMap.get("userId")));
|
||||||
orderDTO.setProductId(Long.valueOf((String) orderMap.get("productId")));
|
orderDTO.setProductId(Long.valueOf((String) orderMap.get("productId")));
|
||||||
orderDTO.setQuantity(Integer.valueOf((String) orderMap.get("quantity")));
|
orderDTO.setQuantity(Integer.valueOf((String) orderMap.get("quantity")));
|
||||||
orderDTO.setTotalPrice(new BigDecimal((String) orderMap.get("totalPrice")));
|
orderDTO.setTotalPrice(new BigDecimal((String) orderMap.get("totalPrice")));
|
||||||
orderDTO.setStatus(Integer.valueOf((String) orderMap.get("status")));
|
orderDTO.setStatus(Integer.valueOf((String) orderMap.get("status")));
|
||||||
orderDTO.setOrderType(Integer.valueOf((String) orderMap.get("orderType")));
|
orderDTO.setOrderType(Integer.valueOf((String) orderMap.get("orderType")));
|
||||||
|
orderDTO.setReceiverName((String) orderMap.get("receiverName"));
|
||||||
|
orderDTO.setReceiverPhone((String) orderMap.get("receiverPhone"));
|
||||||
|
orderDTO.setReceiverAddress((String) orderMap.get("receiverAddress"));
|
||||||
|
orderDTO.setRemark((String) orderMap.get("remark"));
|
||||||
|
orderDTO.setPaymentMethod((String) orderMap.get("paymentMethod"));
|
||||||
orderDTO.setCreatedAt(LocalDateTime.parse((String) orderMap.get("createdAt")));
|
orderDTO.setCreatedAt(LocalDateTime.parse((String) orderMap.get("createdAt")));
|
||||||
|
String updatedAt = (String) orderMap.get("updatedAt");
|
||||||
|
if (updatedAt != null && !updatedAt.isEmpty()) { orderDTO.setUpdatedAt(LocalDateTime.parse(updatedAt)); }
|
||||||
|
String paidAt = (String) orderMap.get("paidAt");
|
||||||
|
if (paidAt != null && !paidAt.isEmpty()) { orderDTO.setPaidAt(LocalDateTime.parse(paidAt)); }
|
||||||
|
String shippedAt = (String) orderMap.get("shippedAt");
|
||||||
|
if (shippedAt != null && !shippedAt.isEmpty()) { orderDTO.setShippedAt(LocalDateTime.parse(shippedAt)); }
|
||||||
|
String completedAt = (String) orderMap.get("completedAt");
|
||||||
|
if (completedAt != null && !completedAt.isEmpty()) { orderDTO.setCompletedAt(LocalDateTime.parse(completedAt)); }
|
||||||
|
|
||||||
// 设置状态和类型描述
|
// 设置状态和类型描述
|
||||||
orderDTO.setStatusDescription(getStatusDescription(orderDTO.getStatus()));
|
orderDTO.setStatusDescription(getStatusDescription(orderDTO.getStatus()));
|
||||||
orderDTO.setOrderTypeDescription(getOrderTypeDescription(orderDTO.getOrderType()));
|
orderDTO.setOrderTypeDescription(getOrderTypeDescription(orderDTO.getOrderType()));
|
||||||
|
|
||||||
// 获取用户和商品信息
|
// 获取用户和商品信息
|
||||||
|
orderDTO.setItems(buildOrderItems(orderDTO.getId(), orderDTO.getProductId(), orderDTO.getProductName(), orderDTO.getProductImageUrl(), orderDTO.getQuantity(), orderDTO.getTotalPrice()));
|
||||||
UserDTO user = userService.getUserById(orderDTO.getUserId());
|
UserDTO user = userService.getUserById(orderDTO.getUserId());
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
orderDTO.setUsername(user.getUsername());
|
orderDTO.setUsername(user.getUsername());
|
||||||
@@ -434,6 +653,8 @@ public class OrderService {
|
|||||||
orderDTO.setStatusDescription(getStatusDescription(order.getStatus()));
|
orderDTO.setStatusDescription(getStatusDescription(order.getStatus()));
|
||||||
orderDTO.setOrderTypeDescription(getOrderTypeDescription(order.getOrderType()));
|
orderDTO.setOrderTypeDescription(getOrderTypeDescription(order.getOrderType()));
|
||||||
|
|
||||||
|
orderDTO.setItems(buildOrderItems(order.getId(), order.getProductId(), null, null, order.getQuantity(), order.getTotalPrice()));
|
||||||
|
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
UserDTO user = userService.getUserById(order.getUserId());
|
UserDTO user = userService.getUserById(order.getUserId());
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
@@ -447,6 +668,60 @@ public class OrderService {
|
|||||||
orderDTO.setProductImageUrl(product.getImageUrl());
|
orderDTO.setProductImageUrl(product.getImageUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (orderDTO.getItems() != null && orderDTO.getItems().size() > 1) {
|
||||||
|
orderDTO.setProductName(orderDTO.getItems().get(0).getProductName() + " 等" + orderDTO.getItems().size() + "件商品");
|
||||||
|
orderDTO.setProductImageUrl(orderDTO.getItems().get(0).getProductImageUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillOrderAddress(Order order, Long userId, String receiverName, String receiverPhone, String receiverAddress) {
|
||||||
|
if (receiverName != null && !receiverName.trim().isEmpty()) {
|
||||||
|
order.setReceiverName(receiverName.trim());
|
||||||
|
order.setReceiverPhone(receiverPhone);
|
||||||
|
order.setReceiverAddress(receiverAddress);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<UserAddress> defaultAddress = userAddressRepository.findByUserIdAndIsDefaultTrue(userId);
|
||||||
|
if (defaultAddress.isPresent()) {
|
||||||
|
UserAddress address = defaultAddress.get();
|
||||||
|
order.setReceiverName(address.getName());
|
||||||
|
order.setReceiverPhone(address.getPhone());
|
||||||
|
order.setReceiverAddress(String.format("%s %s %s %s",
|
||||||
|
address.getProvince() == null ? "" : address.getProvince(),
|
||||||
|
address.getCity() == null ? "" : address.getCity(),
|
||||||
|
address.getDistrict() == null ? "" : address.getDistrict(),
|
||||||
|
address.getAddress() == null ? "" : address.getAddress()).trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateOrderNo() {
|
||||||
|
return "FS" + System.currentTimeMillis() + String.format("%03d", new java.util.Random().nextInt(1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrderDTO buildGroupedOrderDTO(List<Order> orders, Order currentOrder) {
|
||||||
|
if (orders == null || orders.isEmpty()) {
|
||||||
|
return buildOrderDTO(currentOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
Order anchor = currentOrder != null ? currentOrder : orders.get(0);
|
||||||
|
OrderDTO orderDTO = buildOrderDTO(anchor);
|
||||||
|
BigDecimal totalPrice = orders.stream().map(Order::getTotalPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
Integer totalQuantity = orders.stream().map(Order::getQuantity).reduce(0, Integer::sum);
|
||||||
|
List<OrderDTO.OrderItemDTO> items = orders.stream()
|
||||||
|
.flatMap(item -> buildOrderItems(item.getId(), item.getProductId(), null, null, item.getQuantity(), item.getTotalPrice()).stream())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
orderDTO.setItems(items);
|
||||||
|
orderDTO.setTotalPrice(totalPrice);
|
||||||
|
orderDTO.setQuantity(totalQuantity);
|
||||||
|
orderDTO.setOrderNo(anchor.getGroupNo());
|
||||||
|
orderDTO.setGroupNo(anchor.getGroupNo());
|
||||||
|
if (!items.isEmpty()) {
|
||||||
|
orderDTO.setProductName(items.get(0).getProductName() + " 等" + items.size() + "件商品");
|
||||||
|
orderDTO.setProductImageUrl(items.get(0).getProductImageUrl());
|
||||||
|
}
|
||||||
return orderDTO;
|
return orderDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,6 +785,41 @@ public class OrderService {
|
|||||||
redisService.publish("order:status:change", message);
|
redisService.publish("order:status:change", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<OrderDTO.OrderItemDTO> buildOrderItems(Long orderId,
|
||||||
|
Long fallbackProductId,
|
||||||
|
String fallbackProductName,
|
||||||
|
String fallbackProductImageUrl,
|
||||||
|
Integer fallbackQuantity,
|
||||||
|
BigDecimal fallbackTotalPrice) {
|
||||||
|
List<OrderItem> orderItems = orderItemRepository.findByOrderIdOrderByIdAsc(orderId);
|
||||||
|
if (!orderItems.isEmpty()) {
|
||||||
|
return orderItems.stream().map(item -> {
|
||||||
|
OrderDTO.OrderItemDTO dto = new OrderDTO.OrderItemDTO();
|
||||||
|
dto.setId(item.getId());
|
||||||
|
dto.setOrderId(item.getOrderId());
|
||||||
|
dto.setProductId(item.getProductId());
|
||||||
|
dto.setProductName(item.getProductName());
|
||||||
|
dto.setProductImageUrl(item.getProductImageUrl());
|
||||||
|
dto.setPrice(item.getPrice());
|
||||||
|
dto.setQuantity(item.getQuantity());
|
||||||
|
dto.setSubtotal(item.getSubtotal());
|
||||||
|
return dto;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductDTO product = productService.getProductById(fallbackProductId);
|
||||||
|
OrderDTO.OrderItemDTO dto = new OrderDTO.OrderItemDTO();
|
||||||
|
dto.setId(orderId);
|
||||||
|
dto.setOrderId(orderId);
|
||||||
|
dto.setProductId(fallbackProductId);
|
||||||
|
dto.setProductName(fallbackProductName != null ? fallbackProductName : (product != null ? product.getName() : "未知商品"));
|
||||||
|
dto.setProductImageUrl(fallbackProductImageUrl != null ? fallbackProductImageUrl : (product != null ? product.getImageUrl() : null));
|
||||||
|
dto.setPrice(fallbackQuantity != null && fallbackQuantity > 0 ? fallbackTotalPrice.divide(BigDecimal.valueOf(fallbackQuantity), 2, java.math.RoundingMode.HALF_UP) : fallbackTotalPrice);
|
||||||
|
dto.setQuantity(fallbackQuantity);
|
||||||
|
dto.setSubtotal(fallbackTotalPrice);
|
||||||
|
return Collections.singletonList(dto);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取状态描述
|
* 获取状态描述
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ public class ProductService {
|
|||||||
Product product = new Product();
|
Product product = new Product();
|
||||||
BeanUtils.copyProperties(createDTO, product);
|
BeanUtils.copyProperties(createDTO, product);
|
||||||
product.setStatus(1); // 默认上架
|
product.setStatus(1); // 默认上架
|
||||||
|
if (product.getCategory() == null || product.getCategory().trim().isEmpty()) {
|
||||||
|
product.setCategory("默认分类");
|
||||||
|
}
|
||||||
|
|
||||||
product = productRepository.save(product);
|
product = productRepository.save(product);
|
||||||
|
|
||||||
@@ -121,22 +124,22 @@ public class ProductService {
|
|||||||
Sort sort = Sort.by(Sort.Direction.fromString(queryDTO.getSortDirection()), queryDTO.getSortBy());
|
Sort sort = Sort.by(Sort.Direction.fromString(queryDTO.getSortDirection()), queryDTO.getSortBy());
|
||||||
Pageable pageable = PageRequest.of(queryDTO.getPage(), queryDTO.getSize(), sort);
|
Pageable pageable = PageRequest.of(queryDTO.getPage(), queryDTO.getSize(), sort);
|
||||||
|
|
||||||
Page<Product> productPage;
|
Integer status = queryDTO.getStatus() != null ? queryDTO.getStatus() : 1;
|
||||||
|
String keyword = queryDTO.getKeyword() != null && !queryDTO.getKeyword().trim().isEmpty()
|
||||||
|
? queryDTO.getKeyword().trim()
|
||||||
|
: (queryDTO.getName() != null && !queryDTO.getName().trim().isEmpty() ? queryDTO.getName().trim() : null);
|
||||||
|
String category = queryDTO.getCategory() != null && !queryDTO.getCategory().trim().isEmpty()
|
||||||
|
? queryDTO.getCategory().trim()
|
||||||
|
: null;
|
||||||
|
|
||||||
// 根据查询条件获取数据
|
Page<Product> productPage = productRepository.searchProducts(
|
||||||
if (queryDTO.getName() != null && !queryDTO.getName().trim().isEmpty()) {
|
status,
|
||||||
productPage = productRepository.findByStatus(1, pageable)
|
keyword,
|
||||||
.map(product -> {
|
category,
|
||||||
if (product.getName().contains(queryDTO.getName())) {
|
queryDTO.getMinPrice(),
|
||||||
return product;
|
queryDTO.getMaxPrice(),
|
||||||
}
|
pageable
|
||||||
return null;
|
);
|
||||||
})
|
|
||||||
.map(product -> product);
|
|
||||||
} else {
|
|
||||||
productPage = productRepository.findByStatus(queryDTO.getStatus() != null ? queryDTO.getStatus() : 1,
|
|
||||||
pageable);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换为DTO
|
// 转换为DTO
|
||||||
List<ProductDTO> productDTOs = productPage.getContent().stream()
|
List<ProductDTO> productDTOs = productPage.getContent().stream()
|
||||||
@@ -219,6 +222,9 @@ public class ProductService {
|
|||||||
if (updateDTO.getPrice() != null) {
|
if (updateDTO.getPrice() != null) {
|
||||||
product.setPrice(updateDTO.getPrice());
|
product.setPrice(updateDTO.getPrice());
|
||||||
}
|
}
|
||||||
|
if (updateDTO.getCategory() != null) {
|
||||||
|
product.setCategory(updateDTO.getCategory());
|
||||||
|
}
|
||||||
if (updateDTO.getStock() != null) {
|
if (updateDTO.getStock() != null) {
|
||||||
product.setStock(updateDTO.getStock());
|
product.setStock(updateDTO.getStock());
|
||||||
// 同步更新Redis中的库存
|
// 同步更新Redis中的库存
|
||||||
@@ -400,6 +406,7 @@ public class ProductService {
|
|||||||
productMap.put("name", product.getName());
|
productMap.put("name", product.getName());
|
||||||
productMap.put("description", product.getDescription());
|
productMap.put("description", product.getDescription());
|
||||||
productMap.put("price", product.getPrice().toString());
|
productMap.put("price", product.getPrice().toString());
|
||||||
|
productMap.put("category", product.getCategory() == null ? "" : product.getCategory());
|
||||||
productMap.put("stock", product.getStock().toString());
|
productMap.put("stock", product.getStock().toString());
|
||||||
productMap.put("imageUrl", product.getImageUrl());
|
productMap.put("imageUrl", product.getImageUrl());
|
||||||
productMap.put("status", product.getStatus().toString());
|
productMap.put("status", product.getStatus().toString());
|
||||||
@@ -417,6 +424,7 @@ public class ProductService {
|
|||||||
productDTO.setName((String) productMap.get("name"));
|
productDTO.setName((String) productMap.get("name"));
|
||||||
productDTO.setDescription((String) productMap.get("description"));
|
productDTO.setDescription((String) productMap.get("description"));
|
||||||
productDTO.setPrice(new java.math.BigDecimal((String) productMap.get("price")));
|
productDTO.setPrice(new java.math.BigDecimal((String) productMap.get("price")));
|
||||||
|
productDTO.setCategory((String) productMap.get("category"));
|
||||||
productDTO.setStock(Integer.valueOf((String) productMap.get("stock")));
|
productDTO.setStock(Integer.valueOf((String) productMap.get("stock")));
|
||||||
productDTO.setImageUrl((String) productMap.get("imageUrl"));
|
productDTO.setImageUrl((String) productMap.get("imageUrl"));
|
||||||
productDTO.setStatus(Integer.valueOf((String) productMap.get("status")));
|
productDTO.setStatus(Integer.valueOf((String) productMap.get("status")));
|
||||||
@@ -429,6 +437,10 @@ public class ProductService {
|
|||||||
private String buildProductListCacheKey(ProductDTO.QueryDTO queryDTO) {
|
private String buildProductListCacheKey(ProductDTO.QueryDTO queryDTO) {
|
||||||
return PRODUCT_LIST_CACHE_PREFIX +
|
return PRODUCT_LIST_CACHE_PREFIX +
|
||||||
(queryDTO.getName() != null ? queryDTO.getName() : "all") + ":" +
|
(queryDTO.getName() != null ? queryDTO.getName() : "all") + ":" +
|
||||||
|
(queryDTO.getKeyword() != null ? queryDTO.getKeyword() : "all") + ":" +
|
||||||
|
(queryDTO.getCategory() != null ? queryDTO.getCategory() : "all") + ":" +
|
||||||
|
(queryDTO.getMinPrice() != null ? queryDTO.getMinPrice() : "none") + ":" +
|
||||||
|
(queryDTO.getMaxPrice() != null ? queryDTO.getMaxPrice() : "none") + ":" +
|
||||||
(queryDTO.getStatus() != null ? queryDTO.getStatus() : "1") + ":" +
|
(queryDTO.getStatus() != null ? queryDTO.getStatus() : "1") + ":" +
|
||||||
queryDTO.getPage() + ":" + queryDTO.getSize() + ":" +
|
queryDTO.getPage() + ":" + queryDTO.getSize() + ":" +
|
||||||
queryDTO.getSortBy() + ":" + queryDTO.getSortDirection();
|
queryDTO.getSortBy() + ":" + queryDTO.getSortDirection();
|
||||||
@@ -443,6 +455,13 @@ public class ProductService {
|
|||||||
redisService.delete(HOT_PRODUCTS_CACHE);
|
redisService.delete(HOT_PRODUCTS_CACHE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分类列表
|
||||||
|
*/
|
||||||
|
public List<String> getCategories() {
|
||||||
|
return productRepository.findDistinctCategories();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除商品
|
* 删除商品
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -326,6 +326,24 @@ public class RedisService {
|
|||||||
return redisTemplate.hasKey(key);
|
return redisTemplate.hasKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String ping() {
|
||||||
|
try {
|
||||||
|
return stringRedisTemplate.getConnectionFactory().getConnection().ping();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Redis ping失败", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Properties info(String section) {
|
||||||
|
try {
|
||||||
|
return stringRedisTemplate.getConnectionFactory().getConnection().info(section);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Redis info获取失败", e);
|
||||||
|
return new Properties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置键的过期时间
|
* 设置键的过期时间
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.org.flashsalesystem.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class RequestMetricsService {
|
||||||
|
private final AtomicLong totalRequests = new AtomicLong();
|
||||||
|
|
||||||
|
public void increment() {
|
||||||
|
totalRequests.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTotalRequests() {
|
||||||
|
return totalRequests.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
package com.org.flashsalesystem.service;
|
package com.org.flashsalesystem.service;
|
||||||
|
|
||||||
import com.org.flashsalesystem.dto.UserDTO;
|
import com.org.flashsalesystem.dto.UserDTO;
|
||||||
|
import com.org.flashsalesystem.entity.Order;
|
||||||
import com.org.flashsalesystem.entity.User;
|
import com.org.flashsalesystem.entity.User;
|
||||||
|
import com.org.flashsalesystem.repository.OrderRepository;
|
||||||
|
import com.org.flashsalesystem.repository.UserFavoriteRepository;
|
||||||
import com.org.flashsalesystem.repository.UserRepository;
|
import com.org.flashsalesystem.repository.UserRepository;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
@@ -11,6 +14,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -34,6 +38,10 @@ public class UserService {
|
|||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisService redisService;
|
private RedisService redisService;
|
||||||
|
@Autowired
|
||||||
|
private OrderRepository orderRepository;
|
||||||
|
@Autowired
|
||||||
|
private UserFavoriteRepository userFavoriteRepository;
|
||||||
@Value("${flashsale.cache.user-expire-minutes:30}")
|
@Value("${flashsale.cache.user-expire-minutes:30}")
|
||||||
private int userCacheExpireMinutes;
|
private int userCacheExpireMinutes;
|
||||||
|
|
||||||
@@ -70,6 +78,8 @@ public class UserService {
|
|||||||
user.setPassword(encryptPassword(registerDTO.getPassword()));
|
user.setPassword(encryptPassword(registerDTO.getPassword()));
|
||||||
user.setEmail(registerDTO.getEmail());
|
user.setEmail(registerDTO.getEmail());
|
||||||
user.setPhone(registerDTO.getPhone());
|
user.setPhone(registerDTO.getPhone());
|
||||||
|
user.setAvatar("");
|
||||||
|
user.setRole("admin".equalsIgnoreCase(registerDTO.getUsername()) ? "ADMIN" : "USER");
|
||||||
|
|
||||||
// 保存到数据库
|
// 保存到数据库
|
||||||
user = userRepository.save(user);
|
user = userRepository.save(user);
|
||||||
@@ -77,10 +87,7 @@ public class UserService {
|
|||||||
// 缓存用户信息
|
// 缓存用户信息
|
||||||
cacheUserInfo(user);
|
cacheUserInfo(user);
|
||||||
|
|
||||||
// 转换为DTO返回
|
UserDTO userDTO = buildSafeUserDTO(user);
|
||||||
UserDTO userDTO = new UserDTO();
|
|
||||||
BeanUtils.copyProperties(user, userDTO);
|
|
||||||
userDTO.setPassword(null); // 不返回密码
|
|
||||||
|
|
||||||
log.info("用户注册成功: {}, ID: {}", user.getUsername(), user.getId());
|
log.info("用户注册成功: {}, ID: {}", user.getUsername(), user.getId());
|
||||||
return userDTO;
|
return userDTO;
|
||||||
@@ -105,6 +112,10 @@ public class UserService {
|
|||||||
throw new RuntimeException("用户名或密码错误");
|
throw new RuntimeException("用户名或密码错误");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新最后登录时间
|
||||||
|
user.setLastLogin(LocalDateTime.now());
|
||||||
|
user = userRepository.save(user);
|
||||||
|
|
||||||
// 生成token
|
// 生成token
|
||||||
String token = generateToken();
|
String token = generateToken();
|
||||||
|
|
||||||
@@ -116,9 +127,7 @@ public class UserService {
|
|||||||
redisService.sAdd(ONLINE_USERS_SET, user.getId());
|
redisService.sAdd(ONLINE_USERS_SET, user.getId());
|
||||||
|
|
||||||
// 转换为DTO
|
// 转换为DTO
|
||||||
UserDTO userDTO = new UserDTO();
|
UserDTO userDTO = buildSafeUserDTO(user);
|
||||||
BeanUtils.copyProperties(user, userDTO);
|
|
||||||
userDTO.setPassword(null);
|
|
||||||
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
result.put("token", token);
|
result.put("token", token);
|
||||||
@@ -182,13 +191,7 @@ public class UserService {
|
|||||||
Map<Object, Object> userMap = redisService.hGetAll(cacheKey);
|
Map<Object, Object> userMap = redisService.hGetAll(cacheKey);
|
||||||
|
|
||||||
if (!userMap.isEmpty()) {
|
if (!userMap.isEmpty()) {
|
||||||
// 从缓存构造用户对象
|
return buildUserDTOFromCache(userId, userMap);
|
||||||
UserDTO userDTO = new UserDTO();
|
|
||||||
userDTO.setId(userId);
|
|
||||||
userDTO.setUsername((String) userMap.get("username"));
|
|
||||||
userDTO.setEmail((String) userMap.get("email"));
|
|
||||||
userDTO.setPhone((String) userMap.get("phone"));
|
|
||||||
return userDTO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 缓存中没有,从数据库获取
|
// 缓存中没有,从数据库获取
|
||||||
@@ -202,12 +205,7 @@ public class UserService {
|
|||||||
// 缓存用户信息
|
// 缓存用户信息
|
||||||
cacheUserInfo(user);
|
cacheUserInfo(user);
|
||||||
|
|
||||||
// 转换为DTO
|
return buildSafeUserDTO(user);
|
||||||
UserDTO userDTO = new UserDTO();
|
|
||||||
BeanUtils.copyProperties(user, userDTO);
|
|
||||||
userDTO.setPassword(null);
|
|
||||||
|
|
||||||
return userDTO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,21 +238,72 @@ public class UserService {
|
|||||||
user.setPhone(updateDTO.getPhone());
|
user.setPhone(updateDTO.getPhone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (updateDTO.getAvatar() != null) {
|
||||||
|
user.setAvatar(updateDTO.getAvatar());
|
||||||
|
}
|
||||||
|
|
||||||
// 保存到数据库
|
// 保存到数据库
|
||||||
user = userRepository.save(user);
|
user = userRepository.save(user);
|
||||||
|
|
||||||
// 更新缓存
|
// 更新缓存
|
||||||
cacheUserInfo(user);
|
cacheUserInfo(user);
|
||||||
|
|
||||||
// 转换为DTO
|
UserDTO userDTO = buildSafeUserDTO(user);
|
||||||
UserDTO userDTO = new UserDTO();
|
|
||||||
BeanUtils.copyProperties(user, userDTO);
|
|
||||||
userDTO.setPassword(null);
|
|
||||||
|
|
||||||
log.info("用户信息更新成功: {}", userId);
|
log.info("用户信息更新成功: {}", userId);
|
||||||
return userDTO;
|
return userDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改用户密码
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public void changePassword(Long userId, UserDTO.ChangePasswordDTO changePasswordDTO) {
|
||||||
|
log.info("修改用户密码: {}", userId);
|
||||||
|
|
||||||
|
if (!changePasswordDTO.getNewPassword().equals(changePasswordDTO.getConfirmPassword())) {
|
||||||
|
throw new RuntimeException("两次输入的新密码不一致");
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<User> userOpt = userRepository.findById(userId);
|
||||||
|
if (!userOpt.isPresent()) {
|
||||||
|
throw new RuntimeException("用户不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = userOpt.get();
|
||||||
|
if (!verifyPassword(changePasswordDTO.getOldPassword(), user.getPassword())) {
|
||||||
|
throw new RuntimeException("原密码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
user.setPassword(encryptPassword(changePasswordDTO.getNewPassword()));
|
||||||
|
user = userRepository.save(user);
|
||||||
|
cacheUserInfo(user);
|
||||||
|
|
||||||
|
log.info("用户密码修改成功: {}", userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getProfileStats(Long userId) {
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
java.util.List<Order> orders = orderRepository.findByUserId(userId);
|
||||||
|
|
||||||
|
long totalOrders = orders.size();
|
||||||
|
java.math.BigDecimal totalAmount = orders.stream()
|
||||||
|
.filter(order -> order.getStatus() != null && order.getStatus() >= 2 && order.getStatus() <= 4)
|
||||||
|
.map(Order::getTotalPrice)
|
||||||
|
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
|
||||||
|
long flashSaleSuccess = orders.stream()
|
||||||
|
.filter(order -> order.getOrderType() != null && order.getOrderType() == 2)
|
||||||
|
.filter(order -> order.getStatus() != null && order.getStatus() >= 2 && order.getStatus() <= 4)
|
||||||
|
.count();
|
||||||
|
long favoriteCount = userFavoriteRepository.countByUserId(userId);
|
||||||
|
|
||||||
|
result.put("totalOrders", totalOrders);
|
||||||
|
result.put("totalAmount", totalAmount);
|
||||||
|
result.put("flashSaleSuccess", flashSaleSuccess);
|
||||||
|
result.put("favoriteCount", favoriteCount);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查用户是否在线
|
* 检查用户是否在线
|
||||||
*/
|
*/
|
||||||
@@ -276,13 +325,60 @@ public class UserService {
|
|||||||
String cacheKey = USER_CACHE_PREFIX + user.getId();
|
String cacheKey = USER_CACHE_PREFIX + user.getId();
|
||||||
Map<String, Object> userMap = new HashMap<>();
|
Map<String, Object> userMap = new HashMap<>();
|
||||||
userMap.put("username", user.getUsername());
|
userMap.put("username", user.getUsername());
|
||||||
userMap.put("email", user.getEmail());
|
userMap.put("email", user.getEmail() == null ? "" : user.getEmail());
|
||||||
userMap.put("phone", user.getPhone());
|
userMap.put("phone", user.getPhone() == null ? "" : user.getPhone());
|
||||||
|
userMap.put("avatar", user.getAvatar() == null ? "" : user.getAvatar());
|
||||||
|
userMap.put("status", user.getStatus() == null ? 1 : user.getStatus());
|
||||||
|
userMap.put("role", user.getRole() == null ? "USER" : user.getRole());
|
||||||
|
userMap.put("createdAt", user.getCreatedAt() == null ? "" : user.getCreatedAt().toString());
|
||||||
|
userMap.put("lastLogin", user.getLastLogin() == null ? "" : user.getLastLogin().toString());
|
||||||
|
userMap.put("updatedAt", user.getUpdatedAt() == null ? "" : user.getUpdatedAt().toString());
|
||||||
|
|
||||||
redisService.hMSet(cacheKey, userMap);
|
redisService.hMSet(cacheKey, userMap);
|
||||||
redisService.expire(cacheKey, userCacheExpireMinutes, TimeUnit.MINUTES);
|
redisService.expire(cacheKey, userCacheExpireMinutes, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UserDTO buildUserDTOFromCache(Long userId, Map<Object, Object> userMap) {
|
||||||
|
UserDTO userDTO = new UserDTO();
|
||||||
|
userDTO.setId(userId);
|
||||||
|
userDTO.setUsername((String) userMap.get("username"));
|
||||||
|
userDTO.setEmail((String) userMap.get("email"));
|
||||||
|
userDTO.setPhone((String) userMap.get("phone"));
|
||||||
|
userDTO.setAvatar((String) userMap.get("avatar"));
|
||||||
|
userDTO.setStatus(Integer.valueOf(String.valueOf(userMap.getOrDefault("status", "1"))));
|
||||||
|
userDTO.setIsOnline(isUserOnline(userId));
|
||||||
|
userDTO.setRole(String.valueOf(userMap.getOrDefault("role", "USER")));
|
||||||
|
userDTO.setAvatar((String) userMap.getOrDefault("avatar", ""));
|
||||||
|
|
||||||
|
String createdAt = String.valueOf(userMap.getOrDefault("createdAt", ""));
|
||||||
|
if (!createdAt.isEmpty()) {
|
||||||
|
userDTO.setCreatedAt(LocalDateTime.parse(createdAt));
|
||||||
|
}
|
||||||
|
String lastLogin = String.valueOf(userMap.getOrDefault("lastLogin", ""));
|
||||||
|
if (!lastLogin.isEmpty()) {
|
||||||
|
userDTO.setLastLogin(LocalDateTime.parse(lastLogin));
|
||||||
|
}
|
||||||
|
String updatedAt = String.valueOf(userMap.getOrDefault("updatedAt", ""));
|
||||||
|
if (!updatedAt.isEmpty()) {
|
||||||
|
userDTO.setUpdatedAt(LocalDateTime.parse(updatedAt));
|
||||||
|
}
|
||||||
|
return userDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserDTO buildSafeUserDTO(User user) {
|
||||||
|
UserDTO userDTO = new UserDTO();
|
||||||
|
BeanUtils.copyProperties(user, userDTO);
|
||||||
|
userDTO.setPassword(null);
|
||||||
|
userDTO.setIsOnline(isUserOnline(user.getId()));
|
||||||
|
userDTO.setRole(user.getRole() == null ? resolveUserRole(user.getUsername()) : user.getRole());
|
||||||
|
userDTO.setAvatar("");
|
||||||
|
return userDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveUserRole(String username) {
|
||||||
|
return "admin".equalsIgnoreCase(username) ? "ADMIN" : "USER";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存用户token
|
* 缓存用户token
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user