Initial commit
This commit is contained in:
59
backend/pom.xml
Normal file
59
backend/pom.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.maternalmall</groupId>
|
||||
<artifactId>backend</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<name>maternal-mall-backend</name>
|
||||
<description>Maternal mall backend</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.3.5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.maternalmall;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MaternalMallApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MaternalMallApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.maternalmall.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ApiResponse<T> {
|
||||
private int code;
|
||||
private String message;
|
||||
private T data;
|
||||
|
||||
public static <T> ApiResponse<T> ok(T data) {
|
||||
return new ApiResponse<>(0, "ok", data);
|
||||
}
|
||||
|
||||
public static ApiResponse<Void> ok() {
|
||||
return new ApiResponse<>(0, "ok", null);
|
||||
}
|
||||
|
||||
public static ApiResponse<Void> fail(String message) {
|
||||
return new ApiResponse<>(-1, message, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.maternalmall.common;
|
||||
|
||||
public class BizException extends RuntimeException {
|
||||
public BizException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.maternalmall.common;
|
||||
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(BizException.class)
|
||||
public ApiResponse<Void> handleBiz(BizException ex) {
|
||||
return ApiResponse.fail(ex.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ApiResponse<Void> handleValid(MethodArgumentNotValidException ex) {
|
||||
String msg = ex.getBindingResult().getFieldErrors().stream().findFirst()
|
||||
.map(e -> e.getField() + ":" + e.getDefaultMessage()).orElse("参数错误");
|
||||
return ApiResponse.fail(msg);
|
||||
}
|
||||
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
public ApiResponse<Void> handleConstraint(ConstraintViolationException ex) {
|
||||
return ApiResponse.fail(ex.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ApiResponse<Void> handleUnknown(Exception ex) {
|
||||
return ApiResponse.fail(ex.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.maternalmall.config;
|
||||
|
||||
import com.maternalmall.domain.User;
|
||||
|
||||
public class AuthContext {
|
||||
public static final String CURRENT_USER = "CURRENT_USER";
|
||||
|
||||
public static User getUser(jakarta.servlet.http.HttpServletRequest request) {
|
||||
return (User) request.getAttribute(CURRENT_USER);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.maternalmall.config;
|
||||
|
||||
import com.maternalmall.domain.User;
|
||||
import com.maternalmall.repository.UserRepository;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
public class AuthInterceptor implements HandlerInterceptor {
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@Value("${app.token-header:X-Token}")
|
||||
private String tokenHeader;
|
||||
|
||||
private static final Set<String> PUBLIC_PREFIX = Set.of(
|
||||
"/api/auth/login",
|
||||
"/api/auth/register",
|
||||
"/api/public"
|
||||
);
|
||||
|
||||
public AuthInterceptor(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// Let browser CORS preflight pass without auth token.
|
||||
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
return true;
|
||||
}
|
||||
|
||||
String uri = request.getRequestURI();
|
||||
if (uri.equals("/") || uri.startsWith("/error") || PUBLIC_PREFIX.stream().anyMatch(uri::startsWith)) {
|
||||
return true;
|
||||
}
|
||||
String token = request.getHeader(tokenHeader);
|
||||
if (token == null || token.isBlank()) {
|
||||
response.setStatus(401);
|
||||
response.getWriter().write("{\"code\":401,\"message\":\"请先登录\",\"data\":null}");
|
||||
return false;
|
||||
}
|
||||
User user = userRepository.findByToken(token).orElse(null);
|
||||
if (user == null || !Boolean.TRUE.equals(user.getEnabled())) {
|
||||
response.setStatus(401);
|
||||
response.getWriter().write("{\"code\":401,\"message\":\"登录状态无效\",\"data\":null}");
|
||||
return false;
|
||||
}
|
||||
request.setAttribute(AuthContext.CURRENT_USER, user);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.maternalmall.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
private final AuthInterceptor authInterceptor;
|
||||
|
||||
public WebMvcConfig(AuthInterceptor authInterceptor) {
|
||||
this.authInterceptor = authInterceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(authInterceptor).addPathPatterns("/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedOrigins("*").allowedMethods("*").allowedHeaders("*");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package com.maternalmall.controller;
|
||||
|
||||
import com.maternalmall.common.ApiResponse;
|
||||
import com.maternalmall.config.AuthContext;
|
||||
import com.maternalmall.domain.*;
|
||||
import com.maternalmall.service.MallService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/admin")
|
||||
public class AdminController {
|
||||
private final MallService mallService;
|
||||
|
||||
public AdminController(MallService mallService) {
|
||||
this.mallService = mallService;
|
||||
}
|
||||
|
||||
private void check(User user) {
|
||||
mallService.requireRole(user, UserRole.ADMIN);
|
||||
}
|
||||
|
||||
@GetMapping("/overview")
|
||||
public ApiResponse<Map<String, Object>> overview(HttpServletRequest request) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminOverview());
|
||||
}
|
||||
|
||||
@GetMapping("/orders")
|
||||
public ApiResponse<List<Orders>> orders(HttpServletRequest request) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminOrders());
|
||||
}
|
||||
|
||||
@PutMapping("/orders/{id}")
|
||||
public ApiResponse<Orders> updateOrder(HttpServletRequest request, @PathVariable Long id, @RequestBody Map<String, String> body) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminUpdateOrder(id, body.get("status"), body.getOrDefault("logisticsInfo", "")));
|
||||
}
|
||||
|
||||
@GetMapping("/orders/risk")
|
||||
public ApiResponse<List<Map<String, Object>>> riskOrders(HttpServletRequest request) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminRiskOrders());
|
||||
}
|
||||
|
||||
@PutMapping("/orders/{id}/refund-audit")
|
||||
public ApiResponse<Orders> auditRefund(HttpServletRequest request, @PathVariable Long id, @RequestBody Map<String, Object> body) {
|
||||
check(AuthContext.getUser(request));
|
||||
boolean approve = Boolean.parseBoolean(String.valueOf(body.getOrDefault("approve", false)));
|
||||
String remark = String.valueOf(body.getOrDefault("remark", ""));
|
||||
return ApiResponse.ok(mallService.adminAuditRefund(id, approve, remark));
|
||||
}
|
||||
|
||||
@PutMapping("/orders/{id}/ship-audit")
|
||||
public ApiResponse<Orders> auditShipment(HttpServletRequest request, @PathVariable Long id, @RequestBody Map<String, Object> body) {
|
||||
check(AuthContext.getUser(request));
|
||||
boolean approve = Boolean.parseBoolean(String.valueOf(body.getOrDefault("approve", true)));
|
||||
String remark = String.valueOf(body.getOrDefault("remark", ""));
|
||||
return ApiResponse.ok(mallService.adminAuditShipment(id, approve, remark));
|
||||
}
|
||||
|
||||
@GetMapping("/merchant-applications")
|
||||
public ApiResponse<List<Map<String, Object>>> merchantApplications(HttpServletRequest request) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminMerchantApplications());
|
||||
}
|
||||
|
||||
@PutMapping("/merchant-applications/{id}")
|
||||
public ApiResponse<MerchantApplication> auditMerchant(HttpServletRequest request, @PathVariable Long id, @RequestBody Map<String, String> body) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminAuditApplication(id, body.get("status"), body.getOrDefault("remark", "")));
|
||||
}
|
||||
|
||||
@GetMapping("/users")
|
||||
public ApiResponse<List<User>> users(HttpServletRequest request) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminUsers());
|
||||
}
|
||||
|
||||
@PostMapping("/users")
|
||||
public ApiResponse<User> saveUser(HttpServletRequest request, @RequestBody User user) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminSaveUser(user));
|
||||
}
|
||||
|
||||
@DeleteMapping("/users/{id}")
|
||||
public ApiResponse<Void> deleteUser(HttpServletRequest request, @PathVariable Long id) {
|
||||
check(AuthContext.getUser(request));
|
||||
mallService.adminDeleteUser(id);
|
||||
return ApiResponse.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/banners")
|
||||
public ApiResponse<List<Banner>> banners(HttpServletRequest request) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminBanners());
|
||||
}
|
||||
|
||||
@PostMapping("/banners")
|
||||
public ApiResponse<Banner> saveBanner(HttpServletRequest request, @RequestBody Banner banner) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminSaveBanner(banner));
|
||||
}
|
||||
|
||||
@DeleteMapping("/banners/{id}")
|
||||
public ApiResponse<Void> deleteBanner(HttpServletRequest request, @PathVariable Long id) {
|
||||
check(AuthContext.getUser(request));
|
||||
mallService.adminDeleteBanner(id);
|
||||
return ApiResponse.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/products")
|
||||
public ApiResponse<List<Product>> products(HttpServletRequest request) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminProducts());
|
||||
}
|
||||
|
||||
@GetMapping("/products/views")
|
||||
public ApiResponse<List<Map<String, Object>>> productViews(HttpServletRequest request) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminProductViews());
|
||||
}
|
||||
|
||||
@PostMapping("/products")
|
||||
public ApiResponse<Product> saveProduct(HttpServletRequest request, @RequestBody Product product) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminSaveProduct(product));
|
||||
}
|
||||
|
||||
@PutMapping("/products/{id}/approve")
|
||||
public ApiResponse<Product> approveProduct(HttpServletRequest request, @PathVariable Long id, @RequestBody Map<String, Object> body) {
|
||||
check(AuthContext.getUser(request));
|
||||
boolean approved = Boolean.parseBoolean(String.valueOf(body.getOrDefault("approved", true)));
|
||||
return ApiResponse.ok(mallService.adminApproveProduct(id, approved));
|
||||
}
|
||||
|
||||
@DeleteMapping("/products/{id}")
|
||||
public ApiResponse<Void> deleteProduct(HttpServletRequest request, @PathVariable Long id) {
|
||||
check(AuthContext.getUser(request));
|
||||
mallService.adminDeleteProduct(id);
|
||||
return ApiResponse.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/reviews")
|
||||
public ApiResponse<List<Map<String, Object>>> reviews(HttpServletRequest request) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminReviews());
|
||||
}
|
||||
|
||||
@GetMapping("/logistics")
|
||||
public ApiResponse<List<Map<String, Object>>> logistics(HttpServletRequest request) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminLogistics());
|
||||
}
|
||||
|
||||
@GetMapping("/inventory")
|
||||
public ApiResponse<List<Map<String, Object>>> inventory(HttpServletRequest request) {
|
||||
check(AuthContext.getUser(request));
|
||||
return ApiResponse.ok(mallService.adminInventory());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.maternalmall.controller;
|
||||
|
||||
import com.maternalmall.common.ApiResponse;
|
||||
import com.maternalmall.config.AuthContext;
|
||||
import com.maternalmall.domain.User;
|
||||
import com.maternalmall.repository.UserRepository;
|
||||
import com.maternalmall.service.AuthService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
public class AuthController {
|
||||
private final AuthService authService;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public AuthController(AuthService authService, UserRepository userRepository) {
|
||||
this.authService = authService;
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public ApiResponse<Map<String, Object>> register(@RequestBody Map<String, String> body) {
|
||||
return ApiResponse.ok(authService.register(body.get("username"), body.get("password"), body.getOrDefault("role", "CUSTOMER")));
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ApiResponse<Map<String, Object>> login(@RequestBody Map<String, String> body) {
|
||||
return ApiResponse.ok(authService.login(body.get("username"), body.get("password")));
|
||||
}
|
||||
|
||||
@GetMapping("/me")
|
||||
public ApiResponse<User> me(HttpServletRequest request) {
|
||||
return ApiResponse.ok(AuthContext.getUser(request));
|
||||
}
|
||||
|
||||
@PutMapping("/me")
|
||||
public ApiResponse<User> updateMe(HttpServletRequest request, @RequestBody Map<String, String> body) {
|
||||
User user = AuthContext.getUser(request);
|
||||
user.setNickname(body.getOrDefault("nickname", user.getNickname()));
|
||||
user.setPhone(body.getOrDefault("phone", user.getPhone()));
|
||||
user.setAddress(body.getOrDefault("address", user.getAddress()));
|
||||
return ApiResponse.ok(userRepository.save(user));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.maternalmall.controller;
|
||||
|
||||
import com.maternalmall.common.ApiResponse;
|
||||
import com.maternalmall.config.AuthContext;
|
||||
import com.maternalmall.domain.*;
|
||||
import com.maternalmall.service.MallService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/customer")
|
||||
public class CustomerController {
|
||||
private final MallService mallService;
|
||||
|
||||
public CustomerController(MallService mallService) {
|
||||
this.mallService = mallService;
|
||||
}
|
||||
|
||||
@GetMapping("/cart")
|
||||
public ApiResponse<List<CartItem>> cart(HttpServletRequest request) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.cartList(user.getId()));
|
||||
}
|
||||
|
||||
@GetMapping("/cart/views")
|
||||
public ApiResponse<List<Map<String, Object>>> cartViews(HttpServletRequest request) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.cartViews(user.getId()));
|
||||
}
|
||||
|
||||
@PostMapping("/cart")
|
||||
public ApiResponse<CartItem> addCart(HttpServletRequest request, @RequestBody Map<String, Object> body) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.addCart(user.getId(), Long.valueOf(body.get("productId").toString()), Integer.valueOf(body.get("quantity").toString())));
|
||||
}
|
||||
|
||||
@DeleteMapping("/cart/{productId}")
|
||||
public ApiResponse<Void> removeCart(HttpServletRequest request, @PathVariable Long productId) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
mallService.removeCart(user.getId(), productId);
|
||||
return ApiResponse.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/favorites")
|
||||
public ApiResponse<List<Favorite>> favorites(HttpServletRequest request) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.favoriteList(user.getId()));
|
||||
}
|
||||
|
||||
@GetMapping("/favorites/views")
|
||||
public ApiResponse<List<Map<String, Object>>> favoriteViews(HttpServletRequest request) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.favoriteViews(user.getId()));
|
||||
}
|
||||
|
||||
@PostMapping("/favorites")
|
||||
public ApiResponse<Favorite> addFavorite(HttpServletRequest request, @RequestBody Map<String, Object> body) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.addFavorite(user.getId(), Long.valueOf(body.get("productId").toString())));
|
||||
}
|
||||
|
||||
@DeleteMapping("/favorites/{productId}")
|
||||
public ApiResponse<Void> removeFavorite(HttpServletRequest request, @PathVariable Long productId) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
mallService.removeFavorite(user.getId(), productId);
|
||||
return ApiResponse.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/orders/checkout")
|
||||
public ApiResponse<Orders> checkout(HttpServletRequest request, @RequestBody Map<String, String> body) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.checkout(user.getId(), body.get("address")));
|
||||
}
|
||||
|
||||
@PostMapping("/orders/buy-now")
|
||||
public ApiResponse<Orders> buyNow(HttpServletRequest request, @RequestBody Map<String, Object> body) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
Long productId = Long.valueOf(body.get("productId").toString());
|
||||
Integer quantity = Integer.valueOf(String.valueOf(body.getOrDefault("quantity", 1)));
|
||||
String address = String.valueOf(body.getOrDefault("address", ""));
|
||||
return ApiResponse.ok(mallService.buyNow(user.getId(), productId, quantity, address));
|
||||
}
|
||||
|
||||
@GetMapping("/orders")
|
||||
public ApiResponse<List<Orders>> orders(HttpServletRequest request) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.customerOrders(user.getId()));
|
||||
}
|
||||
|
||||
@GetMapping("/orders/{orderId}/items")
|
||||
public ApiResponse<List<OrderItem>> orderItems(HttpServletRequest request, @PathVariable Long orderId) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.customerOrderItems(user.getId(), orderId));
|
||||
}
|
||||
|
||||
@PutMapping("/orders/{orderId}/address")
|
||||
public ApiResponse<Orders> updateAddress(HttpServletRequest request, @PathVariable Long orderId, @RequestBody Map<String, String> body) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.customerUpdateAddress(user.getId(), orderId, body.get("address")));
|
||||
}
|
||||
|
||||
@PutMapping("/orders/{orderId}/refund")
|
||||
public ApiResponse<Orders> refund(HttpServletRequest request, @PathVariable Long orderId, @RequestBody Map<String, String> body) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.customerRefund(user.getId(), orderId, body.get("reason")));
|
||||
}
|
||||
|
||||
@DeleteMapping("/orders/{orderId}")
|
||||
public ApiResponse<Void> deleteOrder(HttpServletRequest request, @PathVariable Long orderId) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
mallService.customerDeleteOrder(user.getId(), orderId);
|
||||
return ApiResponse.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/orders/{orderId}/logistics")
|
||||
public ApiResponse<List<LogisticsRecord>> logistics(HttpServletRequest request, @PathVariable Long orderId) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.logisticsOfOrder(user.getId(), orderId));
|
||||
}
|
||||
|
||||
@PostMapping("/reviews")
|
||||
public ApiResponse<Review> review(HttpServletRequest request, @RequestBody Map<String, Object> body) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.CUSTOMER);
|
||||
return ApiResponse.ok(mallService.addReview(
|
||||
user.getId(),
|
||||
Long.valueOf(body.get("orderId").toString()),
|
||||
Long.valueOf(body.get("productId").toString()),
|
||||
Integer.valueOf(body.get("rating").toString()),
|
||||
body.get("content").toString()));
|
||||
}
|
||||
|
||||
@PostMapping("/merchant-applications")
|
||||
public ApiResponse<MerchantApplication> applyMerchant(HttpServletRequest request, @RequestBody Map<String, String> body) {
|
||||
User user = AuthContext.getUser(request);
|
||||
return ApiResponse.ok(mallService.applyMerchant(user.getId(), body.getOrDefault("qualification", "")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.maternalmall.controller;
|
||||
|
||||
import com.maternalmall.common.ApiResponse;
|
||||
import com.maternalmall.config.AuthContext;
|
||||
import com.maternalmall.domain.*;
|
||||
import com.maternalmall.service.MallService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/merchant")
|
||||
public class MerchantController {
|
||||
private final MallService mallService;
|
||||
|
||||
public MerchantController(MallService mallService) {
|
||||
this.mallService = mallService;
|
||||
}
|
||||
|
||||
@GetMapping("/overview")
|
||||
public ApiResponse<Map<String, Object>> overview(HttpServletRequest request) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.MERCHANT);
|
||||
return ApiResponse.ok(mallService.merchantOverview(user.getId()));
|
||||
}
|
||||
|
||||
@GetMapping("/products")
|
||||
public ApiResponse<List<Product>> products(HttpServletRequest request) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.MERCHANT);
|
||||
return ApiResponse.ok(mallService.merchantProducts(user.getId()));
|
||||
}
|
||||
|
||||
@PostMapping("/products")
|
||||
public ApiResponse<Product> saveProduct(HttpServletRequest request, @RequestBody Product product) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.MERCHANT);
|
||||
return ApiResponse.ok(mallService.merchantSaveProduct(user.getId(), product));
|
||||
}
|
||||
|
||||
@DeleteMapping("/products/{id}")
|
||||
public ApiResponse<Void> deleteProduct(HttpServletRequest request, @PathVariable Long id) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.MERCHANT);
|
||||
mallService.merchantDeleteProduct(user.getId(), id);
|
||||
return ApiResponse.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/orders")
|
||||
public ApiResponse<List<Orders>> orders(HttpServletRequest request) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.MERCHANT);
|
||||
return ApiResponse.ok(mallService.merchantOrders(user.getId()));
|
||||
}
|
||||
|
||||
@PutMapping("/orders/{id}/ship")
|
||||
public ApiResponse<Orders> ship(HttpServletRequest request, @PathVariable Long id, @RequestBody Map<String, String> body) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.MERCHANT);
|
||||
return ApiResponse.ok(mallService.merchantShip(user.getId(), id, body.getOrDefault("note", "已发货")));
|
||||
}
|
||||
|
||||
@PutMapping("/orders/{id}/refund")
|
||||
public ApiResponse<Orders> refund(HttpServletRequest request, @PathVariable Long id, @RequestBody Map<String, Object> body) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.MERCHANT);
|
||||
return ApiResponse.ok(mallService.merchantProcessRefund(user.getId(), id, Boolean.parseBoolean(body.get("agree").toString())));
|
||||
}
|
||||
|
||||
@GetMapping("/reviews")
|
||||
public ApiResponse<List<Map<String, Object>>> reviews(HttpServletRequest request) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.MERCHANT);
|
||||
return ApiResponse.ok(mallService.merchantReviews(user.getId()));
|
||||
}
|
||||
|
||||
@GetMapping("/logistics")
|
||||
public ApiResponse<List<Map<String, Object>>> logistics(HttpServletRequest request) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.MERCHANT);
|
||||
return ApiResponse.ok(mallService.merchantLogistics(user.getId()));
|
||||
}
|
||||
|
||||
@GetMapping("/inventory")
|
||||
public ApiResponse<List<Map<String, Object>>> inventory(HttpServletRequest request) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.MERCHANT);
|
||||
return ApiResponse.ok(mallService.merchantInventory(user.getId()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/inventory/{id}")
|
||||
public ApiResponse<Void> deleteInventory(HttpServletRequest request, @PathVariable Long id) {
|
||||
User user = AuthContext.getUser(request);
|
||||
mallService.requireRole(user, UserRole.MERCHANT);
|
||||
mallService.merchantDeleteInventory(user.getId(), id);
|
||||
return ApiResponse.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.maternalmall.controller;
|
||||
|
||||
import com.maternalmall.common.ApiResponse;
|
||||
import com.maternalmall.domain.Banner;
|
||||
import com.maternalmall.domain.Product;
|
||||
import com.maternalmall.domain.Review;
|
||||
import com.maternalmall.repository.ReviewRepository;
|
||||
import com.maternalmall.service.MallService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/public")
|
||||
public class PublicController {
|
||||
private final MallService mallService;
|
||||
private final ReviewRepository reviewRepository;
|
||||
|
||||
public PublicController(MallService mallService, ReviewRepository reviewRepository) {
|
||||
this.mallService = mallService;
|
||||
this.reviewRepository = reviewRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/products")
|
||||
public ApiResponse<List<Product>> products(@RequestParam(required = false) String keyword) {
|
||||
return ApiResponse.ok(mallService.searchProducts(keyword));
|
||||
}
|
||||
|
||||
@GetMapping("/products/{id}")
|
||||
public ApiResponse<Product> product(@PathVariable Long id) {
|
||||
return ApiResponse.ok(mallService.getProduct(id));
|
||||
}
|
||||
|
||||
@GetMapping("/products/{id}/reviews")
|
||||
public ApiResponse<List<Review>> reviews(@PathVariable Long id) {
|
||||
return ApiResponse.ok(reviewRepository.findByProductId(id));
|
||||
}
|
||||
|
||||
@GetMapping("/banners")
|
||||
public ApiResponse<List<Banner>> banners() {
|
||||
return ApiResponse.ok(mallService.banners());
|
||||
}
|
||||
}
|
||||
25
backend/src/main/java/com/maternalmall/domain/Banner.java
Normal file
25
backend/src/main/java/com/maternalmall/domain/Banner.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "banner")
|
||||
public class Banner {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, length = 255)
|
||||
private String imageUrl;
|
||||
|
||||
@Column(length = 255)
|
||||
private String linkUrl;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Integer sortNo = 0;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Boolean enabled = true;
|
||||
}
|
||||
22
backend/src/main/java/com/maternalmall/domain/CartItem.java
Normal file
22
backend/src/main/java/com/maternalmall/domain/CartItem.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "cart_item", uniqueConstraints = @UniqueConstraint(columnNames = {"customerId", "productId"}))
|
||||
public class CartItem {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long customerId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long productId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Integer quantity;
|
||||
}
|
||||
19
backend/src/main/java/com/maternalmall/domain/Favorite.java
Normal file
19
backend/src/main/java/com/maternalmall/domain/Favorite.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "favorite", uniqueConstraints = @UniqueConstraint(columnNames = {"customerId", "productId"}))
|
||||
public class Favorite {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long customerId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long productId;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "inventory_record")
|
||||
public class InventoryRecord {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long productId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long merchantId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Integer changeQty;
|
||||
|
||||
@Column(length = 255)
|
||||
private String note;
|
||||
|
||||
@CreationTimestamp
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "logistics_record")
|
||||
public class LogisticsRecord {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long orderId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long merchantId;
|
||||
|
||||
@Column(length = 255, nullable = false)
|
||||
private String status;
|
||||
|
||||
@Column(length = 255)
|
||||
private String note;
|
||||
|
||||
@CreationTimestamp
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "merchant_application")
|
||||
public class MerchantApplication {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long userId;
|
||||
|
||||
@Column(length = 255)
|
||||
private String qualification;
|
||||
|
||||
@Column(length = 30, nullable = false)
|
||||
private String status = "PENDING";
|
||||
|
||||
@Column(length = 255)
|
||||
private String remark;
|
||||
|
||||
@CreationTimestamp
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@UpdateTimestamp
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
30
backend/src/main/java/com/maternalmall/domain/OrderItem.java
Normal file
30
backend/src/main/java/com/maternalmall/domain/OrderItem.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "order_item")
|
||||
public class OrderItem {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long orderId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long productId;
|
||||
|
||||
@Column(nullable = false, length = 120)
|
||||
private String productName;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Integer quantity;
|
||||
|
||||
@Column(nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal unitPrice;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
public enum OrderStatus {
|
||||
PENDING_PAYMENT,
|
||||
PAID,
|
||||
SHIPPED,
|
||||
COMPLETED,
|
||||
REFUND_REQUESTED,
|
||||
REFUNDED,
|
||||
CANCELLED
|
||||
}
|
||||
49
backend/src/main/java/com/maternalmall/domain/Orders.java
Normal file
49
backend/src/main/java/com/maternalmall/domain/Orders.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "orders")
|
||||
public class Orders {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, unique = true, length = 50)
|
||||
private String orderNo;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long customerId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long merchantId;
|
||||
|
||||
@Column(nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false, length = 30)
|
||||
private OrderStatus status;
|
||||
|
||||
@Column(length = 255)
|
||||
private String address;
|
||||
|
||||
@Column(length = 255)
|
||||
private String logisticsInfo;
|
||||
|
||||
@Column(length = 255)
|
||||
private String refundReason;
|
||||
|
||||
@CreationTimestamp
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@UpdateTimestamp
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
48
backend/src/main/java/com/maternalmall/domain/Product.java
Normal file
48
backend/src/main/java/com/maternalmall/domain/Product.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "product")
|
||||
public class Product {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, length = 120)
|
||||
private String name;
|
||||
|
||||
@Column(length = 50)
|
||||
private String category;
|
||||
|
||||
@Column(length = 1000)
|
||||
private String description;
|
||||
|
||||
@Column(nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal price;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Integer stock;
|
||||
|
||||
@Column(length = 255)
|
||||
private String imageUrl;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long merchantId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Boolean approved = false;
|
||||
|
||||
@CreationTimestamp
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@UpdateTimestamp
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
34
backend/src/main/java/com/maternalmall/domain/Review.java
Normal file
34
backend/src/main/java/com/maternalmall/domain/Review.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "review")
|
||||
public class Review {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long orderId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long productId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long customerId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Integer rating;
|
||||
|
||||
@Column(length = 1000)
|
||||
private String content;
|
||||
|
||||
@CreationTimestamp
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
48
backend/src/main/java/com/maternalmall/domain/User.java
Normal file
48
backend/src/main/java/com/maternalmall/domain/User.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class User {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(unique = true, nullable = false, length = 50)
|
||||
private String username;
|
||||
|
||||
@Column(nullable = false, length = 100)
|
||||
private String password;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false, length = 20)
|
||||
private UserRole role;
|
||||
|
||||
@Column(length = 50)
|
||||
private String nickname;
|
||||
|
||||
@Column(length = 20)
|
||||
private String phone;
|
||||
|
||||
@Column(length = 255)
|
||||
private String address;
|
||||
|
||||
@Column(length = 100)
|
||||
private String token;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Boolean enabled = true;
|
||||
|
||||
@CreationTimestamp
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@UpdateTimestamp
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.maternalmall.domain;
|
||||
|
||||
public enum UserRole {
|
||||
CUSTOMER,
|
||||
MERCHANT,
|
||||
ADMIN
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.maternalmall.repository;
|
||||
|
||||
import com.maternalmall.domain.Banner;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface BannerRepository extends JpaRepository<Banner, Long> {
|
||||
List<Banner> findByEnabledTrueOrderBySortNoAsc();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.maternalmall.repository;
|
||||
|
||||
import com.maternalmall.domain.CartItem;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface CartItemRepository extends JpaRepository<CartItem, Long> {
|
||||
List<CartItem> findByCustomerId(Long customerId);
|
||||
Optional<CartItem> findByCustomerIdAndProductId(Long customerId, Long productId);
|
||||
|
||||
@Modifying
|
||||
@Transactional
|
||||
void deleteByCustomerIdAndProductId(Long customerId, Long productId);
|
||||
|
||||
@Modifying
|
||||
@Transactional
|
||||
void deleteByCustomerId(Long customerId);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.maternalmall.repository;
|
||||
|
||||
import com.maternalmall.domain.Favorite;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface FavoriteRepository extends JpaRepository<Favorite, Long> {
|
||||
List<Favorite> findByCustomerId(Long customerId);
|
||||
Optional<Favorite> findByCustomerIdAndProductId(Long customerId, Long productId);
|
||||
|
||||
@Modifying
|
||||
@Transactional
|
||||
void deleteByCustomerIdAndProductId(Long customerId, Long productId);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.maternalmall.repository;
|
||||
|
||||
import com.maternalmall.domain.InventoryRecord;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface InventoryRecordRepository extends JpaRepository<InventoryRecord, Long> {
|
||||
List<InventoryRecord> findByMerchantId(Long merchantId);
|
||||
List<InventoryRecord> findByProductId(Long productId);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.maternalmall.repository;
|
||||
|
||||
import com.maternalmall.domain.LogisticsRecord;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface LogisticsRecordRepository extends JpaRepository<LogisticsRecord, Long> {
|
||||
List<LogisticsRecord> findByOrderId(Long orderId);
|
||||
List<LogisticsRecord> findByMerchantId(Long merchantId);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.maternalmall.repository;
|
||||
|
||||
import com.maternalmall.domain.MerchantApplication;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MerchantApplicationRepository extends JpaRepository<MerchantApplication, Long> {
|
||||
List<MerchantApplication> findByStatus(String status);
|
||||
boolean existsByUserIdAndStatus(Long userId, String status);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.maternalmall.repository;
|
||||
|
||||
import com.maternalmall.domain.OrderItem;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface OrderItemRepository extends JpaRepository<OrderItem, Long> {
|
||||
List<OrderItem> findByOrderId(Long orderId);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.maternalmall.repository;
|
||||
|
||||
import com.maternalmall.domain.OrderStatus;
|
||||
import com.maternalmall.domain.Orders;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface OrdersRepository extends JpaRepository<Orders, Long> {
|
||||
List<Orders> findByCustomerId(Long customerId);
|
||||
List<Orders> findByMerchantId(Long merchantId);
|
||||
List<Orders> findByStatus(OrderStatus status);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.maternalmall.repository;
|
||||
|
||||
import com.maternalmall.domain.Product;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ProductRepository extends JpaRepository<Product, Long> {
|
||||
List<Product> findByApprovedTrueAndNameContainingIgnoreCase(String name);
|
||||
List<Product> findByMerchantId(Long merchantId);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.maternalmall.repository;
|
||||
|
||||
import com.maternalmall.domain.Review;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ReviewRepository extends JpaRepository<Review, Long> {
|
||||
List<Review> findByProductId(Long productId);
|
||||
List<Review> findByCustomerId(Long customerId);
|
||||
boolean existsByOrderIdAndProductIdAndCustomerId(Long orderId, Long productId, Long customerId);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.maternalmall.repository;
|
||||
|
||||
import com.maternalmall.domain.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findByUsername(String username);
|
||||
Optional<User> findByToken(String token);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.maternalmall.service;
|
||||
|
||||
import com.maternalmall.common.BizException;
|
||||
import com.maternalmall.domain.User;
|
||||
import com.maternalmall.domain.UserRole;
|
||||
import com.maternalmall.repository.UserRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class AuthService {
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public AuthService(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
public Map<String, Object> register(String username, String password, String role) {
|
||||
username = username == null ? "" : username.trim();
|
||||
password = password == null ? "" : password.trim();
|
||||
if (username.isEmpty()) {
|
||||
throw new BizException("用户名不能为空");
|
||||
}
|
||||
if (password.isEmpty()) {
|
||||
throw new BizException("密码不能为空");
|
||||
}
|
||||
userRepository.findByUsername(username).ifPresent(u -> {
|
||||
throw new BizException("账号已存在");
|
||||
});
|
||||
if (role != null && !role.isBlank() && !"CUSTOMER".equalsIgnoreCase(role)) {
|
||||
throw new BizException("仅支持注册顾客账号,商家需提交入驻申请");
|
||||
}
|
||||
User user = new User();
|
||||
user.setUsername(username);
|
||||
user.setPassword(password);
|
||||
user.setRole(UserRole.CUSTOMER);
|
||||
user.setNickname(username);
|
||||
userRepository.save(user);
|
||||
return Map.of("userId", user.getId());
|
||||
}
|
||||
|
||||
public Map<String, Object> login(String username, String password) {
|
||||
User user = userRepository.findByUsername(username).orElseThrow(() -> new BizException("用户不存在"));
|
||||
if (!user.getPassword().equals(password)) {
|
||||
throw new BizException("密码错误");
|
||||
}
|
||||
if (!Boolean.TRUE.equals(user.getEnabled())) {
|
||||
throw new BizException("账号已被禁用,请联系管理员");
|
||||
}
|
||||
String token = UUID.randomUUID().toString().replace("-", "");
|
||||
user.setToken(token);
|
||||
userRepository.save(user);
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("token", token);
|
||||
data.put("id", user.getId());
|
||||
data.put("username", user.getUsername());
|
||||
data.put("role", user.getRole());
|
||||
data.put("nickname", user.getNickname());
|
||||
return data;
|
||||
}
|
||||
}
|
||||
887
backend/src/main/java/com/maternalmall/service/MallService.java
Normal file
887
backend/src/main/java/com/maternalmall/service/MallService.java
Normal file
@@ -0,0 +1,887 @@
|
||||
package com.maternalmall.service;
|
||||
|
||||
import com.maternalmall.common.BizException;
|
||||
import com.maternalmall.domain.*;
|
||||
import com.maternalmall.repository.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class MallService {
|
||||
private final ProductRepository productRepository;
|
||||
private final CartItemRepository cartItemRepository;
|
||||
private final FavoriteRepository favoriteRepository;
|
||||
private final OrdersRepository ordersRepository;
|
||||
private final OrderItemRepository orderItemRepository;
|
||||
private final ReviewRepository reviewRepository;
|
||||
private final LogisticsRecordRepository logisticsRecordRepository;
|
||||
private final InventoryRecordRepository inventoryRecordRepository;
|
||||
private final MerchantApplicationRepository merchantApplicationRepository;
|
||||
private final BannerRepository bannerRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public MallService(ProductRepository productRepository,
|
||||
CartItemRepository cartItemRepository,
|
||||
FavoriteRepository favoriteRepository,
|
||||
OrdersRepository ordersRepository,
|
||||
OrderItemRepository orderItemRepository,
|
||||
ReviewRepository reviewRepository,
|
||||
LogisticsRecordRepository logisticsRecordRepository,
|
||||
InventoryRecordRepository inventoryRecordRepository,
|
||||
MerchantApplicationRepository merchantApplicationRepository,
|
||||
BannerRepository bannerRepository,
|
||||
UserRepository userRepository) {
|
||||
this.productRepository = productRepository;
|
||||
this.cartItemRepository = cartItemRepository;
|
||||
this.favoriteRepository = favoriteRepository;
|
||||
this.ordersRepository = ordersRepository;
|
||||
this.orderItemRepository = orderItemRepository;
|
||||
this.reviewRepository = reviewRepository;
|
||||
this.logisticsRecordRepository = logisticsRecordRepository;
|
||||
this.inventoryRecordRepository = inventoryRecordRepository;
|
||||
this.merchantApplicationRepository = merchantApplicationRepository;
|
||||
this.bannerRepository = bannerRepository;
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
public void requireRole(User user, UserRole role) {
|
||||
if (user.getRole() != role) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
}
|
||||
|
||||
public List<Product> searchProducts(String keyword) {
|
||||
return productRepository.findByApprovedTrueAndNameContainingIgnoreCase(keyword == null ? "" : keyword);
|
||||
}
|
||||
|
||||
public Product getProduct(Long id) {
|
||||
return productRepository.findById(id).orElseThrow(() -> new BizException("商品不存在"));
|
||||
}
|
||||
|
||||
public List<Banner> banners() {
|
||||
return bannerRepository.findByEnabledTrueOrderBySortNoAsc();
|
||||
}
|
||||
|
||||
public List<CartItem> cartList(Long customerId) {
|
||||
return cartItemRepository.findByCustomerId(customerId);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> cartViews(Long customerId) {
|
||||
List<CartItem> items = cartItemRepository.findByCustomerId(customerId);
|
||||
List<Map<String, Object>> views = new ArrayList<>();
|
||||
for (CartItem item : items) {
|
||||
Product p = getProduct(item.getProductId());
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("productId", p.getId());
|
||||
row.put("productName", p.getName());
|
||||
row.put("category", p.getCategory());
|
||||
row.put("imageUrl", p.getImageUrl());
|
||||
row.put("unitPrice", p.getPrice());
|
||||
row.put("quantity", item.getQuantity());
|
||||
row.put("subtotal", p.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())));
|
||||
row.put("stock", p.getStock());
|
||||
views.add(row);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
public CartItem addCart(Long customerId, Long productId, Integer quantity) {
|
||||
Product product = getProduct(productId);
|
||||
if (!product.getApproved()) {
|
||||
throw new BizException("商品未上架");
|
||||
}
|
||||
CartItem item = cartItemRepository.findByCustomerIdAndProductId(customerId, productId).orElseGet(CartItem::new);
|
||||
item.setCustomerId(customerId);
|
||||
item.setProductId(productId);
|
||||
item.setQuantity((item.getQuantity() == null ? 0 : item.getQuantity()) + quantity);
|
||||
return cartItemRepository.save(item);
|
||||
}
|
||||
|
||||
public void removeCart(Long customerId, Long productId) {
|
||||
cartItemRepository.deleteByCustomerIdAndProductId(customerId, productId);
|
||||
}
|
||||
|
||||
public List<Favorite> favoriteList(Long customerId) {
|
||||
return favoriteRepository.findByCustomerId(customerId);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> favoriteViews(Long customerId) {
|
||||
List<Favorite> favorites = favoriteRepository.findByCustomerId(customerId);
|
||||
List<Map<String, Object>> views = new ArrayList<>();
|
||||
for (Favorite favorite : favorites) {
|
||||
Product p = getProduct(favorite.getProductId());
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("productId", p.getId());
|
||||
row.put("productName", p.getName());
|
||||
row.put("category", p.getCategory());
|
||||
row.put("imageUrl", p.getImageUrl());
|
||||
row.put("unitPrice", p.getPrice());
|
||||
row.put("stock", p.getStock());
|
||||
views.add(row);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
public Favorite addFavorite(Long customerId, Long productId) {
|
||||
favoriteRepository.findByCustomerIdAndProductId(customerId, productId).ifPresent(f -> {
|
||||
throw new BizException("已收藏");
|
||||
});
|
||||
Favorite favorite = new Favorite();
|
||||
favorite.setCustomerId(customerId);
|
||||
favorite.setProductId(productId);
|
||||
return favoriteRepository.save(favorite);
|
||||
}
|
||||
|
||||
public void removeFavorite(Long customerId, Long productId) {
|
||||
favoriteRepository.deleteByCustomerIdAndProductId(customerId, productId);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Orders checkout(Long customerId, String address) {
|
||||
List<CartItem> cartItems = cartItemRepository.findByCustomerId(customerId);
|
||||
if (cartItems.isEmpty()) {
|
||||
throw new BizException("购物车为空");
|
||||
}
|
||||
Long merchantId = null;
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
Orders order = new Orders();
|
||||
order.setOrderNo("MM" + System.currentTimeMillis());
|
||||
order.setCustomerId(customerId);
|
||||
order.setStatus(OrderStatus.PAID);
|
||||
order.setAddress(address);
|
||||
|
||||
for (CartItem cartItem : cartItems) {
|
||||
Product product = getProduct(cartItem.getProductId());
|
||||
if (product.getStock() < cartItem.getQuantity()) {
|
||||
throw new BizException("库存不足: " + product.getName());
|
||||
}
|
||||
if (merchantId == null) {
|
||||
merchantId = product.getMerchantId();
|
||||
} else if (!merchantId.equals(product.getMerchantId())) {
|
||||
throw new BizException("当前基础版本仅支持单商家结算");
|
||||
}
|
||||
product.setStock(product.getStock() - cartItem.getQuantity());
|
||||
productRepository.save(product);
|
||||
|
||||
InventoryRecord record = new InventoryRecord();
|
||||
record.setMerchantId(product.getMerchantId());
|
||||
record.setProductId(product.getId());
|
||||
record.setChangeQty(-cartItem.getQuantity());
|
||||
record.setNote("订单扣减库存");
|
||||
inventoryRecordRepository.save(record);
|
||||
|
||||
BigDecimal line = product.getPrice().multiply(BigDecimal.valueOf(cartItem.getQuantity()));
|
||||
total = total.add(line);
|
||||
}
|
||||
order.setMerchantId(merchantId);
|
||||
order.setTotalAmount(total);
|
||||
ordersRepository.save(order);
|
||||
|
||||
for (CartItem cartItem : cartItems) {
|
||||
Product product = getProduct(cartItem.getProductId());
|
||||
OrderItem item = new OrderItem();
|
||||
item.setOrderId(order.getId());
|
||||
item.setProductId(product.getId());
|
||||
item.setProductName(product.getName());
|
||||
item.setQuantity(cartItem.getQuantity());
|
||||
item.setUnitPrice(product.getPrice());
|
||||
orderItemRepository.save(item);
|
||||
}
|
||||
cartItemRepository.deleteByCustomerId(customerId);
|
||||
return order;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Orders buyNow(Long customerId, Long productId, Integer quantity, String address) {
|
||||
if (quantity == null || quantity <= 0) {
|
||||
throw new BizException("购买数量必须大于0");
|
||||
}
|
||||
Product product = getProduct(productId);
|
||||
if (!Boolean.TRUE.equals(product.getApproved())) {
|
||||
throw new BizException("商品未上架");
|
||||
}
|
||||
if (product.getStock() < quantity) {
|
||||
throw new BizException("库存不足: " + product.getName());
|
||||
}
|
||||
|
||||
product.setStock(product.getStock() - quantity);
|
||||
productRepository.save(product);
|
||||
|
||||
InventoryRecord record = new InventoryRecord();
|
||||
record.setMerchantId(product.getMerchantId());
|
||||
record.setProductId(product.getId());
|
||||
record.setChangeQty(-quantity);
|
||||
record.setNote("立即购买扣减库存");
|
||||
inventoryRecordRepository.save(record);
|
||||
|
||||
Orders order = new Orders();
|
||||
order.setOrderNo("MM" + System.currentTimeMillis());
|
||||
order.setCustomerId(customerId);
|
||||
order.setMerchantId(product.getMerchantId());
|
||||
order.setStatus(OrderStatus.PAID);
|
||||
order.setAddress(address);
|
||||
order.setTotalAmount(product.getPrice().multiply(BigDecimal.valueOf(quantity)));
|
||||
ordersRepository.save(order);
|
||||
|
||||
OrderItem item = new OrderItem();
|
||||
item.setOrderId(order.getId());
|
||||
item.setProductId(product.getId());
|
||||
item.setProductName(product.getName());
|
||||
item.setQuantity(quantity);
|
||||
item.setUnitPrice(product.getPrice());
|
||||
orderItemRepository.save(item);
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
public List<Orders> customerOrders(Long customerId) {
|
||||
return ordersRepository.findByCustomerId(customerId);
|
||||
}
|
||||
|
||||
public List<OrderItem> customerOrderItems(Long customerId, Long orderId) {
|
||||
Orders order = ordersRepository.findById(orderId).orElseThrow(() -> new BizException("订单不存在"));
|
||||
if (!order.getCustomerId().equals(customerId)) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
return orderItemRepository.findByOrderId(orderId);
|
||||
}
|
||||
|
||||
public Orders customerUpdateAddress(Long customerId, Long orderId, String address) {
|
||||
Orders order = ordersRepository.findById(orderId).orElseThrow(() -> new BizException("订单不存在"));
|
||||
if (!order.getCustomerId().equals(customerId)) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
if (order.getStatus() == OrderStatus.SHIPPED || order.getStatus() == OrderStatus.COMPLETED) {
|
||||
throw new BizException("该状态不可修改地址");
|
||||
}
|
||||
order.setAddress(address);
|
||||
return ordersRepository.save(order);
|
||||
}
|
||||
|
||||
public Orders customerRefund(Long customerId, Long orderId, String reason) {
|
||||
Orders order = ordersRepository.findById(orderId).orElseThrow(() -> new BizException("订单不存在"));
|
||||
if (!order.getCustomerId().equals(customerId)) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
order.setStatus(OrderStatus.REFUND_REQUESTED);
|
||||
order.setRefundReason(reason);
|
||||
return ordersRepository.save(order);
|
||||
}
|
||||
|
||||
public void customerDeleteOrder(Long customerId, Long orderId) {
|
||||
Orders order = ordersRepository.findById(orderId).orElseThrow(() -> new BizException("订单不存在"));
|
||||
if (!order.getCustomerId().equals(customerId)) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
if (!(order.getStatus() == OrderStatus.COMPLETED || order.getStatus() == OrderStatus.REFUNDED || order.getStatus() == OrderStatus.CANCELLED)) {
|
||||
throw new BizException("当前状态不可删除");
|
||||
}
|
||||
ordersRepository.delete(order);
|
||||
}
|
||||
|
||||
public List<LogisticsRecord> logisticsOfOrder(Long customerId, Long orderId) {
|
||||
Orders order = ordersRepository.findById(orderId).orElseThrow(() -> new BizException("订单不存在"));
|
||||
if (!order.getCustomerId().equals(customerId)) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
return logisticsRecordRepository.findByOrderId(orderId);
|
||||
}
|
||||
|
||||
public Review addReview(Long customerId, Long orderId, Long productId, Integer rating, String content) {
|
||||
Orders order = ordersRepository.findById(orderId).orElseThrow(() -> new BizException("订单不存在"));
|
||||
if (!order.getCustomerId().equals(customerId)) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
if (rating == null || rating < 1 || rating > 5) {
|
||||
throw new BizException("评分范围为1-5分");
|
||||
}
|
||||
if (!(order.getStatus() == OrderStatus.PAID
|
||||
|| order.getStatus() == OrderStatus.SHIPPED
|
||||
|| order.getStatus() == OrderStatus.COMPLETED
|
||||
|| order.getStatus() == OrderStatus.REFUND_REQUESTED
|
||||
|| order.getStatus() == OrderStatus.REFUNDED)) {
|
||||
throw new BizException("仅已购买商品可评价");
|
||||
}
|
||||
boolean ordered = orderItemRepository.findByOrderId(orderId)
|
||||
.stream()
|
||||
.anyMatch(i -> i.getProductId().equals(productId));
|
||||
if (!ordered) {
|
||||
throw new BizException("该商品不在订单内");
|
||||
}
|
||||
if (reviewRepository.existsByOrderIdAndProductIdAndCustomerId(orderId, productId, customerId)) {
|
||||
throw new BizException("该商品已评价");
|
||||
}
|
||||
Review review = new Review();
|
||||
review.setOrderId(orderId);
|
||||
review.setProductId(productId);
|
||||
review.setCustomerId(customerId);
|
||||
review.setRating(rating);
|
||||
review.setContent(content);
|
||||
return reviewRepository.save(review);
|
||||
}
|
||||
|
||||
public Map<String, Object> merchantOverview(Long merchantId) {
|
||||
List<Orders> orders = ordersRepository.findByMerchantId(merchantId);
|
||||
BigDecimal sales = orders.stream()
|
||||
.filter(o -> o.getStatus() == OrderStatus.PAID || o.getStatus() == OrderStatus.SHIPPED || o.getStatus() == OrderStatus.COMPLETED)
|
||||
.map(Orders::getTotalAmount)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
Map<Long, Integer> hotMap = new HashMap<>();
|
||||
Map<String, Long> categoryCount = new HashMap<>();
|
||||
for (Orders order : orders) {
|
||||
for (OrderItem item : orderItemRepository.findByOrderId(order.getId())) {
|
||||
hotMap.put(item.getProductId(), hotMap.getOrDefault(item.getProductId(), 0) + item.getQuantity());
|
||||
}
|
||||
}
|
||||
for (Product p : productRepository.findByMerchantId(merchantId)) {
|
||||
String category = p.getCategory() == null ? "未分类" : p.getCategory();
|
||||
categoryCount.put(category, categoryCount.getOrDefault(category, 0L) + 1);
|
||||
}
|
||||
return Map.of(
|
||||
"orderCount", orders.size(),
|
||||
"salesAmount", sales,
|
||||
"hotProducts", hotMap,
|
||||
"categoryRatio", categoryCount,
|
||||
"notifications", List.of("请及时处理退款申请", "注意库存预警")
|
||||
);
|
||||
}
|
||||
|
||||
public Product merchantSaveProduct(Long merchantId, Product body) {
|
||||
Product product = body;
|
||||
if (product.getId() != null) {
|
||||
Product old = productRepository.findById(product.getId()).orElseThrow(() -> new BizException("商品不存在"));
|
||||
if (!old.getMerchantId().equals(merchantId)) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
}
|
||||
product.setMerchantId(merchantId);
|
||||
if (product.getApproved() == null) {
|
||||
product.setApproved(false);
|
||||
}
|
||||
Product saved = productRepository.save(product);
|
||||
InventoryRecord record = new InventoryRecord();
|
||||
record.setMerchantId(merchantId);
|
||||
record.setProductId(saved.getId());
|
||||
record.setChangeQty(saved.getStock());
|
||||
record.setNote("商品上架或更新库存");
|
||||
inventoryRecordRepository.save(record);
|
||||
return saved;
|
||||
}
|
||||
|
||||
public List<Product> merchantProducts(Long merchantId) {
|
||||
return productRepository.findByMerchantId(merchantId);
|
||||
}
|
||||
|
||||
public void merchantDeleteProduct(Long merchantId, Long productId) {
|
||||
Product product = getProduct(productId);
|
||||
if (!product.getMerchantId().equals(merchantId)) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
productRepository.delete(product);
|
||||
}
|
||||
|
||||
public List<Orders> merchantOrders(Long merchantId) {
|
||||
return ordersRepository.findByMerchantId(merchantId);
|
||||
}
|
||||
|
||||
public Orders merchantShip(Long merchantId, Long orderId, String note) {
|
||||
Orders order = ordersRepository.findById(orderId).orElseThrow(() -> new BizException("订单不存在"));
|
||||
if (!order.getMerchantId().equals(merchantId)) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
order.setStatus(OrderStatus.SHIPPED);
|
||||
order.setLogisticsInfo(note);
|
||||
ordersRepository.save(order);
|
||||
|
||||
LogisticsRecord record = new LogisticsRecord();
|
||||
record.setOrderId(orderId);
|
||||
record.setMerchantId(merchantId);
|
||||
record.setStatus("SHIPPED");
|
||||
record.setNote(note);
|
||||
logisticsRecordRepository.save(record);
|
||||
return order;
|
||||
}
|
||||
|
||||
public Orders merchantProcessRefund(Long merchantId, Long orderId, boolean agree) {
|
||||
Orders order = ordersRepository.findById(orderId).orElseThrow(() -> new BizException("订单不存在"));
|
||||
if (!order.getMerchantId().equals(merchantId)) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
if (order.getStatus() != OrderStatus.REFUND_REQUESTED) {
|
||||
throw new BizException("仅退款申请中的订单可处理");
|
||||
}
|
||||
order.setStatus(agree ? OrderStatus.REFUNDED : OrderStatus.PAID);
|
||||
return ordersRepository.save(order);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> merchantReviews(Long merchantId) {
|
||||
List<Product> products = productRepository.findByMerchantId(merchantId);
|
||||
Set<Long> productIds = new HashSet<>();
|
||||
for (Product product : products) {
|
||||
productIds.add(product.getId());
|
||||
}
|
||||
List<Review> reviews = reviewRepository.findAll();
|
||||
Map<Long, Orders> orderMap = new HashMap<>();
|
||||
for (Orders order : ordersRepository.findAll()) {
|
||||
orderMap.put(order.getId(), order);
|
||||
}
|
||||
List<Map<String, Object>> views = new ArrayList<>();
|
||||
for (Review r : reviews) {
|
||||
if (!productIds.contains(r.getProductId())) continue;
|
||||
Orders order = orderMap.get(r.getOrderId());
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("id", r.getId());
|
||||
row.put("orderId", r.getOrderId());
|
||||
row.put("orderNo", order == null ? "" : order.getOrderNo());
|
||||
row.put("productId", r.getProductId());
|
||||
row.put("productName", products.stream().filter(p -> p.getId().equals(r.getProductId())).findFirst().map(Product::getName).orElse(""));
|
||||
row.put("rating", r.getRating());
|
||||
row.put("content", r.getContent());
|
||||
row.put("createdAt", r.getCreatedAt());
|
||||
views.add(row);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> merchantLogistics(Long merchantId) {
|
||||
List<LogisticsRecord> records = logisticsRecordRepository.findByMerchantId(merchantId);
|
||||
Map<Long, Orders> orderMap = new HashMap<>();
|
||||
for (Orders order : ordersRepository.findAll()) {
|
||||
orderMap.put(order.getId(), order);
|
||||
}
|
||||
List<Map<String, Object>> views = new ArrayList<>();
|
||||
for (LogisticsRecord rec : records) {
|
||||
Orders order = orderMap.get(rec.getOrderId());
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("id", rec.getId());
|
||||
row.put("orderId", rec.getOrderId());
|
||||
row.put("orderNo", order == null ? "" : order.getOrderNo());
|
||||
row.put("status", rec.getStatus());
|
||||
row.put("note", rec.getNote());
|
||||
row.put("createdAt", rec.getCreatedAt());
|
||||
views.add(row);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> merchantInventory(Long merchantId) {
|
||||
List<InventoryRecord> records = inventoryRecordRepository.findByMerchantId(merchantId);
|
||||
Map<Long, Product> productMap = new HashMap<>();
|
||||
for (Product product : productRepository.findByMerchantId(merchantId)) {
|
||||
productMap.put(product.getId(), product);
|
||||
}
|
||||
List<Map<String, Object>> views = new ArrayList<>();
|
||||
for (InventoryRecord rec : records) {
|
||||
Product product = productMap.get(rec.getProductId());
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("id", rec.getId());
|
||||
row.put("productId", rec.getProductId());
|
||||
row.put("productName", product == null ? "" : product.getName());
|
||||
row.put("changeQty", rec.getChangeQty());
|
||||
row.put("note", rec.getNote());
|
||||
row.put("createdAt", rec.getCreatedAt());
|
||||
views.add(row);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
public void merchantDeleteInventory(Long merchantId, Long id) {
|
||||
InventoryRecord rec = inventoryRecordRepository.findById(id).orElseThrow(() -> new BizException("记录不存在"));
|
||||
if (!rec.getMerchantId().equals(merchantId)) {
|
||||
throw new BizException("无权限操作");
|
||||
}
|
||||
inventoryRecordRepository.delete(rec);
|
||||
}
|
||||
|
||||
public MerchantApplication applyMerchant(Long userId, String qualification) {
|
||||
User user = userRepository.findById(userId).orElseThrow(() -> new BizException("用户不存在"));
|
||||
if (user.getRole() != UserRole.CUSTOMER) {
|
||||
throw new BizException("仅顾客账号可提交入驻申请");
|
||||
}
|
||||
if (merchantApplicationRepository.existsByUserIdAndStatus(userId, "PENDING")) {
|
||||
throw new BizException("已有待审核申请,请勿重复提交");
|
||||
}
|
||||
MerchantApplication m = new MerchantApplication();
|
||||
m.setUserId(userId);
|
||||
m.setQualification(qualification);
|
||||
m.setStatus("PENDING");
|
||||
return merchantApplicationRepository.save(m);
|
||||
}
|
||||
|
||||
public Map<String, Object> adminOverview() {
|
||||
List<Orders> orders = ordersRepository.findAll();
|
||||
BigDecimal sales = orders.stream().map(Orders::getTotalAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
Map<String, Long> categoryCount = new HashMap<>();
|
||||
Map<Long, Integer> hotMap = new HashMap<>();
|
||||
for (Product p : productRepository.findAll()) {
|
||||
String category = p.getCategory() == null ? "未分类" : p.getCategory();
|
||||
categoryCount.put(category, categoryCount.getOrDefault(category, 0L) + 1);
|
||||
}
|
||||
for (Orders order : orders) {
|
||||
for (OrderItem item : orderItemRepository.findByOrderId(order.getId())) {
|
||||
hotMap.put(item.getProductId(), hotMap.getOrDefault(item.getProductId(), 0) + item.getQuantity());
|
||||
}
|
||||
}
|
||||
return Map.of(
|
||||
"orderCount", orders.size(),
|
||||
"salesAmount", sales,
|
||||
"categoryRatio", categoryCount,
|
||||
"hotProducts", hotMap
|
||||
);
|
||||
}
|
||||
|
||||
public List<Orders> adminOrders() {
|
||||
return ordersRepository.findAll();
|
||||
}
|
||||
|
||||
public Orders adminUpdateOrder(Long orderId, String status, String logisticsInfo) {
|
||||
Orders order = ordersRepository.findById(orderId).orElseThrow(() -> new BizException("订单不存在"));
|
||||
try {
|
||||
order.setStatus(OrderStatus.valueOf(status));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new BizException("订单状态不合法");
|
||||
}
|
||||
order.setLogisticsInfo(logisticsInfo);
|
||||
return ordersRepository.save(order);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> adminRiskOrders() {
|
||||
List<Orders> orders = ordersRepository.findAll();
|
||||
List<Map<String, Object>> risks = new ArrayList<>();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
for (Orders order : orders) {
|
||||
if (order.getTotalAmount().compareTo(new BigDecimal("5000")) >= 0) {
|
||||
risks.add(buildRisk(order, "HIGH_AMOUNT", "订单金额较高,请人工核查"));
|
||||
}
|
||||
if (order.getStatus() == OrderStatus.PAID && order.getCreatedAt() != null && order.getCreatedAt().isBefore(now.minusDays(2))) {
|
||||
risks.add(buildRisk(order, "SHIP_DELAY", "已支付超过48小时未发货"));
|
||||
}
|
||||
if (order.getStatus() == OrderStatus.SHIPPED && logisticsRecordRepository.findByOrderId(order.getId()).isEmpty()) {
|
||||
risks.add(buildRisk(order, "NO_LOGISTICS", "订单标记已发货但无物流轨迹"));
|
||||
}
|
||||
if (order.getStatus() == OrderStatus.REFUND_REQUESTED) {
|
||||
risks.add(buildRisk(order, "REFUND_PENDING", "退款申请待审核"));
|
||||
}
|
||||
}
|
||||
return risks;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildRisk(Orders order, String type, String reason) {
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("orderId", order.getId());
|
||||
row.put("orderNo", order.getOrderNo());
|
||||
row.put("status", order.getStatus());
|
||||
row.put("totalAmount", order.getTotalAmount());
|
||||
row.put("customerId", order.getCustomerId());
|
||||
row.put("merchantId", order.getMerchantId());
|
||||
row.put("riskType", type);
|
||||
row.put("riskReason", reason);
|
||||
return row;
|
||||
}
|
||||
|
||||
public Orders adminAuditRefund(Long orderId, boolean approve, String remark) {
|
||||
Orders order = ordersRepository.findById(orderId).orElseThrow(() -> new BizException("订单不存在"));
|
||||
if (order.getStatus() != OrderStatus.REFUND_REQUESTED) {
|
||||
throw new BizException("当前订单不在退款审核状态");
|
||||
}
|
||||
order.setStatus(approve ? OrderStatus.REFUNDED : OrderStatus.PAID);
|
||||
if (remark != null && !remark.trim().isEmpty()) {
|
||||
String old = order.getRefundReason() == null ? "" : order.getRefundReason();
|
||||
String merged = old.isBlank() ? remark.trim() : old + ";管理员备注:" + remark.trim();
|
||||
order.setRefundReason(merged);
|
||||
}
|
||||
Orders saved = ordersRepository.save(order);
|
||||
|
||||
LogisticsRecord record = new LogisticsRecord();
|
||||
record.setOrderId(orderId);
|
||||
record.setMerchantId(order.getMerchantId());
|
||||
record.setStatus(approve ? "REFUND_APPROVED" : "REFUND_REJECTED");
|
||||
record.setNote((approve ? "管理员通过退款审核" : "管理员驳回退款申请") + (remark == null || remark.trim().isEmpty() ? "" : (":" + remark.trim())));
|
||||
logisticsRecordRepository.save(record);
|
||||
return saved;
|
||||
}
|
||||
|
||||
public Orders adminAuditShipment(Long orderId, boolean approved, String remark) {
|
||||
Orders order = ordersRepository.findById(orderId).orElseThrow(() -> new BizException("订单不存在"));
|
||||
if (order.getStatus() != OrderStatus.SHIPPED && order.getStatus() != OrderStatus.PAID) {
|
||||
throw new BizException("仅已支付或已发货订单可审核发货");
|
||||
}
|
||||
if (approved && order.getStatus() == OrderStatus.PAID) {
|
||||
order.setStatus(OrderStatus.SHIPPED);
|
||||
}
|
||||
if (!approved && order.getStatus() == OrderStatus.SHIPPED) {
|
||||
order.setStatus(OrderStatus.PAID);
|
||||
}
|
||||
if (remark != null && !remark.trim().isEmpty()) {
|
||||
order.setLogisticsInfo(remark.trim());
|
||||
}
|
||||
Orders saved = ordersRepository.save(order);
|
||||
|
||||
LogisticsRecord record = new LogisticsRecord();
|
||||
record.setOrderId(orderId);
|
||||
record.setMerchantId(order.getMerchantId());
|
||||
record.setStatus(approved ? "SHIP_AUDIT_PASS" : "SHIP_AUDIT_REJECT");
|
||||
record.setNote((approved ? "管理员发货审核通过" : "管理员发货审核驳回") + (remark == null || remark.trim().isEmpty() ? "" : (":" + remark.trim())));
|
||||
logisticsRecordRepository.save(record);
|
||||
return saved;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> adminMerchantApplications() {
|
||||
List<MerchantApplication> apps = merchantApplicationRepository.findAll();
|
||||
List<Map<String, Object>> views = new ArrayList<>();
|
||||
Map<Long, User> userMap = new HashMap<>();
|
||||
for (User user : userRepository.findAll()) {
|
||||
userMap.put(user.getId(), user);
|
||||
}
|
||||
for (MerchantApplication app : apps) {
|
||||
User applicant = userMap.get(app.getUserId());
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("id", app.getId());
|
||||
row.put("userId", app.getUserId());
|
||||
row.put("applicantUsername", applicant == null ? "" : applicant.getUsername());
|
||||
row.put("applicantNickname", applicant == null ? "" : applicant.getNickname());
|
||||
row.put("qualification", app.getQualification());
|
||||
row.put("status", app.getStatus());
|
||||
row.put("remark", app.getRemark());
|
||||
views.add(row);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
public MerchantApplication adminAuditApplication(Long id, String status, String remark) {
|
||||
MerchantApplication app = merchantApplicationRepository.findById(id).orElseThrow(() -> new BizException("申请不存在"));
|
||||
app.setStatus(status);
|
||||
app.setRemark(remark);
|
||||
merchantApplicationRepository.save(app);
|
||||
|
||||
if ("APPROVED".equals(status)) {
|
||||
User user = userRepository.findById(app.getUserId()).orElseThrow(() -> new BizException("用户不存在"));
|
||||
user.setRole(UserRole.MERCHANT);
|
||||
userRepository.save(user);
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
public List<User> adminUsers() {
|
||||
return userRepository.findAll();
|
||||
}
|
||||
|
||||
public User adminSaveUser(User user) {
|
||||
String username = user.getUsername() == null ? "" : user.getUsername().trim();
|
||||
String password = user.getPassword() == null ? "" : user.getPassword().trim();
|
||||
if (username.isEmpty()) {
|
||||
throw new BizException("用户名不能为空");
|
||||
}
|
||||
if (password.isEmpty()) {
|
||||
throw new BizException("密码不能为空");
|
||||
}
|
||||
|
||||
if (user.getId() == null) {
|
||||
userRepository.findByUsername(username).ifPresent(u -> {
|
||||
throw new BizException("账号已存在");
|
||||
});
|
||||
} else {
|
||||
User existing = userRepository.findById(user.getId()).orElseThrow(() -> new BizException("用户不存在"));
|
||||
userRepository.findByUsername(username).ifPresent(u -> {
|
||||
if (!u.getId().equals(existing.getId())) {
|
||||
throw new BizException("账号已存在");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
user.setUsername(username);
|
||||
user.setPassword(password);
|
||||
if (user.getRole() == null) {
|
||||
user.setRole(UserRole.CUSTOMER);
|
||||
}
|
||||
if (user.getNickname() == null || user.getNickname().trim().isEmpty()) {
|
||||
user.setNickname(username);
|
||||
}
|
||||
if (user.getEnabled() == null) {
|
||||
user.setEnabled(true);
|
||||
}
|
||||
return userRepository.save(user);
|
||||
}
|
||||
|
||||
public void adminDeleteUser(Long id) {
|
||||
userRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public List<Banner> adminBanners() {
|
||||
return bannerRepository.findAll();
|
||||
}
|
||||
|
||||
public Banner adminSaveBanner(Banner banner) {
|
||||
return bannerRepository.save(banner);
|
||||
}
|
||||
|
||||
public void adminDeleteBanner(Long id) {
|
||||
bannerRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public List<Product> adminProducts() {
|
||||
return productRepository.findAll();
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> adminProductViews() {
|
||||
List<Product> products = productRepository.findAll();
|
||||
Map<Long, User> userMap = new HashMap<>();
|
||||
for (User user : userRepository.findAll()) {
|
||||
userMap.put(user.getId(), user);
|
||||
}
|
||||
List<Map<String, Object>> views = new ArrayList<>();
|
||||
for (Product p : products) {
|
||||
User merchant = userMap.get(p.getMerchantId());
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("id", p.getId());
|
||||
row.put("name", p.getName());
|
||||
row.put("category", p.getCategory());
|
||||
row.put("description", p.getDescription());
|
||||
row.put("price", p.getPrice());
|
||||
row.put("stock", p.getStock());
|
||||
row.put("imageUrl", p.getImageUrl());
|
||||
row.put("merchantId", p.getMerchantId());
|
||||
row.put("merchantUsername", merchant == null ? "" : merchant.getUsername());
|
||||
row.put("merchantName", merchant == null ? "" : merchant.getNickname());
|
||||
row.put("approved", p.getApproved());
|
||||
row.put("createdAt", p.getCreatedAt());
|
||||
views.add(row);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
public Product adminSaveProduct(Product input) {
|
||||
if (input.getName() == null || input.getName().trim().isEmpty()) {
|
||||
throw new BizException("商品名称不能为空");
|
||||
}
|
||||
if (input.getMerchantId() == null) {
|
||||
throw new BizException("商家ID不能为空");
|
||||
}
|
||||
if (input.getPrice() == null || input.getStock() == null) {
|
||||
throw new BizException("商品价格或库存不能为空");
|
||||
}
|
||||
Product product = input;
|
||||
if (input.getId() != null) {
|
||||
product = productRepository.findById(input.getId()).orElseThrow(() -> new BizException("商品不存在"));
|
||||
product.setName(input.getName());
|
||||
product.setCategory(input.getCategory());
|
||||
product.setDescription(input.getDescription());
|
||||
product.setPrice(input.getPrice());
|
||||
product.setStock(input.getStock());
|
||||
product.setImageUrl(input.getImageUrl());
|
||||
product.setMerchantId(input.getMerchantId());
|
||||
product.setApproved(Boolean.TRUE.equals(input.getApproved()));
|
||||
} else {
|
||||
if (product.getApproved() == null) {
|
||||
product.setApproved(false);
|
||||
}
|
||||
}
|
||||
return productRepository.save(product);
|
||||
}
|
||||
|
||||
public Product adminApproveProduct(Long productId, boolean approved) {
|
||||
Product product = productRepository.findById(productId).orElseThrow(() -> new BizException("商品不存在"));
|
||||
product.setApproved(approved);
|
||||
return productRepository.save(product);
|
||||
}
|
||||
|
||||
public void adminDeleteProduct(Long id) {
|
||||
productRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> adminReviews() {
|
||||
List<Review> reviews = reviewRepository.findAll();
|
||||
Map<Long, User> userMap = new HashMap<>();
|
||||
for (User user : userRepository.findAll()) {
|
||||
userMap.put(user.getId(), user);
|
||||
}
|
||||
Map<Long, Orders> orderMap = new HashMap<>();
|
||||
for (Orders order : ordersRepository.findAll()) {
|
||||
orderMap.put(order.getId(), order);
|
||||
}
|
||||
Map<Long, Product> productMap = new HashMap<>();
|
||||
for (Product product : productRepository.findAll()) {
|
||||
productMap.put(product.getId(), product);
|
||||
}
|
||||
List<Map<String, Object>> views = new ArrayList<>();
|
||||
for (Review r : reviews) {
|
||||
User customer = userMap.get(r.getCustomerId());
|
||||
Orders order = orderMap.get(r.getOrderId());
|
||||
Product product = productMap.get(r.getProductId());
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("id", r.getId());
|
||||
row.put("orderId", r.getOrderId());
|
||||
row.put("orderNo", order == null ? "" : order.getOrderNo());
|
||||
row.put("productId", r.getProductId());
|
||||
row.put("productName", product == null ? "" : product.getName());
|
||||
row.put("customerId", r.getCustomerId());
|
||||
row.put("customerUsername", customer == null ? "" : customer.getUsername());
|
||||
row.put("rating", r.getRating());
|
||||
row.put("content", r.getContent());
|
||||
row.put("createdAt", r.getCreatedAt());
|
||||
views.add(row);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> adminLogistics() {
|
||||
List<LogisticsRecord> records = logisticsRecordRepository.findAll();
|
||||
Map<Long, User> userMap = new HashMap<>();
|
||||
for (User user : userRepository.findAll()) {
|
||||
userMap.put(user.getId(), user);
|
||||
}
|
||||
Map<Long, Orders> orderMap = new HashMap<>();
|
||||
for (Orders order : ordersRepository.findAll()) {
|
||||
orderMap.put(order.getId(), order);
|
||||
}
|
||||
List<Map<String, Object>> views = new ArrayList<>();
|
||||
for (LogisticsRecord rec : records) {
|
||||
User merchant = userMap.get(rec.getMerchantId());
|
||||
Orders order = orderMap.get(rec.getOrderId());
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("id", rec.getId());
|
||||
row.put("orderId", rec.getOrderId());
|
||||
row.put("orderNo", order == null ? "" : order.getOrderNo());
|
||||
row.put("merchantId", rec.getMerchantId());
|
||||
row.put("merchantUsername", merchant == null ? "" : merchant.getUsername());
|
||||
row.put("status", rec.getStatus());
|
||||
row.put("note", rec.getNote());
|
||||
row.put("createdAt", rec.getCreatedAt());
|
||||
views.add(row);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> adminInventory() {
|
||||
List<InventoryRecord> records = inventoryRecordRepository.findAll();
|
||||
Map<Long, User> userMap = new HashMap<>();
|
||||
for (User user : userRepository.findAll()) {
|
||||
userMap.put(user.getId(), user);
|
||||
}
|
||||
Map<Long, Product> productMap = new HashMap<>();
|
||||
for (Product product : productRepository.findAll()) {
|
||||
productMap.put(product.getId(), product);
|
||||
}
|
||||
List<Map<String, Object>> views = new ArrayList<>();
|
||||
for (InventoryRecord rec : records) {
|
||||
User merchant = userMap.get(rec.getMerchantId());
|
||||
Product product = productMap.get(rec.getProductId());
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("id", rec.getId());
|
||||
row.put("productId", rec.getProductId());
|
||||
row.put("productName", product == null ? "" : product.getName());
|
||||
row.put("merchantId", rec.getMerchantId());
|
||||
row.put("merchantUsername", merchant == null ? "" : merchant.getUsername());
|
||||
row.put("changeQty", rec.getChangeQty());
|
||||
row.put("note", rec.getNote());
|
||||
row.put("createdAt", rec.getCreatedAt());
|
||||
views.add(row);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
}
|
||||
20
backend/src/main/resources/application.yml
Normal file
20
backend/src/main/resources/application.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://localhost:3306/maternal_mall?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8
|
||||
username: root
|
||||
password: qq5211314
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
show-sql: false
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
jackson:
|
||||
time-zone: Asia/Shanghai
|
||||
|
||||
app:
|
||||
token-header: X-Token
|
||||
20
backend/target/classes/application.yml
Normal file
20
backend/target/classes/application.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://localhost:3306/maternal_mall?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8
|
||||
username: root
|
||||
password: qq5211314
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
show-sql: false
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
jackson:
|
||||
time-zone: Asia/Shanghai
|
||||
|
||||
app:
|
||||
token-header: X-Token
|
||||
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/common/ApiResponse.class
Normal file
BIN
backend/target/classes/com/maternalmall/common/ApiResponse.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/config/AuthContext.class
Normal file
BIN
backend/target/classes/com/maternalmall/config/AuthContext.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/domain/Banner.class
Normal file
BIN
backend/target/classes/com/maternalmall/domain/Banner.class
Normal file
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/domain/CartItem.class
Normal file
BIN
backend/target/classes/com/maternalmall/domain/CartItem.class
Normal file
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/domain/Favorite.class
Normal file
BIN
backend/target/classes/com/maternalmall/domain/Favorite.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/domain/OrderItem.class
Normal file
BIN
backend/target/classes/com/maternalmall/domain/OrderItem.class
Normal file
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/domain/OrderStatus.class
Normal file
BIN
backend/target/classes/com/maternalmall/domain/OrderStatus.class
Normal file
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/domain/Orders.class
Normal file
BIN
backend/target/classes/com/maternalmall/domain/Orders.class
Normal file
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/domain/Product.class
Normal file
BIN
backend/target/classes/com/maternalmall/domain/Product.class
Normal file
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/domain/Review.class
Normal file
BIN
backend/target/classes/com/maternalmall/domain/Review.class
Normal file
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/domain/User.class
Normal file
BIN
backend/target/classes/com/maternalmall/domain/User.class
Normal file
Binary file not shown.
BIN
backend/target/classes/com/maternalmall/domain/UserRole.class
Normal file
BIN
backend/target/classes/com/maternalmall/domain/UserRole.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user