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.FileUploadService;
|
||||
import com.org.flashsalesystem.service.OrderMigrationService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -28,6 +29,9 @@ public class AdminController {
|
||||
@Autowired
|
||||
private FileUploadService fileUploadService;
|
||||
|
||||
@Autowired
|
||||
private OrderMigrationService orderMigrationService;
|
||||
|
||||
/**
|
||||
* 获取仪表盘统计数据
|
||||
*/
|
||||
@@ -279,9 +283,10 @@ public class AdminController {
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) String category,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
try {
|
||||
Object products = adminService.getProducts(page, size, keyword, status);
|
||||
Object products = adminService.getProducts(page, size, keyword, category, status);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
@@ -488,4 +493,82 @@ public class AdminController {
|
||||
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}")
|
||||
public ResponseEntity<Map<String, Object>> getOrder(@PathVariable Long id, HttpServletRequest request) {
|
||||
try {
|
||||
Long userId = getCurrentUserId(request);
|
||||
if (userId == null) {
|
||||
UserDTO currentUser = getCurrentUser(request);
|
||||
if (currentUser == null) {
|
||||
return createUnauthorizedResponse();
|
||||
}
|
||||
|
||||
Long userId = currentUser.getId();
|
||||
OrderDTO order = orderService.getOrderById(id);
|
||||
|
||||
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<>();
|
||||
response.put("success", false);
|
||||
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) {
|
||||
// 更新订单状态为已支付
|
||||
OrderDTO updatedOrder = orderService.updateOrderStatus(id, 2, "模拟支付成功 - " + paymentMethod);
|
||||
OrderDTO updatedOrder = orderService.payOrder(id, userId, paymentMethod);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
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);
|
||||
if (session == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,8 @@ public class ProductController {
|
||||
@RequestParam(defaultValue = "12") int size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@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 = "desc") String sortDirection) {
|
||||
try {
|
||||
@@ -113,6 +115,8 @@ public class ProductController {
|
||||
queryDTO.setSize(size);
|
||||
queryDTO.setKeyword(keyword);
|
||||
queryDTO.setCategory(category);
|
||||
queryDTO.setMinPrice(minPrice);
|
||||
queryDTO.setMaxPrice(maxPrice);
|
||||
queryDTO.setSortBy(sortBy);
|
||||
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 java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 订单数据传输对象
|
||||
@@ -19,6 +20,8 @@ import java.time.LocalDateTime;
|
||||
public class OrderDTO {
|
||||
|
||||
private Long id;
|
||||
private String orderNo;
|
||||
private String groupNo;
|
||||
private Long userId;
|
||||
private String username;
|
||||
private Long productId;
|
||||
@@ -26,6 +29,11 @@ public class OrderDTO {
|
||||
private String productImageUrl;
|
||||
private Integer quantity;
|
||||
private BigDecimal totalPrice;
|
||||
private String receiverName;
|
||||
private String receiverPhone;
|
||||
private String receiverAddress;
|
||||
private String remark;
|
||||
private String paymentMethod;
|
||||
private Integer status;
|
||||
private String statusDescription;
|
||||
private Integer orderType;
|
||||
@@ -37,6 +45,38 @@ public class OrderDTO {
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -78,6 +78,8 @@ public class ProductDTO {
|
||||
@DecimalMin(value = "0.01", message = "商品价格必须大于0")
|
||||
private BigDecimal price;
|
||||
|
||||
private String category;
|
||||
|
||||
@Min(value = 0, message = "库存不能为负数")
|
||||
private Integer stock = 0;
|
||||
|
||||
@@ -94,6 +96,7 @@ public class ProductDTO {
|
||||
private String name;
|
||||
private String description;
|
||||
private BigDecimal price;
|
||||
private String category;
|
||||
private Integer stock;
|
||||
private String imageUrl;
|
||||
private Integer status;
|
||||
|
||||
@@ -47,6 +47,12 @@ public class UserDTO {
|
||||
@Schema(description = "是否在线", example = "true")
|
||||
private Boolean isOnline;
|
||||
|
||||
@Schema(description = "用户角色", example = "ADMIN")
|
||||
private String role;
|
||||
|
||||
@Schema(description = "头像地址")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createdAt;
|
||||
@@ -113,5 +119,25 @@ public class UserDTO {
|
||||
|
||||
@Size(max = 20, message = "手机号长度不能超过20个字符")
|
||||
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)
|
||||
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不能为空")
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private Long userId;
|
||||
@@ -58,6 +64,30 @@ public class Order {
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
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")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@@ -73,6 +103,9 @@ public class Order {
|
||||
protected void onCreate() {
|
||||
createdAt = LocalDateTime.now();
|
||||
updatedAt = LocalDateTime.now();
|
||||
if (orderNo == null || orderNo.trim().isEmpty()) {
|
||||
orderNo = "FS" + System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
|
||||
@@ -39,6 +39,9 @@ public class Product {
|
||||
@Column(nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal price;
|
||||
|
||||
@Column(length = 100)
|
||||
private String category;
|
||||
|
||||
@Min(value = 0, message = "库存不能为负数")
|
||||
@Column(nullable = false)
|
||||
private Integer stock = 0;
|
||||
@@ -60,6 +63,9 @@ public class Product {
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
if (category == null || category.trim().isEmpty()) {
|
||||
category = "默认分类";
|
||||
}
|
||||
createdAt = LocalDateTime.now();
|
||||
updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@@ -43,9 +43,15 @@ public class User {
|
||||
@Column(length = 20)
|
||||
private String phone;
|
||||
|
||||
@Column(length = 500)
|
||||
private String avatar;
|
||||
|
||||
@Column(name = "status", nullable = false)
|
||||
private Integer status = 1; // 1-正常, 0-禁用
|
||||
|
||||
@Column(name = "role", nullable = false, length = 20)
|
||||
private String role = "USER";
|
||||
|
||||
@Column(name = "last_login")
|
||||
private LocalDateTime lastLogin;
|
||||
|
||||
@@ -57,6 +63,9 @@ public class User {
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
if (role == null || role.trim().isEmpty()) {
|
||||
role = "USER";
|
||||
}
|
||||
createdAt = LocalDateTime.now();
|
||||
updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@@ -139,4 +139,11 @@ public interface OrderRepository extends JpaRepository<Order, Long> {
|
||||
".username LIKE %:keyword%")
|
||||
Page<Order> findByIdContainingOrUserUsernameContaining(@Param("keyword") String keyword1,
|
||||
@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);
|
||||
|
||||
/**
|
||||
* 商品综合搜索
|
||||
*/
|
||||
@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.entity.Order;
|
||||
import com.org.flashsalesystem.entity.Product;
|
||||
import com.org.flashsalesystem.entity.ProductReview;
|
||||
import com.org.flashsalesystem.entity.User;
|
||||
import com.org.flashsalesystem.entity.UserFavorite;
|
||||
import com.org.flashsalesystem.repository.FlashSaleRepository;
|
||||
import com.org.flashsalesystem.repository.OrderItemRepository;
|
||||
import com.org.flashsalesystem.repository.OrderRepository;
|
||||
import com.org.flashsalesystem.repository.ProductRepository;
|
||||
import com.org.flashsalesystem.repository.ProductReviewRepository;
|
||||
import com.org.flashsalesystem.repository.UserFavoriteRepository;
|
||||
import com.org.flashsalesystem.repository.UserRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
@@ -17,6 +22,9 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.File;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
@@ -40,12 +48,27 @@ public class AdminService {
|
||||
@Autowired
|
||||
private OrderRepository orderRepository;
|
||||
|
||||
@Autowired
|
||||
private OrderItemRepository orderItemRepository;
|
||||
|
||||
@Autowired
|
||||
private ProductReviewRepository productReviewRepository;
|
||||
|
||||
@Autowired
|
||||
private UserFavoriteRepository userFavoriteRepository;
|
||||
|
||||
@Autowired
|
||||
private FlashSaleRepository flashSaleRepository;
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
private RequestMetricsService requestMetricsService;
|
||||
|
||||
/**
|
||||
* 获取仪表盘统计数据
|
||||
*/
|
||||
@@ -315,6 +338,7 @@ public class AdminService {
|
||||
productMap.put("id", product.getId());
|
||||
productMap.put("name", product.getName());
|
||||
productMap.put("price", product.getPrice());
|
||||
productMap.put("category", product.getCategory());
|
||||
productMap.put("stock", product.getStock());
|
||||
productMap.put("sales", 0); // 暂时设为0,后续可以添加销量统计
|
||||
return productMap;
|
||||
@@ -347,6 +371,8 @@ public class AdminService {
|
||||
BeanUtils.copyProperties(user, dto);
|
||||
dto.setPassword(null); // 不返回密码
|
||||
dto.setIsOnline(redisService.sIsMember("online_users", user.getId().toString()));
|
||||
dto.setRole(user.getRole() == null ? ("admin".equalsIgnoreCase(user.getUsername()) ? "ADMIN" : "USER") : user.getRole());
|
||||
dto.setAvatar("");
|
||||
return dto;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
@@ -425,21 +451,19 @@ public class AdminService {
|
||||
/**
|
||||
* 获取商品列表
|
||||
*/
|
||||
public Object getProducts(int page, int size, String keyword, Integer status) {
|
||||
public Object getProducts(int page, int size, String keyword, String category, Integer status) {
|
||||
try {
|
||||
Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createdAt"));
|
||||
Page<Product> productPage;
|
||||
|
||||
if (keyword != null && !keyword.trim().isEmpty() && status != null) {
|
||||
// 同时按关键词和状态筛选
|
||||
productPage = productRepository.findByNameContainingAndStatus(keyword, status, pageable);
|
||||
} else if (keyword != null && !keyword.trim().isEmpty()) {
|
||||
productPage = productRepository.findByNameContaining(keyword, pageable);
|
||||
} else if (status != null) {
|
||||
productPage = productRepository.findByStatus(status, pageable);
|
||||
} else {
|
||||
productPage = productRepository.findAll(pageable);
|
||||
}
|
||||
productPage = productRepository.searchProducts(
|
||||
status,
|
||||
keyword != null && !keyword.trim().isEmpty() ? keyword.trim() : null,
|
||||
category != null && !category.trim().isEmpty() ? category.trim() : null,
|
||||
null,
|
||||
null,
|
||||
pageable
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<Map<String, Object>> productList = productPage.getContent().stream().map(product -> {
|
||||
@@ -447,6 +471,7 @@ public class AdminService {
|
||||
productMap.put("id", product.getId());
|
||||
productMap.put("name", product.getName());
|
||||
productMap.put("price", product.getPrice());
|
||||
productMap.put("category", product.getCategory());
|
||||
productMap.put("stock", product.getStock());
|
||||
productMap.put("status", product.getStatus());
|
||||
productMap.put("description", product.getDescription());
|
||||
@@ -482,27 +507,49 @@ public class AdminService {
|
||||
try {
|
||||
Map<String, Object> systemStatus = new HashMap<>();
|
||||
|
||||
// 获取JVM内存使用情况
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
long totalMemory = runtime.totalMemory();
|
||||
long freeMemory = runtime.freeMemory();
|
||||
long usedMemory = totalMemory - freeMemory;
|
||||
double memoryUsage = (double) usedMemory / totalMemory * 100;
|
||||
double memoryUsage = totalMemory == 0 ? 0 : (double) usedMemory / totalMemory * 100;
|
||||
|
||||
// 获取可用处理器数量(模拟CPU使用率)
|
||||
int availableProcessors = runtime.availableProcessors();
|
||||
double cpuUsage = Math.random() * 30 + 20; // 模拟20-50%的CPU使用率
|
||||
double cpuUsage = 0;
|
||||
java.lang.management.OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
|
||||
if (osBean instanceof com.sun.management.OperatingSystemMXBean) {
|
||||
cpuUsage = ((com.sun.management.OperatingSystemMXBean) osBean).getSystemCpuLoad() * 100;
|
||||
if (cpuUsage < 0) {
|
||||
cpuUsage = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟磁盘使用率
|
||||
double diskUsage = Math.random() * 40 + 10; // 模拟10-50%的磁盘使用率
|
||||
File root = new File("/");
|
||||
long totalSpace = root.getTotalSpace();
|
||||
long usableSpace = root.getUsableSpace();
|
||||
long usedSpace = totalSpace - usableSpace;
|
||||
double diskUsage = totalSpace == 0 ? 0 : (double) usedSpace / totalSpace * 100;
|
||||
|
||||
systemStatus.put("status", "正常");
|
||||
boolean dbHealthy = false;
|
||||
try (java.sql.Connection connection = dataSource.getConnection()) {
|
||||
dbHealthy = connection.isValid(2);
|
||||
}
|
||||
|
||||
String redisPing = redisService.ping();
|
||||
boolean redisHealthy = "PONG".equalsIgnoreCase(redisPing);
|
||||
|
||||
String requestCountKey = "request_count:" + LocalDate.now().format(java.time.format.DateTimeFormatter.BASIC_ISO_DATE);
|
||||
Object requestCountValue = redisService.get(requestCountKey);
|
||||
long requestCountToday = requestCountValue == null ? requestMetricsService.getTotalRequests() : Long.parseLong(requestCountValue.toString());
|
||||
|
||||
systemStatus.put("status", dbHealthy && redisHealthy ? "正常" : "异常");
|
||||
systemStatus.put("cpuUsage", Math.round(cpuUsage));
|
||||
systemStatus.put("memoryUsage", Math.round(memoryUsage));
|
||||
systemStatus.put("diskUsage", Math.round(diskUsage));
|
||||
systemStatus.put("availableProcessors", availableProcessors);
|
||||
systemStatus.put("availableProcessors", runtime.availableProcessors());
|
||||
systemStatus.put("totalMemory", totalMemory / 1024 / 1024 + "MB");
|
||||
systemStatus.put("usedMemory", usedMemory / 1024 / 1024 + "MB");
|
||||
systemStatus.put("dbStatus", dbHealthy ? "正常" : "异常");
|
||||
systemStatus.put("redisStatus", redisHealthy ? "正常" : "异常");
|
||||
systemStatus.put("requestCountToday", requestCountToday);
|
||||
|
||||
return systemStatus;
|
||||
} catch (Exception e) {
|
||||
@@ -512,6 +559,9 @@ public class AdminService {
|
||||
errorStatus.put("cpuUsage", 0);
|
||||
errorStatus.put("memoryUsage", 0);
|
||||
errorStatus.put("diskUsage", 0);
|
||||
errorStatus.put("dbStatus", "异常");
|
||||
errorStatus.put("redisStatus", "异常");
|
||||
errorStatus.put("requestCountToday", requestMetricsService.getTotalRequests());
|
||||
return errorStatus;
|
||||
}
|
||||
}
|
||||
@@ -522,21 +572,16 @@ public class AdminService {
|
||||
public Object getRedisStatus() {
|
||||
try {
|
||||
List<Map<String, Object>> redisNodes = new ArrayList<>();
|
||||
Properties memoryInfo = redisService.info("memory");
|
||||
Properties clientInfo = redisService.info("clients");
|
||||
String ping = redisService.ping();
|
||||
|
||||
// 模拟Redis集群节点状态
|
||||
String[] nodes = {
|
||||
"42.192.62.91:7000", "42.192.62.91:7001", "42.192.62.91:7002",
|
||||
"42.192.62.91:7003", "42.192.62.91:7004", "42.192.62.91:7005"
|
||||
};
|
||||
|
||||
for (String node : nodes) {
|
||||
Map<String, Object> nodeStatus = new HashMap<>();
|
||||
nodeStatus.put("node", node);
|
||||
nodeStatus.put("status", "正常");
|
||||
nodeStatus.put("memory", (200 + (int) (Math.random() * 100)) + "MB");
|
||||
nodeStatus.put("connections", 30 + (int) (Math.random() * 30));
|
||||
nodeStatus.put("node", "default");
|
||||
nodeStatus.put("status", "PONG".equalsIgnoreCase(ping) ? "正常" : "异常");
|
||||
nodeStatus.put("memory", memoryInfo.getProperty("used_memory_human", "unknown"));
|
||||
nodeStatus.put("connections", Integer.parseInt(clientInfo.getProperty("connected_clients", "0")));
|
||||
redisNodes.add(nodeStatus);
|
||||
}
|
||||
|
||||
return redisNodes;
|
||||
} catch (Exception e) {
|
||||
@@ -564,11 +609,16 @@ public class AdminService {
|
||||
productMap.put("createdAt", product.getCreatedAt());
|
||||
productMap.put("updatedAt", product.getUpdatedAt());
|
||||
|
||||
// 添加统计信息(模拟数据,实际应该从统计表获取)
|
||||
productMap.put("totalSales", 0); // 总销量
|
||||
productMap.put("totalRevenue", 0.0); // 总收入
|
||||
productMap.put("viewCount", 0); // 浏览次数
|
||||
productMap.put("rating", 0.0); // 平均评分
|
||||
long totalSales = orderItemRepository.countByProductId(product.getId());
|
||||
java.math.BigDecimal totalRevenue = orderItemRepository.sumSubtotalByProductId(product.getId());
|
||||
Double averageRating = productReviewRepository.findAverageRatingByProductId(product.getId());
|
||||
long reviewCount = productReviewRepository.countByProductId(product.getId());
|
||||
|
||||
productMap.put("totalSales", totalSales);
|
||||
productMap.put("totalRevenue", totalRevenue == null ? 0.0 : totalRevenue);
|
||||
productMap.put("viewCount", 0);
|
||||
productMap.put("rating", averageRating == null ? 0.0 : averageRating);
|
||||
productMap.put("reviewCount", reviewCount);
|
||||
|
||||
return productMap;
|
||||
} else {
|
||||
@@ -598,6 +648,9 @@ public class AdminService {
|
||||
if (productData.containsKey("stock")) {
|
||||
product.setStock(Integer.parseInt(productData.get("stock").toString()));
|
||||
}
|
||||
if (productData.containsKey("category")) {
|
||||
product.setCategory((String) productData.get("category"));
|
||||
}
|
||||
if (productData.containsKey("status")) {
|
||||
product.setStatus(Integer.parseInt(productData.get("status").toString()));
|
||||
}
|
||||
@@ -643,6 +696,7 @@ public class AdminService {
|
||||
Product product = new Product();
|
||||
product.setName((String) productData.get("name"));
|
||||
product.setPrice(new BigDecimal(productData.get("price").toString()));
|
||||
product.setCategory((String) productData.getOrDefault("category", "默认分类"));
|
||||
product.setStock(Integer.parseInt(productData.get("stock").toString()));
|
||||
product.setStatus(Integer.parseInt(productData.get("status").toString()));
|
||||
product.setDescription((String) productData.get("description"));
|
||||
@@ -656,6 +710,7 @@ public class AdminService {
|
||||
result.put("id", savedProduct.getId());
|
||||
result.put("name", savedProduct.getName());
|
||||
result.put("price", savedProduct.getPrice());
|
||||
result.put("category", savedProduct.getCategory());
|
||||
result.put("stock", savedProduct.getStock());
|
||||
result.put("status", savedProduct.getStatus());
|
||||
result.put("description", savedProduct.getDescription());
|
||||
@@ -668,4 +723,133 @@ public class AdminService {
|
||||
throw new RuntimeException("添加商品失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> getReviewStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
List<ProductReview> reviews = productReviewRepository.findAll();
|
||||
LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
|
||||
long todayReviews = reviews.stream().filter(item -> item.getCreatedAt() != null && item.getCreatedAt().isAfter(startOfDay)).count();
|
||||
double averageRating = reviews.isEmpty() ? 0.0 : reviews.stream().mapToInt(ProductReview::getRating).average().orElse(0.0);
|
||||
stats.put("totalReviews", reviews.size());
|
||||
stats.put("todayReviews", todayReviews);
|
||||
stats.put("averageRating", averageRating);
|
||||
stats.put("fiveStarReviews", reviews.stream().filter(item -> item.getRating() == 5).count());
|
||||
return stats;
|
||||
}
|
||||
|
||||
public Map<String, Object> getFavoriteStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
List<UserFavorite> favorites = userFavoriteRepository.findAll();
|
||||
stats.put("totalFavorites", favorites.size());
|
||||
stats.put("favoriteUsers", favorites.stream().map(UserFavorite::getUserId).distinct().count());
|
||||
stats.put("favoriteProducts", favorites.stream().map(UserFavorite::getProductId).distinct().count());
|
||||
stats.put("todayFavorites", favorites.stream().filter(item -> item.getCreatedAt() != null && item.getCreatedAt().isAfter(LocalDate.now().atStartOfDay())).count());
|
||||
return stats;
|
||||
}
|
||||
|
||||
public Map<String, Object> getReviews(int page, int size, String keyword) {
|
||||
List<Map<String, Object>> rows = productReviewRepository.findAll(Sort.by(Sort.Direction.DESC, "createdAt"))
|
||||
.stream()
|
||||
.map(review -> {
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
item.put("id", review.getId());
|
||||
item.put("productId", review.getProductId());
|
||||
item.put("userId", review.getUserId());
|
||||
item.put("orderId", review.getOrderId());
|
||||
item.put("rating", review.getRating());
|
||||
item.put("content", review.getContent());
|
||||
item.put("status", review.getStatus());
|
||||
item.put("statusText", review.getStatus() != null && review.getStatus() == 1 ? "显示" : "隐藏");
|
||||
item.put("adminReply", review.getAdminReply());
|
||||
item.put("repliedAt", review.getRepliedAt());
|
||||
item.put("createdAt", review.getCreatedAt());
|
||||
Product product = productRepository.findById(review.getProductId()).orElse(null);
|
||||
User user = userRepository.findById(review.getUserId()).orElse(null);
|
||||
item.put("productName", product != null ? product.getName() : "未知商品");
|
||||
item.put("username", user != null ? user.getUsername() : "未知用户");
|
||||
return item;
|
||||
})
|
||||
.filter(item -> {
|
||||
if (keyword == null || keyword.trim().isEmpty()) return true;
|
||||
String value = keyword.trim().toLowerCase();
|
||||
return String.valueOf(item.get("productName")).toLowerCase().contains(value)
|
||||
|| String.valueOf(item.get("username")).toLowerCase().contains(value)
|
||||
|| String.valueOf(item.get("content")).toLowerCase().contains(value);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
return paginate(rows, page, size, "reviews");
|
||||
}
|
||||
|
||||
public Object updateReview(Long id, com.org.flashsalesystem.dto.ProductReviewDTO.UpdateDTO updateDTO) {
|
||||
ProductReview review = productReviewRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("评价不存在"));
|
||||
|
||||
if (updateDTO.getStatus() != null) {
|
||||
review.setStatus(updateDTO.getStatus());
|
||||
}
|
||||
if (updateDTO.getAdminReply() != null) {
|
||||
review.setAdminReply(updateDTO.getAdminReply());
|
||||
review.setRepliedAt(java.time.LocalDateTime.now());
|
||||
}
|
||||
|
||||
review = productReviewRepository.save(review);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("id", review.getId());
|
||||
result.put("status", review.getStatus());
|
||||
result.put("statusText", review.getStatus() != null && review.getStatus() == 1 ? "显示" : "隐藏");
|
||||
result.put("adminReply", review.getAdminReply());
|
||||
result.put("repliedAt", review.getRepliedAt());
|
||||
return result;
|
||||
}
|
||||
|
||||
public void deleteReview(Long id) {
|
||||
productReviewRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public Map<String, Object> getFavorites(int page, int size, String keyword) {
|
||||
List<Map<String, Object>> rows = userFavoriteRepository.findAll(Sort.by(Sort.Direction.DESC, "createdAt"))
|
||||
.stream()
|
||||
.map(favorite -> {
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
item.put("id", favorite.getId());
|
||||
item.put("userId", favorite.getUserId());
|
||||
item.put("productId", favorite.getProductId());
|
||||
item.put("createdAt", favorite.getCreatedAt());
|
||||
Product product = productRepository.findById(favorite.getProductId()).orElse(null);
|
||||
User user = userRepository.findById(favorite.getUserId()).orElse(null);
|
||||
item.put("productName", product != null ? product.getName() : "未知商品");
|
||||
item.put("productCategory", product != null ? product.getCategory() : "默认分类");
|
||||
item.put("username", user != null ? user.getUsername() : "未知用户");
|
||||
return item;
|
||||
})
|
||||
.filter(item -> {
|
||||
if (keyword == null || keyword.trim().isEmpty()) return true;
|
||||
String value = keyword.trim().toLowerCase();
|
||||
return String.valueOf(item.get("productName")).toLowerCase().contains(value)
|
||||
|| String.valueOf(item.get("username")).toLowerCase().contains(value);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
return paginate(rows, page, size, "favorites");
|
||||
}
|
||||
|
||||
public void deleteFavorite(Long id) {
|
||||
userFavoriteRepository.deleteById(id);
|
||||
}
|
||||
|
||||
private Map<String, Object> paginate(List<Map<String, Object>> rows, int page, int size, String key) {
|
||||
int currentPage = Math.max(page, 1);
|
||||
int pageSize = Math.max(size, 1);
|
||||
int fromIndex = Math.min((currentPage - 1) * pageSize, rows.size());
|
||||
int toIndex = Math.min(fromIndex + pageSize, rows.size());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put(key, rows.subList(fromIndex, toIndex));
|
||||
result.put("total", rows.size());
|
||||
result.put("totalPages", (int) Math.ceil(rows.size() * 1.0 / pageSize));
|
||||
result.put("currentPage", currentPage);
|
||||
result.put("size", pageSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -355,42 +355,29 @@ public class CartService {
|
||||
.map(CartDTO.CartItemDTO::getSubtotal)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
// 根据商品数量创建订单
|
||||
if (itemsToOrder.size() == 1) {
|
||||
// 单商品订单
|
||||
CartDTO.CartItemDTO item = itemsToOrder.get(0);
|
||||
OrderDTO.CreateDTO createDTO = new OrderDTO.CreateDTO();
|
||||
createDTO.setProductId(item.getProductId());
|
||||
createDTO.setQuantity(item.getQuantity());
|
||||
|
||||
OrderDTO order = orderService.createOrder(userId, createDTO);
|
||||
|
||||
// 从购物车中移除已下单的商品
|
||||
redisService.hDel(cartKey, item.getProductId().toString());
|
||||
|
||||
log.info("单商品购物车下单成功: 用户ID={}, 订单ID={}", userId, order.getId());
|
||||
return order;
|
||||
} else {
|
||||
// 多商品订单 - 创建多个订单
|
||||
List<OrderDTO> orders = new ArrayList<>();
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
Order order = new Order();
|
||||
order.setOrderNo("FS" + System.currentTimeMillis() + String.format("%03d", new java.util.Random().nextInt(1000)));
|
||||
order.setUserId(userId);
|
||||
order.setProductId(flashSale.getProductId());
|
||||
order.setQuantity(participateDTO.getQuantity());
|
||||
order.setTotalPrice(flashSale.getFlashPrice().multiply(BigDecimal.valueOf(participateDTO.getQuantity())));
|
||||
order.setStatus(1); // 待支付
|
||||
order.setOrderType(2); // 秒杀订单
|
||||
order.setReceiverPhone(participateDTO.getPhone());
|
||||
order.setReceiverAddress(participateDTO.getAddress());
|
||||
order.setRemark("秒杀订单");
|
||||
|
||||
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.UserDTO;
|
||||
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.ProductRepository;
|
||||
import com.org.flashsalesystem.repository.UserAddressRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -37,6 +41,8 @@ public class OrderService {
|
||||
@Autowired
|
||||
private OrderRepository orderRepository;
|
||||
@Autowired
|
||||
private OrderItemRepository orderItemRepository;
|
||||
@Autowired
|
||||
private ProductRepository productRepository;
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
@@ -44,12 +50,19 @@ public class OrderService {
|
||||
private ProductService productService;
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
@Autowired
|
||||
private UserAddressRepository userAddressRepository;
|
||||
|
||||
/**
|
||||
* 创建普通订单
|
||||
*/
|
||||
@Transactional
|
||||
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());
|
||||
|
||||
// 验证商品
|
||||
@@ -72,14 +85,19 @@ public class OrderService {
|
||||
|
||||
// 创建订单
|
||||
Order order = new Order();
|
||||
order.setOrderNo(generateOrderNo());
|
||||
order.setGroupNo(groupNo);
|
||||
order.setUserId(userId);
|
||||
order.setProductId(createDTO.getProductId());
|
||||
order.setQuantity(createDTO.getQuantity());
|
||||
order.setTotalPrice(totalPrice);
|
||||
order.setStatus(1); // 待支付
|
||||
order.setOrderType(1); // 普通订单
|
||||
order.setRemark(createDTO.getRemark());
|
||||
fillOrderAddress(order, userId, createDTO.getReceiverName(), createDTO.getReceiverPhone(), createDTO.getReceiverAddress());
|
||||
|
||||
order = orderRepository.save(order);
|
||||
createOrderItem(order, product, createDTO.getQuantity());
|
||||
|
||||
// 扣减库存
|
||||
boolean stockUpdated = productService.updateStock(createDTO.getProductId(), createDTO.getQuantity(),
|
||||
@@ -102,6 +120,66 @@ public class OrderService {
|
||||
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获取订单
|
||||
*/
|
||||
@@ -129,6 +207,11 @@ public class OrderService {
|
||||
// 缓存订单信息
|
||||
cacheOrderInfo(order);
|
||||
|
||||
if (order.getGroupNo() != null && !order.getGroupNo().trim().isEmpty()) {
|
||||
List<Order> groupOrders = orderRepository.findByGroupNoOrderByCreatedAtAsc(order.getGroupNo());
|
||||
return buildGroupedOrderDTO(groupOrders, order);
|
||||
}
|
||||
|
||||
return buildOrderDTO(order);
|
||||
}
|
||||
|
||||
@@ -150,9 +233,7 @@ public class OrderService {
|
||||
}
|
||||
|
||||
// 转换为DTO
|
||||
List<OrderDTO> orderDTOs = orderPage.getContent().stream()
|
||||
.map(this::buildOrderDTO)
|
||||
.collect(Collectors.toList());
|
||||
List<OrderDTO> orderDTOs = aggregateOrders(orderPage.getContent());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("content", orderDTOs);
|
||||
@@ -164,6 +245,16 @@ public class OrderService {
|
||||
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
|
||||
List<OrderDTO> orderDTOs = orderPage.getContent().stream()
|
||||
.map(this::buildOrderDTO)
|
||||
.collect(Collectors.toList());
|
||||
List<OrderDTO> orderDTOs = aggregateOrders(orderPage.getContent());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("content", orderDTOs);
|
||||
@@ -222,6 +311,18 @@ public class OrderService {
|
||||
|
||||
// 更新状态
|
||||
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);
|
||||
|
||||
// 更新缓存
|
||||
@@ -238,6 +339,37 @@ public class OrderService {
|
||||
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.setRemark("用户取消订单");
|
||||
order = orderRepository.save(order);
|
||||
|
||||
// 恢复库存
|
||||
@@ -328,6 +461,35 @@ public class OrderService {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
String cacheKey = ORDER_CACHE_PREFIX + order.getId();
|
||||
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("productId", order.getProductId().toString());
|
||||
orderMap.put("quantity", order.getQuantity().toString());
|
||||
orderMap.put("totalPrice", order.getTotalPrice().toString());
|
||||
orderMap.put("status", order.getStatus().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("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.expire(cacheKey, 24, TimeUnit.HOURS);
|
||||
@@ -396,19 +599,35 @@ public class OrderService {
|
||||
private OrderDTO buildOrderDTOFromCache(Long orderId, Map<Object, Object> orderMap) {
|
||||
OrderDTO orderDTO = new OrderDTO();
|
||||
orderDTO.setId(orderId);
|
||||
orderDTO.setOrderNo((String) orderMap.get("orderNo"));
|
||||
orderDTO.setGroupNo((String) orderMap.get("groupNo"));
|
||||
orderDTO.setUserId(Long.valueOf((String) orderMap.get("userId")));
|
||||
orderDTO.setProductId(Long.valueOf((String) orderMap.get("productId")));
|
||||
orderDTO.setQuantity(Integer.valueOf((String) orderMap.get("quantity")));
|
||||
orderDTO.setTotalPrice(new BigDecimal((String) orderMap.get("totalPrice")));
|
||||
orderDTO.setStatus(Integer.valueOf((String) orderMap.get("status")));
|
||||
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")));
|
||||
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.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());
|
||||
if (user != null) {
|
||||
orderDTO.setUsername(user.getUsername());
|
||||
@@ -434,6 +653,8 @@ public class OrderService {
|
||||
orderDTO.setStatusDescription(getStatusDescription(order.getStatus()));
|
||||
orderDTO.setOrderTypeDescription(getOrderTypeDescription(order.getOrderType()));
|
||||
|
||||
orderDTO.setItems(buildOrderItems(order.getId(), order.getProductId(), null, null, order.getQuantity(), order.getTotalPrice()));
|
||||
|
||||
// 获取用户信息
|
||||
UserDTO user = userService.getUserById(order.getUserId());
|
||||
if (user != null) {
|
||||
@@ -447,6 +668,60 @@ public class OrderService {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -510,6 +785,41 @@ public class OrderService {
|
||||
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();
|
||||
BeanUtils.copyProperties(createDTO, product);
|
||||
product.setStatus(1); // 默认上架
|
||||
if (product.getCategory() == null || product.getCategory().trim().isEmpty()) {
|
||||
product.setCategory("默认分类");
|
||||
}
|
||||
|
||||
product = productRepository.save(product);
|
||||
|
||||
@@ -121,22 +124,22 @@ public class ProductService {
|
||||
Sort sort = Sort.by(Sort.Direction.fromString(queryDTO.getSortDirection()), queryDTO.getSortBy());
|
||||
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;
|
||||
|
||||
// 根据查询条件获取数据
|
||||
if (queryDTO.getName() != null && !queryDTO.getName().trim().isEmpty()) {
|
||||
productPage = productRepository.findByStatus(1, pageable)
|
||||
.map(product -> {
|
||||
if (product.getName().contains(queryDTO.getName())) {
|
||||
return product;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.map(product -> product);
|
||||
} else {
|
||||
productPage = productRepository.findByStatus(queryDTO.getStatus() != null ? queryDTO.getStatus() : 1,
|
||||
pageable);
|
||||
}
|
||||
Page<Product> productPage = productRepository.searchProducts(
|
||||
status,
|
||||
keyword,
|
||||
category,
|
||||
queryDTO.getMinPrice(),
|
||||
queryDTO.getMaxPrice(),
|
||||
pageable
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<ProductDTO> productDTOs = productPage.getContent().stream()
|
||||
@@ -219,6 +222,9 @@ public class ProductService {
|
||||
if (updateDTO.getPrice() != null) {
|
||||
product.setPrice(updateDTO.getPrice());
|
||||
}
|
||||
if (updateDTO.getCategory() != null) {
|
||||
product.setCategory(updateDTO.getCategory());
|
||||
}
|
||||
if (updateDTO.getStock() != null) {
|
||||
product.setStock(updateDTO.getStock());
|
||||
// 同步更新Redis中的库存
|
||||
@@ -400,6 +406,7 @@ public class ProductService {
|
||||
productMap.put("name", product.getName());
|
||||
productMap.put("description", product.getDescription());
|
||||
productMap.put("price", product.getPrice().toString());
|
||||
productMap.put("category", product.getCategory() == null ? "" : product.getCategory());
|
||||
productMap.put("stock", product.getStock().toString());
|
||||
productMap.put("imageUrl", product.getImageUrl());
|
||||
productMap.put("status", product.getStatus().toString());
|
||||
@@ -417,6 +424,7 @@ public class ProductService {
|
||||
productDTO.setName((String) productMap.get("name"));
|
||||
productDTO.setDescription((String) productMap.get("description"));
|
||||
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.setImageUrl((String) productMap.get("imageUrl"));
|
||||
productDTO.setStatus(Integer.valueOf((String) productMap.get("status")));
|
||||
@@ -429,6 +437,10 @@ public class ProductService {
|
||||
private String buildProductListCacheKey(ProductDTO.QueryDTO queryDTO) {
|
||||
return PRODUCT_LIST_CACHE_PREFIX +
|
||||
(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.getPage() + ":" + queryDTO.getSize() + ":" +
|
||||
queryDTO.getSortBy() + ":" + queryDTO.getSortDirection();
|
||||
@@ -443,6 +455,13 @@ public class ProductService {
|
||||
redisService.delete(HOT_PRODUCTS_CACHE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类列表
|
||||
*/
|
||||
public List<String> getCategories() {
|
||||
return productRepository.findDistinctCategories();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除商品
|
||||
*/
|
||||
|
||||
@@ -326,6 +326,24 @@ public class RedisService {
|
||||
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;
|
||||
|
||||
import com.org.flashsalesystem.dto.UserDTO;
|
||||
import com.org.flashsalesystem.entity.Order;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
@@ -11,6 +14,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -34,6 +38,10 @@ public class UserService {
|
||||
private UserRepository userRepository;
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
@Autowired
|
||||
private OrderRepository orderRepository;
|
||||
@Autowired
|
||||
private UserFavoriteRepository userFavoriteRepository;
|
||||
@Value("${flashsale.cache.user-expire-minutes:30}")
|
||||
private int userCacheExpireMinutes;
|
||||
|
||||
@@ -70,6 +78,8 @@ public class UserService {
|
||||
user.setPassword(encryptPassword(registerDTO.getPassword()));
|
||||
user.setEmail(registerDTO.getEmail());
|
||||
user.setPhone(registerDTO.getPhone());
|
||||
user.setAvatar("");
|
||||
user.setRole("admin".equalsIgnoreCase(registerDTO.getUsername()) ? "ADMIN" : "USER");
|
||||
|
||||
// 保存到数据库
|
||||
user = userRepository.save(user);
|
||||
@@ -77,10 +87,7 @@ public class UserService {
|
||||
// 缓存用户信息
|
||||
cacheUserInfo(user);
|
||||
|
||||
// 转换为DTO返回
|
||||
UserDTO userDTO = new UserDTO();
|
||||
BeanUtils.copyProperties(user, userDTO);
|
||||
userDTO.setPassword(null); // 不返回密码
|
||||
UserDTO userDTO = buildSafeUserDTO(user);
|
||||
|
||||
log.info("用户注册成功: {}, ID: {}", user.getUsername(), user.getId());
|
||||
return userDTO;
|
||||
@@ -105,6 +112,10 @@ public class UserService {
|
||||
throw new RuntimeException("用户名或密码错误");
|
||||
}
|
||||
|
||||
// 更新最后登录时间
|
||||
user.setLastLogin(LocalDateTime.now());
|
||||
user = userRepository.save(user);
|
||||
|
||||
// 生成token
|
||||
String token = generateToken();
|
||||
|
||||
@@ -116,9 +127,7 @@ public class UserService {
|
||||
redisService.sAdd(ONLINE_USERS_SET, user.getId());
|
||||
|
||||
// 转换为DTO
|
||||
UserDTO userDTO = new UserDTO();
|
||||
BeanUtils.copyProperties(user, userDTO);
|
||||
userDTO.setPassword(null);
|
||||
UserDTO userDTO = buildSafeUserDTO(user);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("token", token);
|
||||
@@ -182,13 +191,7 @@ public class UserService {
|
||||
Map<Object, Object> userMap = redisService.hGetAll(cacheKey);
|
||||
|
||||
if (!userMap.isEmpty()) {
|
||||
// 从缓存构造用户对象
|
||||
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;
|
||||
return buildUserDTOFromCache(userId, userMap);
|
||||
}
|
||||
|
||||
// 缓存中没有,从数据库获取
|
||||
@@ -202,12 +205,7 @@ public class UserService {
|
||||
// 缓存用户信息
|
||||
cacheUserInfo(user);
|
||||
|
||||
// 转换为DTO
|
||||
UserDTO userDTO = new UserDTO();
|
||||
BeanUtils.copyProperties(user, userDTO);
|
||||
userDTO.setPassword(null);
|
||||
|
||||
return userDTO;
|
||||
return buildSafeUserDTO(user);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,21 +238,72 @@ public class UserService {
|
||||
user.setPhone(updateDTO.getPhone());
|
||||
}
|
||||
|
||||
if (updateDTO.getAvatar() != null) {
|
||||
user.setAvatar(updateDTO.getAvatar());
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
user = userRepository.save(user);
|
||||
|
||||
// 更新缓存
|
||||
cacheUserInfo(user);
|
||||
|
||||
// 转换为DTO
|
||||
UserDTO userDTO = new UserDTO();
|
||||
BeanUtils.copyProperties(user, userDTO);
|
||||
userDTO.setPassword(null);
|
||||
UserDTO userDTO = buildSafeUserDTO(user);
|
||||
|
||||
log.info("用户信息更新成功: {}", userId);
|
||||
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();
|
||||
Map<String, Object> userMap = new HashMap<>();
|
||||
userMap.put("username", user.getUsername());
|
||||
userMap.put("email", user.getEmail());
|
||||
userMap.put("phone", user.getPhone());
|
||||
userMap.put("email", user.getEmail() == null ? "" : user.getEmail());
|
||||
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.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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user