From a7ce0a089eb2fb47a85ba976be3d1059a675e8c2 Mon Sep 17 00:00:00 2001 From: wangziqi Date: Mon, 9 Feb 2026 09:51:14 -0800 Subject: [PATCH] Initial commit --- .gitignore | 48 + README.md | 79 + backend/pom.xml | 59 + .../maternalmall/MaternalMallApplication.java | 11 + .../com/maternalmall/common/ApiResponse.java | 26 + .../com/maternalmall/common/BizException.java | 7 + .../common/GlobalExceptionHandler.java | 32 + .../com/maternalmall/config/AuthContext.java | 11 + .../maternalmall/config/AuthInterceptor.java | 58 + .../com/maternalmall/config/WebMvcConfig.java | 25 + .../controller/AdminController.java | 165 ++ .../controller/AuthController.java | 47 + .../controller/CustomerController.java | 157 ++ .../controller/MerchantController.java | 100 + .../controller/PublicController.java | 43 + .../java/com/maternalmall/domain/Banner.java | 25 + .../com/maternalmall/domain/CartItem.java | 22 + .../com/maternalmall/domain/Favorite.java | 19 + .../maternalmall/domain/InventoryRecord.java | 31 + .../maternalmall/domain/LogisticsRecord.java | 31 + .../domain/MerchantApplication.java | 35 + .../com/maternalmall/domain/OrderItem.java | 30 + .../com/maternalmall/domain/OrderStatus.java | 11 + .../java/com/maternalmall/domain/Orders.java | 49 + .../java/com/maternalmall/domain/Product.java | 48 + .../java/com/maternalmall/domain/Review.java | 34 + .../java/com/maternalmall/domain/User.java | 48 + .../com/maternalmall/domain/UserRole.java | 7 + .../repository/BannerRepository.java | 10 + .../repository/CartItemRepository.java | 22 + .../repository/FavoriteRepository.java | 18 + .../repository/InventoryRecordRepository.java | 11 + .../repository/LogisticsRecordRepository.java | 11 + .../MerchantApplicationRepository.java | 11 + .../repository/OrderItemRepository.java | 10 + .../repository/OrdersRepository.java | 13 + .../repository/ProductRepository.java | 11 + .../repository/ReviewRepository.java | 12 + .../repository/UserRepository.java | 11 + .../com/maternalmall/service/AuthService.java | 65 + .../com/maternalmall/service/MallService.java | 887 +++++++++ backend/src/main/resources/application.yml | 20 + backend/target/classes/application.yml | 20 + .../MaternalMallApplication.class | Bin 0 -> 757 bytes .../com/maternalmall/common/ApiResponse.class | Bin 0 -> 4086 bytes .../maternalmall/common/BizException.class | Bin 0 -> 413 bytes .../common/GlobalExceptionHandler.class | Bin 0 -> 4078 bytes .../com/maternalmall/config/AuthContext.class | Bin 0 -> 760 bytes .../maternalmall/config/AuthInterceptor.class | Bin 0 -> 3898 bytes .../maternalmall/config/WebMvcConfig.class | Bin 0 -> 2128 bytes .../controller/AdminController.class | Bin 0 -> 10759 bytes .../controller/AuthController.class | Bin 0 -> 3756 bytes .../controller/CustomerController.class | Bin 0 -> 10445 bytes .../controller/MerchantController.class | Bin 0 -> 6899 bytes .../controller/PublicController.class | Bin 0 -> 2750 bytes .../com/maternalmall/domain/Banner.class | Bin 0 -> 4486 bytes .../com/maternalmall/domain/CartItem.class | Bin 0 -> 3847 bytes .../com/maternalmall/domain/Favorite.class | Bin 0 -> 3287 bytes .../maternalmall/domain/InventoryRecord.class | Bin 0 -> 5041 bytes .../maternalmall/domain/LogisticsRecord.class | Bin 0 -> 4939 bytes .../domain/MerchantApplication.class | Bin 0 -> 5573 bytes .../com/maternalmall/domain/OrderItem.class | Bin 0 -> 5025 bytes .../com/maternalmall/domain/OrderStatus.class | Bin 0 -> 1458 bytes .../com/maternalmall/domain/Orders.class | Bin 0 -> 8029 bytes .../com/maternalmall/domain/Product.class | Bin 0 -> 7889 bytes .../com/maternalmall/domain/Review.class | Bin 0 -> 5491 bytes .../com/maternalmall/domain/User.class | Bin 0 -> 7884 bytes .../com/maternalmall/domain/UserRole.class | Bin 0 -> 1199 bytes .../repository/BannerRepository.class | Bin 0 -> 476 bytes .../repository/CartItemRepository.class | Bin 0 -> 1097 bytes .../repository/FavoriteRepository.class | Bin 0 -> 1019 bytes .../InventoryRecordRepository.class | Bin 0 -> 627 bytes .../LogisticsRecordRepository.class | Bin 0 -> 623 bytes .../MerchantApplicationRepository.class | Bin 0 -> 680 bytes .../repository/OrderItemRepository.class | Bin 0 -> 540 bytes .../repository/OrdersRepository.class | Bin 0 -> 794 bytes .../repository/ProductRepository.class | Bin 0 -> 733 bytes .../repository/ReviewRepository.class | Bin 0 -> 725 bytes .../repository/UserRepository.class | Bin 0 -> 583 bytes .../maternalmall/service/AuthService.class | Bin 0 -> 4628 bytes .../maternalmall/service/MallService.class | Bin 0 -> 42745 bytes frontend/index.html | 12 + frontend/package-lock.json | 1684 +++++++++++++++++ frontend/package.json | 24 + frontend/src/App.vue | 3 + frontend/src/api/http.js | 24 + frontend/src/api/index.js | 65 + frontend/src/main.js | 9 + frontend/src/router/index.js | 29 + frontend/src/stores/user.js | 21 + frontend/src/style.css | 13 + frontend/src/views/AdminView.vue | 644 +++++++ frontend/src/views/CartView.vue | 86 + frontend/src/views/CustomerView.vue | 173 ++ frontend/src/views/FavoritesView.vue | 86 + frontend/src/views/HomeView.vue | 132 ++ frontend/src/views/LoginView.vue | 57 + frontend/src/views/MerchantView.vue | 355 ++++ frontend/src/views/OrdersView.vue | 154 ++ frontend/src/views/ProfileView.vue | 75 + frontend/vite.config.js | 9 + sql/init.sql | 144 ++ 崔梦雪-开题报告修改版.md | 237 +++ 需要实现的功能总结.md | 44 + 104 files changed, 6470 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 backend/pom.xml create mode 100644 backend/src/main/java/com/maternalmall/MaternalMallApplication.java create mode 100644 backend/src/main/java/com/maternalmall/common/ApiResponse.java create mode 100644 backend/src/main/java/com/maternalmall/common/BizException.java create mode 100644 backend/src/main/java/com/maternalmall/common/GlobalExceptionHandler.java create mode 100644 backend/src/main/java/com/maternalmall/config/AuthContext.java create mode 100644 backend/src/main/java/com/maternalmall/config/AuthInterceptor.java create mode 100644 backend/src/main/java/com/maternalmall/config/WebMvcConfig.java create mode 100644 backend/src/main/java/com/maternalmall/controller/AdminController.java create mode 100644 backend/src/main/java/com/maternalmall/controller/AuthController.java create mode 100644 backend/src/main/java/com/maternalmall/controller/CustomerController.java create mode 100644 backend/src/main/java/com/maternalmall/controller/MerchantController.java create mode 100644 backend/src/main/java/com/maternalmall/controller/PublicController.java create mode 100644 backend/src/main/java/com/maternalmall/domain/Banner.java create mode 100644 backend/src/main/java/com/maternalmall/domain/CartItem.java create mode 100644 backend/src/main/java/com/maternalmall/domain/Favorite.java create mode 100644 backend/src/main/java/com/maternalmall/domain/InventoryRecord.java create mode 100644 backend/src/main/java/com/maternalmall/domain/LogisticsRecord.java create mode 100644 backend/src/main/java/com/maternalmall/domain/MerchantApplication.java create mode 100644 backend/src/main/java/com/maternalmall/domain/OrderItem.java create mode 100644 backend/src/main/java/com/maternalmall/domain/OrderStatus.java create mode 100644 backend/src/main/java/com/maternalmall/domain/Orders.java create mode 100644 backend/src/main/java/com/maternalmall/domain/Product.java create mode 100644 backend/src/main/java/com/maternalmall/domain/Review.java create mode 100644 backend/src/main/java/com/maternalmall/domain/User.java create mode 100644 backend/src/main/java/com/maternalmall/domain/UserRole.java create mode 100644 backend/src/main/java/com/maternalmall/repository/BannerRepository.java create mode 100644 backend/src/main/java/com/maternalmall/repository/CartItemRepository.java create mode 100644 backend/src/main/java/com/maternalmall/repository/FavoriteRepository.java create mode 100644 backend/src/main/java/com/maternalmall/repository/InventoryRecordRepository.java create mode 100644 backend/src/main/java/com/maternalmall/repository/LogisticsRecordRepository.java create mode 100644 backend/src/main/java/com/maternalmall/repository/MerchantApplicationRepository.java create mode 100644 backend/src/main/java/com/maternalmall/repository/OrderItemRepository.java create mode 100644 backend/src/main/java/com/maternalmall/repository/OrdersRepository.java create mode 100644 backend/src/main/java/com/maternalmall/repository/ProductRepository.java create mode 100644 backend/src/main/java/com/maternalmall/repository/ReviewRepository.java create mode 100644 backend/src/main/java/com/maternalmall/repository/UserRepository.java create mode 100644 backend/src/main/java/com/maternalmall/service/AuthService.java create mode 100644 backend/src/main/java/com/maternalmall/service/MallService.java create mode 100644 backend/src/main/resources/application.yml create mode 100644 backend/target/classes/application.yml create mode 100644 backend/target/classes/com/maternalmall/MaternalMallApplication.class create mode 100644 backend/target/classes/com/maternalmall/common/ApiResponse.class create mode 100644 backend/target/classes/com/maternalmall/common/BizException.class create mode 100644 backend/target/classes/com/maternalmall/common/GlobalExceptionHandler.class create mode 100644 backend/target/classes/com/maternalmall/config/AuthContext.class create mode 100644 backend/target/classes/com/maternalmall/config/AuthInterceptor.class create mode 100644 backend/target/classes/com/maternalmall/config/WebMvcConfig.class create mode 100644 backend/target/classes/com/maternalmall/controller/AdminController.class create mode 100644 backend/target/classes/com/maternalmall/controller/AuthController.class create mode 100644 backend/target/classes/com/maternalmall/controller/CustomerController.class create mode 100644 backend/target/classes/com/maternalmall/controller/MerchantController.class create mode 100644 backend/target/classes/com/maternalmall/controller/PublicController.class create mode 100644 backend/target/classes/com/maternalmall/domain/Banner.class create mode 100644 backend/target/classes/com/maternalmall/domain/CartItem.class create mode 100644 backend/target/classes/com/maternalmall/domain/Favorite.class create mode 100644 backend/target/classes/com/maternalmall/domain/InventoryRecord.class create mode 100644 backend/target/classes/com/maternalmall/domain/LogisticsRecord.class create mode 100644 backend/target/classes/com/maternalmall/domain/MerchantApplication.class create mode 100644 backend/target/classes/com/maternalmall/domain/OrderItem.class create mode 100644 backend/target/classes/com/maternalmall/domain/OrderStatus.class create mode 100644 backend/target/classes/com/maternalmall/domain/Orders.class create mode 100644 backend/target/classes/com/maternalmall/domain/Product.class create mode 100644 backend/target/classes/com/maternalmall/domain/Review.class create mode 100644 backend/target/classes/com/maternalmall/domain/User.class create mode 100644 backend/target/classes/com/maternalmall/domain/UserRole.class create mode 100644 backend/target/classes/com/maternalmall/repository/BannerRepository.class create mode 100644 backend/target/classes/com/maternalmall/repository/CartItemRepository.class create mode 100644 backend/target/classes/com/maternalmall/repository/FavoriteRepository.class create mode 100644 backend/target/classes/com/maternalmall/repository/InventoryRecordRepository.class create mode 100644 backend/target/classes/com/maternalmall/repository/LogisticsRecordRepository.class create mode 100644 backend/target/classes/com/maternalmall/repository/MerchantApplicationRepository.class create mode 100644 backend/target/classes/com/maternalmall/repository/OrderItemRepository.class create mode 100644 backend/target/classes/com/maternalmall/repository/OrdersRepository.class create mode 100644 backend/target/classes/com/maternalmall/repository/ProductRepository.class create mode 100644 backend/target/classes/com/maternalmall/repository/ReviewRepository.class create mode 100644 backend/target/classes/com/maternalmall/repository/UserRepository.class create mode 100644 backend/target/classes/com/maternalmall/service/AuthService.class create mode 100644 backend/target/classes/com/maternalmall/service/MallService.class create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/src/App.vue create mode 100644 frontend/src/api/http.js create mode 100644 frontend/src/api/index.js create mode 100644 frontend/src/main.js create mode 100644 frontend/src/router/index.js create mode 100644 frontend/src/stores/user.js create mode 100644 frontend/src/style.css create mode 100644 frontend/src/views/AdminView.vue create mode 100644 frontend/src/views/CartView.vue create mode 100644 frontend/src/views/CustomerView.vue create mode 100644 frontend/src/views/FavoritesView.vue create mode 100644 frontend/src/views/HomeView.vue create mode 100644 frontend/src/views/LoginView.vue create mode 100644 frontend/src/views/MerchantView.vue create mode 100644 frontend/src/views/OrdersView.vue create mode 100644 frontend/src/views/ProfileView.vue create mode 100644 frontend/vite.config.js create mode 100644 sql/init.sql create mode 100644 崔梦雪-开题报告修改版.md create mode 100644 需要实现的功能总结.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d92744d --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +# Node.js +node_modules/ +npm-debug.log +yarn-error.log +yarn-debug.log +.pnpm-debug.log +.npm +.yarn + +# Build outputs +dist/ +build/ +out/ +.next/ +.nuxt/ +.cache/ + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE and editors +.vscode/ +.idea/ +*.swp +*.swo +.DS_Store +*.sublime-project +*.sublime-workspace + +# Logs +logs +*.log + +# Testing +coverage/ +.nyc_output/ + +# Dependency directories +jspm_packages/ +bower_components/ + +# Misc +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5d494ab --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# 萌贝母婴商城(Spring Boot + Vue + Arco Design + MySQL) + +基于《崔梦雪-开题报告修改版》实现的三端基础版母婴商城,包含顾客端、商家端、管理员端核心功能。 + +## 1. 技术栈 + +- 后端: Spring Boot 3 + Spring Web + Spring Data JPA +- 前端: Vue3 + Vite + Arco Design +- 数据库: MySQL 8 + +## 2. 功能覆盖 + +### 顾客 +- 登录注册 +- 搜索/浏览商品 +- 购物车管理(加购、移除、结算) +- 下单购买 +- 收藏管理 +- 评价商品 +- 订单管理(退款申请、查看物流、修改地址、删除订单) +- 个人信息修改 + +### 商家 +- 数据概览(订单量、销售额、热销商品) +- 商品管理(增删改查) +- 订单管理(查看、发货、退款处理) +- 评价查看 +- 物流查看 +- 库存记录查看/删除 +- 个人信息修改(通过 `/api/auth/me`) + +### 管理员 +- 数据概览 +- 订单管理 +- 审核管理(商家入驻审核) +- 用户管理 +- 轮播图管理 +- 个人信息修改(通过 `/api/auth/me`) + +## 3. 目录结构 + +- `/Users/apple/code/bs/mying/backend` 后端工程 +- `/Users/apple/code/bs/mying/frontend` 前端工程 +- `/Users/apple/code/bs/mying/sql/init.sql` 数据库初始化脚本 + +## 4. 启动步骤 + +1. 创建并初始化数据库: + - 执行 `/Users/apple/code/bs/mying/sql/init.sql` +2. 修改后端数据库配置: + - 文件: `/Users/apple/code/bs/mying/backend/src/main/resources/application.yml` +3. 启动后端: +```bash +cd /Users/apple/code/bs/mying/backend +mvn spring-boot:run +``` +4. 启动前端: +```bash +cd /Users/apple/code/bs/mying/frontend +npm install +npm run dev +``` +5. 访问前端: + - `http://localhost:5173` + +## 5. 默认账号 + +- 管理员: `admin / 123456` +- 商家: `merchant1 / 123456` +- 顾客: `customer1 / 123456` + +## 6. 主要接口前缀 + +- `/api/auth` 登录注册与个人资料 +- `/api/public` 商品与轮播公开接口 +- `/api/customer` 顾客接口 +- `/api/merchant` 商家接口 +- `/api/admin` 管理员接口 + diff --git a/backend/pom.xml b/backend/pom.xml new file mode 100644 index 0000000..4eeaa4e --- /dev/null +++ b/backend/pom.xml @@ -0,0 +1,59 @@ + + 4.0.0 + com.maternalmall + backend + 1.0.0 + maternal-mall-backend + Maternal mall backend + + + org.springframework.boot + spring-boot-starter-parent + 3.3.5 + + + + + 17 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.mysql + mysql-connector-j + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/backend/src/main/java/com/maternalmall/MaternalMallApplication.java b/backend/src/main/java/com/maternalmall/MaternalMallApplication.java new file mode 100644 index 0000000..3de676b --- /dev/null +++ b/backend/src/main/java/com/maternalmall/MaternalMallApplication.java @@ -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); + } +} diff --git a/backend/src/main/java/com/maternalmall/common/ApiResponse.java b/backend/src/main/java/com/maternalmall/common/ApiResponse.java new file mode 100644 index 0000000..f24a2de --- /dev/null +++ b/backend/src/main/java/com/maternalmall/common/ApiResponse.java @@ -0,0 +1,26 @@ +package com.maternalmall.common; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ApiResponse { + private int code; + private String message; + private T data; + + public static ApiResponse ok(T data) { + return new ApiResponse<>(0, "ok", data); + } + + public static ApiResponse ok() { + return new ApiResponse<>(0, "ok", null); + } + + public static ApiResponse fail(String message) { + return new ApiResponse<>(-1, message, null); + } +} diff --git a/backend/src/main/java/com/maternalmall/common/BizException.java b/backend/src/main/java/com/maternalmall/common/BizException.java new file mode 100644 index 0000000..5afd58b --- /dev/null +++ b/backend/src/main/java/com/maternalmall/common/BizException.java @@ -0,0 +1,7 @@ +package com.maternalmall.common; + +public class BizException extends RuntimeException { + public BizException(String message) { + super(message); + } +} diff --git a/backend/src/main/java/com/maternalmall/common/GlobalExceptionHandler.java b/backend/src/main/java/com/maternalmall/common/GlobalExceptionHandler.java new file mode 100644 index 0000000..3f8a86e --- /dev/null +++ b/backend/src/main/java/com/maternalmall/common/GlobalExceptionHandler.java @@ -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 handleBiz(BizException ex) { + return ApiResponse.fail(ex.getMessage()); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ApiResponse 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 handleConstraint(ConstraintViolationException ex) { + return ApiResponse.fail(ex.getMessage()); + } + + @ExceptionHandler(Exception.class) + public ApiResponse handleUnknown(Exception ex) { + return ApiResponse.fail(ex.getMessage()); + } +} diff --git a/backend/src/main/java/com/maternalmall/config/AuthContext.java b/backend/src/main/java/com/maternalmall/config/AuthContext.java new file mode 100644 index 0000000..7693194 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/config/AuthContext.java @@ -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); + } +} diff --git a/backend/src/main/java/com/maternalmall/config/AuthInterceptor.java b/backend/src/main/java/com/maternalmall/config/AuthInterceptor.java new file mode 100644 index 0000000..5986a89 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/config/AuthInterceptor.java @@ -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 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; + } +} diff --git a/backend/src/main/java/com/maternalmall/config/WebMvcConfig.java b/backend/src/main/java/com/maternalmall/config/WebMvcConfig.java new file mode 100644 index 0000000..f0c8f17 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/config/WebMvcConfig.java @@ -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("*"); + } +} diff --git a/backend/src/main/java/com/maternalmall/controller/AdminController.java b/backend/src/main/java/com/maternalmall/controller/AdminController.java new file mode 100644 index 0000000..3bcfea1 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/controller/AdminController.java @@ -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> overview(HttpServletRequest request) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminOverview()); + } + + @GetMapping("/orders") + public ApiResponse> orders(HttpServletRequest request) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminOrders()); + } + + @PutMapping("/orders/{id}") + public ApiResponse updateOrder(HttpServletRequest request, @PathVariable Long id, @RequestBody Map body) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminUpdateOrder(id, body.get("status"), body.getOrDefault("logisticsInfo", ""))); + } + + @GetMapping("/orders/risk") + public ApiResponse>> riskOrders(HttpServletRequest request) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminRiskOrders()); + } + + @PutMapping("/orders/{id}/refund-audit") + public ApiResponse auditRefund(HttpServletRequest request, @PathVariable Long id, @RequestBody Map 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 auditShipment(HttpServletRequest request, @PathVariable Long id, @RequestBody Map 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>> merchantApplications(HttpServletRequest request) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminMerchantApplications()); + } + + @PutMapping("/merchant-applications/{id}") + public ApiResponse auditMerchant(HttpServletRequest request, @PathVariable Long id, @RequestBody Map body) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminAuditApplication(id, body.get("status"), body.getOrDefault("remark", ""))); + } + + @GetMapping("/users") + public ApiResponse> users(HttpServletRequest request) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminUsers()); + } + + @PostMapping("/users") + public ApiResponse saveUser(HttpServletRequest request, @RequestBody User user) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminSaveUser(user)); + } + + @DeleteMapping("/users/{id}") + public ApiResponse deleteUser(HttpServletRequest request, @PathVariable Long id) { + check(AuthContext.getUser(request)); + mallService.adminDeleteUser(id); + return ApiResponse.ok(); + } + + @GetMapping("/banners") + public ApiResponse> banners(HttpServletRequest request) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminBanners()); + } + + @PostMapping("/banners") + public ApiResponse saveBanner(HttpServletRequest request, @RequestBody Banner banner) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminSaveBanner(banner)); + } + + @DeleteMapping("/banners/{id}") + public ApiResponse deleteBanner(HttpServletRequest request, @PathVariable Long id) { + check(AuthContext.getUser(request)); + mallService.adminDeleteBanner(id); + return ApiResponse.ok(); + } + + @GetMapping("/products") + public ApiResponse> products(HttpServletRequest request) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminProducts()); + } + + @GetMapping("/products/views") + public ApiResponse>> productViews(HttpServletRequest request) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminProductViews()); + } + + @PostMapping("/products") + public ApiResponse saveProduct(HttpServletRequest request, @RequestBody Product product) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminSaveProduct(product)); + } + + @PutMapping("/products/{id}/approve") + public ApiResponse approveProduct(HttpServletRequest request, @PathVariable Long id, @RequestBody Map 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 deleteProduct(HttpServletRequest request, @PathVariable Long id) { + check(AuthContext.getUser(request)); + mallService.adminDeleteProduct(id); + return ApiResponse.ok(); + } + + @GetMapping("/reviews") + public ApiResponse>> reviews(HttpServletRequest request) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminReviews()); + } + + @GetMapping("/logistics") + public ApiResponse>> logistics(HttpServletRequest request) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminLogistics()); + } + + @GetMapping("/inventory") + public ApiResponse>> inventory(HttpServletRequest request) { + check(AuthContext.getUser(request)); + return ApiResponse.ok(mallService.adminInventory()); + } +} diff --git a/backend/src/main/java/com/maternalmall/controller/AuthController.java b/backend/src/main/java/com/maternalmall/controller/AuthController.java new file mode 100644 index 0000000..cdd00b1 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/controller/AuthController.java @@ -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> register(@RequestBody Map body) { + return ApiResponse.ok(authService.register(body.get("username"), body.get("password"), body.getOrDefault("role", "CUSTOMER"))); + } + + @PostMapping("/login") + public ApiResponse> login(@RequestBody Map body) { + return ApiResponse.ok(authService.login(body.get("username"), body.get("password"))); + } + + @GetMapping("/me") + public ApiResponse me(HttpServletRequest request) { + return ApiResponse.ok(AuthContext.getUser(request)); + } + + @PutMapping("/me") + public ApiResponse updateMe(HttpServletRequest request, @RequestBody Map 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)); + } +} diff --git a/backend/src/main/java/com/maternalmall/controller/CustomerController.java b/backend/src/main/java/com/maternalmall/controller/CustomerController.java new file mode 100644 index 0000000..f3c7558 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/controller/CustomerController.java @@ -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> 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>> cartViews(HttpServletRequest request) { + User user = AuthContext.getUser(request); + mallService.requireRole(user, UserRole.CUSTOMER); + return ApiResponse.ok(mallService.cartViews(user.getId())); + } + + @PostMapping("/cart") + public ApiResponse addCart(HttpServletRequest request, @RequestBody Map 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 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> 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>> favoriteViews(HttpServletRequest request) { + User user = AuthContext.getUser(request); + mallService.requireRole(user, UserRole.CUSTOMER); + return ApiResponse.ok(mallService.favoriteViews(user.getId())); + } + + @PostMapping("/favorites") + public ApiResponse addFavorite(HttpServletRequest request, @RequestBody Map 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 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 checkout(HttpServletRequest request, @RequestBody Map 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 buyNow(HttpServletRequest request, @RequestBody Map 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> 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> 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 updateAddress(HttpServletRequest request, @PathVariable Long orderId, @RequestBody Map 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 refund(HttpServletRequest request, @PathVariable Long orderId, @RequestBody Map 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 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> 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(HttpServletRequest request, @RequestBody Map 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 applyMerchant(HttpServletRequest request, @RequestBody Map body) { + User user = AuthContext.getUser(request); + return ApiResponse.ok(mallService.applyMerchant(user.getId(), body.getOrDefault("qualification", ""))); + } +} diff --git a/backend/src/main/java/com/maternalmall/controller/MerchantController.java b/backend/src/main/java/com/maternalmall/controller/MerchantController.java new file mode 100644 index 0000000..acf873e --- /dev/null +++ b/backend/src/main/java/com/maternalmall/controller/MerchantController.java @@ -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> overview(HttpServletRequest request) { + User user = AuthContext.getUser(request); + mallService.requireRole(user, UserRole.MERCHANT); + return ApiResponse.ok(mallService.merchantOverview(user.getId())); + } + + @GetMapping("/products") + public ApiResponse> products(HttpServletRequest request) { + User user = AuthContext.getUser(request); + mallService.requireRole(user, UserRole.MERCHANT); + return ApiResponse.ok(mallService.merchantProducts(user.getId())); + } + + @PostMapping("/products") + public ApiResponse 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 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> 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 ship(HttpServletRequest request, @PathVariable Long id, @RequestBody Map 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 refund(HttpServletRequest request, @PathVariable Long id, @RequestBody Map 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>> reviews(HttpServletRequest request) { + User user = AuthContext.getUser(request); + mallService.requireRole(user, UserRole.MERCHANT); + return ApiResponse.ok(mallService.merchantReviews(user.getId())); + } + + @GetMapping("/logistics") + public ApiResponse>> logistics(HttpServletRequest request) { + User user = AuthContext.getUser(request); + mallService.requireRole(user, UserRole.MERCHANT); + return ApiResponse.ok(mallService.merchantLogistics(user.getId())); + } + + @GetMapping("/inventory") + public ApiResponse>> 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 deleteInventory(HttpServletRequest request, @PathVariable Long id) { + User user = AuthContext.getUser(request); + mallService.requireRole(user, UserRole.MERCHANT); + mallService.merchantDeleteInventory(user.getId(), id); + return ApiResponse.ok(); + } +} diff --git a/backend/src/main/java/com/maternalmall/controller/PublicController.java b/backend/src/main/java/com/maternalmall/controller/PublicController.java new file mode 100644 index 0000000..882bd7a --- /dev/null +++ b/backend/src/main/java/com/maternalmall/controller/PublicController.java @@ -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> products(@RequestParam(required = false) String keyword) { + return ApiResponse.ok(mallService.searchProducts(keyword)); + } + + @GetMapping("/products/{id}") + public ApiResponse product(@PathVariable Long id) { + return ApiResponse.ok(mallService.getProduct(id)); + } + + @GetMapping("/products/{id}/reviews") + public ApiResponse> reviews(@PathVariable Long id) { + return ApiResponse.ok(reviewRepository.findByProductId(id)); + } + + @GetMapping("/banners") + public ApiResponse> banners() { + return ApiResponse.ok(mallService.banners()); + } +} diff --git a/backend/src/main/java/com/maternalmall/domain/Banner.java b/backend/src/main/java/com/maternalmall/domain/Banner.java new file mode 100644 index 0000000..a74691e --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/Banner.java @@ -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; +} diff --git a/backend/src/main/java/com/maternalmall/domain/CartItem.java b/backend/src/main/java/com/maternalmall/domain/CartItem.java new file mode 100644 index 0000000..3975d5f --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/CartItem.java @@ -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; +} diff --git a/backend/src/main/java/com/maternalmall/domain/Favorite.java b/backend/src/main/java/com/maternalmall/domain/Favorite.java new file mode 100644 index 0000000..6b57b12 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/Favorite.java @@ -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; +} diff --git a/backend/src/main/java/com/maternalmall/domain/InventoryRecord.java b/backend/src/main/java/com/maternalmall/domain/InventoryRecord.java new file mode 100644 index 0000000..ac407b6 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/InventoryRecord.java @@ -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; +} diff --git a/backend/src/main/java/com/maternalmall/domain/LogisticsRecord.java b/backend/src/main/java/com/maternalmall/domain/LogisticsRecord.java new file mode 100644 index 0000000..90f6b3f --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/LogisticsRecord.java @@ -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; +} diff --git a/backend/src/main/java/com/maternalmall/domain/MerchantApplication.java b/backend/src/main/java/com/maternalmall/domain/MerchantApplication.java new file mode 100644 index 0000000..2744bc5 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/MerchantApplication.java @@ -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; +} diff --git a/backend/src/main/java/com/maternalmall/domain/OrderItem.java b/backend/src/main/java/com/maternalmall/domain/OrderItem.java new file mode 100644 index 0000000..000bd5c --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/OrderItem.java @@ -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; +} diff --git a/backend/src/main/java/com/maternalmall/domain/OrderStatus.java b/backend/src/main/java/com/maternalmall/domain/OrderStatus.java new file mode 100644 index 0000000..660c1ca --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/OrderStatus.java @@ -0,0 +1,11 @@ +package com.maternalmall.domain; + +public enum OrderStatus { + PENDING_PAYMENT, + PAID, + SHIPPED, + COMPLETED, + REFUND_REQUESTED, + REFUNDED, + CANCELLED +} diff --git a/backend/src/main/java/com/maternalmall/domain/Orders.java b/backend/src/main/java/com/maternalmall/domain/Orders.java new file mode 100644 index 0000000..4b95569 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/Orders.java @@ -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; +} diff --git a/backend/src/main/java/com/maternalmall/domain/Product.java b/backend/src/main/java/com/maternalmall/domain/Product.java new file mode 100644 index 0000000..652e4ed --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/Product.java @@ -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; +} diff --git a/backend/src/main/java/com/maternalmall/domain/Review.java b/backend/src/main/java/com/maternalmall/domain/Review.java new file mode 100644 index 0000000..9b19ff7 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/Review.java @@ -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; +} diff --git a/backend/src/main/java/com/maternalmall/domain/User.java b/backend/src/main/java/com/maternalmall/domain/User.java new file mode 100644 index 0000000..5413967 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/User.java @@ -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; +} diff --git a/backend/src/main/java/com/maternalmall/domain/UserRole.java b/backend/src/main/java/com/maternalmall/domain/UserRole.java new file mode 100644 index 0000000..e9efabd --- /dev/null +++ b/backend/src/main/java/com/maternalmall/domain/UserRole.java @@ -0,0 +1,7 @@ +package com.maternalmall.domain; + +public enum UserRole { + CUSTOMER, + MERCHANT, + ADMIN +} diff --git a/backend/src/main/java/com/maternalmall/repository/BannerRepository.java b/backend/src/main/java/com/maternalmall/repository/BannerRepository.java new file mode 100644 index 0000000..6524a1f --- /dev/null +++ b/backend/src/main/java/com/maternalmall/repository/BannerRepository.java @@ -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 { + List findByEnabledTrueOrderBySortNoAsc(); +} diff --git a/backend/src/main/java/com/maternalmall/repository/CartItemRepository.java b/backend/src/main/java/com/maternalmall/repository/CartItemRepository.java new file mode 100644 index 0000000..8aa6c5a --- /dev/null +++ b/backend/src/main/java/com/maternalmall/repository/CartItemRepository.java @@ -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 { + List findByCustomerId(Long customerId); + Optional findByCustomerIdAndProductId(Long customerId, Long productId); + + @Modifying + @Transactional + void deleteByCustomerIdAndProductId(Long customerId, Long productId); + + @Modifying + @Transactional + void deleteByCustomerId(Long customerId); +} diff --git a/backend/src/main/java/com/maternalmall/repository/FavoriteRepository.java b/backend/src/main/java/com/maternalmall/repository/FavoriteRepository.java new file mode 100644 index 0000000..d51e5c1 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/repository/FavoriteRepository.java @@ -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 { + List findByCustomerId(Long customerId); + Optional findByCustomerIdAndProductId(Long customerId, Long productId); + + @Modifying + @Transactional + void deleteByCustomerIdAndProductId(Long customerId, Long productId); +} diff --git a/backend/src/main/java/com/maternalmall/repository/InventoryRecordRepository.java b/backend/src/main/java/com/maternalmall/repository/InventoryRecordRepository.java new file mode 100644 index 0000000..7f2a9ac --- /dev/null +++ b/backend/src/main/java/com/maternalmall/repository/InventoryRecordRepository.java @@ -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 { + List findByMerchantId(Long merchantId); + List findByProductId(Long productId); +} diff --git a/backend/src/main/java/com/maternalmall/repository/LogisticsRecordRepository.java b/backend/src/main/java/com/maternalmall/repository/LogisticsRecordRepository.java new file mode 100644 index 0000000..2a01074 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/repository/LogisticsRecordRepository.java @@ -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 { + List findByOrderId(Long orderId); + List findByMerchantId(Long merchantId); +} diff --git a/backend/src/main/java/com/maternalmall/repository/MerchantApplicationRepository.java b/backend/src/main/java/com/maternalmall/repository/MerchantApplicationRepository.java new file mode 100644 index 0000000..52ead6a --- /dev/null +++ b/backend/src/main/java/com/maternalmall/repository/MerchantApplicationRepository.java @@ -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 { + List findByStatus(String status); + boolean existsByUserIdAndStatus(Long userId, String status); +} diff --git a/backend/src/main/java/com/maternalmall/repository/OrderItemRepository.java b/backend/src/main/java/com/maternalmall/repository/OrderItemRepository.java new file mode 100644 index 0000000..4ae771b --- /dev/null +++ b/backend/src/main/java/com/maternalmall/repository/OrderItemRepository.java @@ -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 { + List findByOrderId(Long orderId); +} diff --git a/backend/src/main/java/com/maternalmall/repository/OrdersRepository.java b/backend/src/main/java/com/maternalmall/repository/OrdersRepository.java new file mode 100644 index 0000000..62a7a78 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/repository/OrdersRepository.java @@ -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 { + List findByCustomerId(Long customerId); + List findByMerchantId(Long merchantId); + List findByStatus(OrderStatus status); +} diff --git a/backend/src/main/java/com/maternalmall/repository/ProductRepository.java b/backend/src/main/java/com/maternalmall/repository/ProductRepository.java new file mode 100644 index 0000000..e9524a5 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/repository/ProductRepository.java @@ -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 { + List findByApprovedTrueAndNameContainingIgnoreCase(String name); + List findByMerchantId(Long merchantId); +} diff --git a/backend/src/main/java/com/maternalmall/repository/ReviewRepository.java b/backend/src/main/java/com/maternalmall/repository/ReviewRepository.java new file mode 100644 index 0000000..5eb8550 --- /dev/null +++ b/backend/src/main/java/com/maternalmall/repository/ReviewRepository.java @@ -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 { + List findByProductId(Long productId); + List findByCustomerId(Long customerId); + boolean existsByOrderIdAndProductIdAndCustomerId(Long orderId, Long productId, Long customerId); +} diff --git a/backend/src/main/java/com/maternalmall/repository/UserRepository.java b/backend/src/main/java/com/maternalmall/repository/UserRepository.java new file mode 100644 index 0000000..e5d9dff --- /dev/null +++ b/backend/src/main/java/com/maternalmall/repository/UserRepository.java @@ -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 { + Optional findByUsername(String username); + Optional findByToken(String token); +} diff --git a/backend/src/main/java/com/maternalmall/service/AuthService.java b/backend/src/main/java/com/maternalmall/service/AuthService.java new file mode 100644 index 0000000..faae71a --- /dev/null +++ b/backend/src/main/java/com/maternalmall/service/AuthService.java @@ -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 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 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 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; + } +} diff --git a/backend/src/main/java/com/maternalmall/service/MallService.java b/backend/src/main/java/com/maternalmall/service/MallService.java new file mode 100644 index 0000000..1a7a97f --- /dev/null +++ b/backend/src/main/java/com/maternalmall/service/MallService.java @@ -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 searchProducts(String keyword) { + return productRepository.findByApprovedTrueAndNameContainingIgnoreCase(keyword == null ? "" : keyword); + } + + public Product getProduct(Long id) { + return productRepository.findById(id).orElseThrow(() -> new BizException("商品不存在")); + } + + public List banners() { + return bannerRepository.findByEnabledTrueOrderBySortNoAsc(); + } + + public List cartList(Long customerId) { + return cartItemRepository.findByCustomerId(customerId); + } + + public List> cartViews(Long customerId) { + List items = cartItemRepository.findByCustomerId(customerId); + List> views = new ArrayList<>(); + for (CartItem item : items) { + Product p = getProduct(item.getProductId()); + Map 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 favoriteList(Long customerId) { + return favoriteRepository.findByCustomerId(customerId); + } + + public List> favoriteViews(Long customerId) { + List favorites = favoriteRepository.findByCustomerId(customerId); + List> views = new ArrayList<>(); + for (Favorite favorite : favorites) { + Product p = getProduct(favorite.getProductId()); + Map 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 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 customerOrders(Long customerId) { + return ordersRepository.findByCustomerId(customerId); + } + + public List 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 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 merchantOverview(Long merchantId) { + List 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 hotMap = new HashMap<>(); + Map 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 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 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> merchantReviews(Long merchantId) { + List products = productRepository.findByMerchantId(merchantId); + Set productIds = new HashSet<>(); + for (Product product : products) { + productIds.add(product.getId()); + } + List reviews = reviewRepository.findAll(); + Map orderMap = new HashMap<>(); + for (Orders order : ordersRepository.findAll()) { + orderMap.put(order.getId(), order); + } + List> views = new ArrayList<>(); + for (Review r : reviews) { + if (!productIds.contains(r.getProductId())) continue; + Orders order = orderMap.get(r.getOrderId()); + Map 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> merchantLogistics(Long merchantId) { + List records = logisticsRecordRepository.findByMerchantId(merchantId); + Map orderMap = new HashMap<>(); + for (Orders order : ordersRepository.findAll()) { + orderMap.put(order.getId(), order); + } + List> views = new ArrayList<>(); + for (LogisticsRecord rec : records) { + Orders order = orderMap.get(rec.getOrderId()); + Map 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> merchantInventory(Long merchantId) { + List records = inventoryRecordRepository.findByMerchantId(merchantId); + Map productMap = new HashMap<>(); + for (Product product : productRepository.findByMerchantId(merchantId)) { + productMap.put(product.getId(), product); + } + List> views = new ArrayList<>(); + for (InventoryRecord rec : records) { + Product product = productMap.get(rec.getProductId()); + Map 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 adminOverview() { + List orders = ordersRepository.findAll(); + BigDecimal sales = orders.stream().map(Orders::getTotalAmount).reduce(BigDecimal.ZERO, BigDecimal::add); + Map categoryCount = new HashMap<>(); + Map 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 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> adminRiskOrders() { + List orders = ordersRepository.findAll(); + List> 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 buildRisk(Orders order, String type, String reason) { + Map 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> adminMerchantApplications() { + List apps = merchantApplicationRepository.findAll(); + List> views = new ArrayList<>(); + Map userMap = new HashMap<>(); + for (User user : userRepository.findAll()) { + userMap.put(user.getId(), user); + } + for (MerchantApplication app : apps) { + User applicant = userMap.get(app.getUserId()); + Map 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 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 adminBanners() { + return bannerRepository.findAll(); + } + + public Banner adminSaveBanner(Banner banner) { + return bannerRepository.save(banner); + } + + public void adminDeleteBanner(Long id) { + bannerRepository.deleteById(id); + } + + public List adminProducts() { + return productRepository.findAll(); + } + + public List> adminProductViews() { + List products = productRepository.findAll(); + Map userMap = new HashMap<>(); + for (User user : userRepository.findAll()) { + userMap.put(user.getId(), user); + } + List> views = new ArrayList<>(); + for (Product p : products) { + User merchant = userMap.get(p.getMerchantId()); + Map 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> adminReviews() { + List reviews = reviewRepository.findAll(); + Map userMap = new HashMap<>(); + for (User user : userRepository.findAll()) { + userMap.put(user.getId(), user); + } + Map orderMap = new HashMap<>(); + for (Orders order : ordersRepository.findAll()) { + orderMap.put(order.getId(), order); + } + Map productMap = new HashMap<>(); + for (Product product : productRepository.findAll()) { + productMap.put(product.getId(), product); + } + List> 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 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> adminLogistics() { + List records = logisticsRecordRepository.findAll(); + Map userMap = new HashMap<>(); + for (User user : userRepository.findAll()) { + userMap.put(user.getId(), user); + } + Map orderMap = new HashMap<>(); + for (Orders order : ordersRepository.findAll()) { + orderMap.put(order.getId(), order); + } + List> views = new ArrayList<>(); + for (LogisticsRecord rec : records) { + User merchant = userMap.get(rec.getMerchantId()); + Orders order = orderMap.get(rec.getOrderId()); + Map 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> adminInventory() { + List records = inventoryRecordRepository.findAll(); + Map userMap = new HashMap<>(); + for (User user : userRepository.findAll()) { + userMap.put(user.getId(), user); + } + Map productMap = new HashMap<>(); + for (Product product : productRepository.findAll()) { + productMap.put(product.getId(), product); + } + List> views = new ArrayList<>(); + for (InventoryRecord rec : records) { + User merchant = userMap.get(rec.getMerchantId()); + Product product = productMap.get(rec.getProductId()); + Map 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; + } +} diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml new file mode 100644 index 0000000..cfbfb44 --- /dev/null +++ b/backend/src/main/resources/application.yml @@ -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 diff --git a/backend/target/classes/application.yml b/backend/target/classes/application.yml new file mode 100644 index 0000000..cfbfb44 --- /dev/null +++ b/backend/target/classes/application.yml @@ -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 diff --git a/backend/target/classes/com/maternalmall/MaternalMallApplication.class b/backend/target/classes/com/maternalmall/MaternalMallApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..61c707e0905d3abac138cc15bd3a21db76a2f0f9 GIT binary patch literal 757 zcma)4OHbQC5dJnKIE3&D<<-K0TgZXEaS25#kvKVk2ucqU+ru|!-ia78C z_)!(JZlvT8BrJJG^ZI7?n|b^9@(SQP_8O>QER7ET%6Km&^bnoTTWnW1BB!t;?; zI=QtXlfR99;E^$o4@&1$C3UlfW{BG1WTIXw^1Sq(kZbqFolifnetQae!%{rXO3a#s?bhla;S3U z8PM9eayx``GLyJ+C8BVK7%Z8S;{~14uBI65VjltBkN+xQ{(?xZki|(e(zVWCu%|M- zO45fxC2J&`{}a%{I#~<&f(-)k#YZI=P;UjDXDs}j)ca%gxdGkP1fz~kY>|JP{u+@? Qs)C(Te#JM^zU?mX4m)wi@c;k- literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/common/ApiResponse.class b/backend/target/classes/com/maternalmall/common/ApiResponse.class new file mode 100644 index 0000000000000000000000000000000000000000..7edbcb3ae86dc7b87bcf39fd729492aec292df11 GIT binary patch literal 4086 zcmbVP-BS}+6hAlFO#&1>QG`z71i$c894)*f1UKRdMA2I+Q#I>->D<=u5lkjOD{k3n6O6cjI7o@Z`= z-W`%x8kw=%pncQ>>2=dL>$#K__OQ>O{d7Q3B5ykLkIH62P;WY>UCBF>8D|XM%cH+rs-|6bM17B#^Q9^pfmJdogwK7wCEE_}Sadw!bbL?Hg@4Jh0^%RA`H)NIf*>HfDyVJQ z-f&F6Ttc3sD=S&n9`dV_orM$)<;K+T!Vh(jBOcP21UVw=8v+$bodp%=>QX zv7oQQRjL@J(krI3Ua-7@1=rmwZ-=iSmC2Cjb%HCH15McPh4?F(4w00KSzMXRwr^z< zw+eo7ca8819g>;+|89$M@6YY*>UXcDB_EDO` z-&k;#q>q?G^6|Zl-EzN;@?I{-Fh&w|1bYPUj z`7|Dh0aLDGcMT6Cw1BtZPlzU}I~&0?{)cLOMkp-8Uhi#G-Pw|A3C3cB_t)u$GO>Ut zA{<%f;4^Cb9;+}i@xVr(BcKFY!w73)5jmLRV*xPXsF8M8P77vc~t z4`;q5U3)tGD~Yd3k3Jp#g~Z}W<~j8STP#u|c)sB?Yz|+??(n!a`Wtnmv{$tE2^pg) z?KvH*un&&wU>O{brF7QH;=HnWOhQEJe16Ca&qoZ23g@Pa*{|6KcV>ObIJtt!EucbQkptRaHeo{ zsCfp?O8qJWQPk6Cftxyd?sKdnaQ-xXfxl521rqnM(n7(^DYO~mtE9aF&>Aon0cj_#=(Jr1yy$hm*{RM7>vqI1S+% zW%o$MZwUo{ZSK9G^Nk&wXld@%PXqk2Rny>0n9$%z53auR!OY8yU=C$Qo6Lr`z*0u? zs~{k$rXR0(?r>H11ovb#$d2rV9+e$=mIZcPbe&NMCj+tC+*qrFTvO8|PwNnfkVOyh VP5}B1JVkP` literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/common/BizException.class b/backend/target/classes/com/maternalmall/common/BizException.class new file mode 100644 index 0000000000000000000000000000000000000000..f7e98f59c737bc475b8106ed807072d1428f34ec GIT binary patch literal 413 zcma)&%Sr<=7==&9>5PumUPKq}1Q9RbJV3PzMOUMUmG0ANm_~AuOs0r$YFl^uRInNb0ReswTFSWRMEX9MD)-WU| z(nvpK*xWCEb0hDhsRjq*6w3^|rPaCSUO2;*=1S#cXlwE_`LuwF&+Sw&bP8$2b)zT3 z-SLScr(f8Tt1)*n=pQroGg&k275{+`7*Z|jnpa=AKSde3SHjQi^oBdG1vMpfHnNQ? z#ia}#dW&6;0{}ZnX;I-Yy0j`%E2M_>2R}JHdI5W;DMChXSSu(OlB+areW%dI8Y#+S S9UUTdX>KtR0o8}7^s_@|Q+2!BRrGLWZo|&|n&j4rv#;O&_MVg9{<-}NfFt-Oh8C>S z(5j;i?F{i5KF1A{TP5REZbsx?hW0^eNq2~0RbMI_LkBuF#B_8)XGrE%*(h^Y*cLa- z+%yeZC@aetkypnq=S9Vp%3_F>ggYS|hnEDyy1rDV?q$lgrBzDD(2X@3;yTtM!LYNL z-QkKnBbGPjcR$Ju-4wyN6lP(}ww3KLBo?u*y3#Z<(s9YdHf-0hL&r`eNyRV4 z(l(Xbac#lN44WHtgH?mdMVn+~Hy+o}rz3?u3>)i48sr!{igItLxaInJ~z0T zP7o3I>39PB8QOTIVqRg`+8|o2T6rI<@!A6wt{(?9JgMU#o}x7C{uj*>UCX?}aIi)W zKf~pQYE<-h7gf_8c!oj$(m`A6%H>qd$J7LzT-aSx662xV>kp zmMhC5D;-JD4qKLTeWp?*1Cdj2X_WFMS>w)9lHoRLges-b6=3a;Sq9N}cboSZ&nvFN z(1W@0=8jYHc(;t%4D3CHRHj&s(m zrRM2?G8mp#OO#x3zUxE9u4^+)K7iTCxYwDyoGb8Ts56rN3`e7R75bpm=R749!AwT| zwl|Xb(MIS@H>Ij}UW`j`%5IJxnEO3X4EJcM&@`ZPlS>?RZDrC}A1=(vyr78?A5qHn zK8Tf|%bhjtn2+Hyu4uTb<4wH9u%n)pwC2>TFfu+?q``|k?<$+l6u6~ zu%O~lwD3U;AL1hoAA4WyPw2$H45r|_8+^pa7lHcz;X_mV-d_ws8F7fZGtCZsMkAID zFgl@~v@Btdn%r@Oqv1=2UCWY-DyN388MfSAl#Y2p?XjHzZJ{G7&`t**`jbT07wOHQ zJEMEso}a*eq=y!qsR;p{n4)Jr5WrcSqc?^u&ZE^=9;7$VXvf|KXg_21O?nRNulDsf z1Tr;!lEwwRM4B&?bQ=wDx?S`YU%{(%@20KS=y&NNSl$=;;f5h3n}37ox_pdjJ3c literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/config/AuthContext.class b/backend/target/classes/com/maternalmall/config/AuthContext.class new file mode 100644 index 0000000000000000000000000000000000000000..f7bf2e8acdd018f0c3e2a84c43f9a70e3dd190e6 GIT binary patch literal 760 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 literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/controller/AdminController.class b/backend/target/classes/com/maternalmall/controller/AdminController.class new file mode 100644 index 0000000000000000000000000000000000000000..756a23d933a3d6d095498d89d20ed61fa8ddee36 GIT binary patch literal 10759 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 literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/controller/PublicController.class b/backend/target/classes/com/maternalmall/controller/PublicController.class new file mode 100644 index 0000000000000000000000000000000000000000..27a8a8613d227deb0152ba789cbaaba426db8f9b GIT binary patch literal 2750 zcmbVOYjfK~6g}(2QEaPDaN9u3qiq^m+X-2qr4JlJTDa?((kT3CA+bu3?(IWm}$O zo2GDhz27utYdacX$SIRu;T+1AV7OL`t#*SszB90^sE~Ob3s@w0jyRO!s3Cf`D?Qse zW>}AVaiVG77_+ZrQNURZ=X9LM62rQ zeNURaCS8wWy@LxHF6wv}?=f7B#iD_(ZSk$1YzVh!TQ0GZvJV*U4!aK$LGGtm@9*ye z9hY#KA=?pN2$|u=u*;h5{z!N`Mz5!i z9YgxiF#EKZ3?I<9JQ^kmW7%E1@3h22scOmj@%wRI?VsV! zDMaadgX@*JIL%UD&x13R<3>;No_}TI3SGG=5_XC%OS*YSSe4G*)S5je0+* zp9g9e%D;n^S6@LZuf4*VUuh_XkLX>%EWOXt`y4WKn+ zLkV9~230Z?or>_BO2%tkPXJKz0qB8Q2q2*Sn}k?3+SCp?OyP+?TcEF})UVU}ATi(m E2S7FJ2mk;8 literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/domain/Banner.class b/backend/target/classes/com/maternalmall/domain/Banner.class new file mode 100644 index 0000000000000000000000000000000000000000..5950cce0c94e720b56adfb7e6c02c99fc8c99761 GIT binary patch literal 4486 zcma)9ZF3t}6@IR?(ypcTYw8!drlhS}{E}1L1K?*+!gF`;N-J5!nX#mE?|II%=bm%! zIqQG^_r1RYSi)O5q>$F3naCh3Fml~Gv`Vh!)k>FluG`f>AbZa7oM2fXJv+CZgMk4Z zITLx90;3&x#S843-4M_ZEw^P~epz5*wlcSN1hF`PAq?vnF)@lUfrqMoy;QdXyWv@G z-E!U1u3xttuXMrkJV}^s`i)@27dX*}EQbd$u46(vG}-0Q1>bjV%ad+gOS+-IT30P^^_7-I>+#uyd6bRCxoZ-9%EW0rA&{xrL52Q| z%_a{RdL;AuNfV!voLO5EHl^jKO?(EQ6&Rr9rV8tLm$k~{qC|hr#FKbRAV>6S9HDM_ z93u%|G;t0~0(t3HI|vVS(W@{>_~%W0LBb{bS{#WKE|^%sMMh%JYVNK0yLMQ4l`+=q zB@<8M8G*^VwQsNZUeyYocLIXEW?*?iQ@~5cEDS^-)aO_>A-F@on~NrRl8K#T@)zxRXuI9;CT~vx8)Tg z%xDI3UAu9N1)sNd;~=!CTwLAQs%*U=Fw^%LE56&Rdy?C0xvs4GG$RXEiEDecU{9{@ z3TRQ}hnX0AqgCIr8(R`9Yuc|`?zYu%#yq?sk}+U98s$5cptck z@54m$IdMJ27VT@$g=&`wc?COF^5UzT%X9d_Uf3XIu!^#e7~xJR=EKdn)0vT?y$qG@ z9yJ(gJL`zBec~ZS+ukrkfa*2^%D%JS46N$@x^rIsw}?YW zsnt9tolfqkQ8B_q1oF-tL{-ro&f?V^euCF@{8ToN*LjgT6Up%o{e8O>c^UPSOI9`T z8@zA7NmhXZMx@VJ-mYsmi)+5W-#S>NgYn2z)s@)kG7PV1LaE>{Cbr@>pV(3qE1Zm* zPGBz@cthaHD2B7n?vgllG7i6#RrsYCrAkUFRZC({z^urzy5d^Rrrp%>3xUTvC+PCG z`=C00#bJGKC{qzd1rM{qr`Sp|{N~_ff2nXa%GI;{{UXGqlYyxz)tP^y$Y!q|}G89NJ z7zt3hJU~#noGT1SP)>mc3b_Pmxtu3xxoj5l5@ae6r5L2$;?dG!+eyaj)#%9WTKy0< zUS{VQ$KO$7+ex8_KVpx2X$Ir1)ouk1!R91ILR{Nw#~E ztjT|VR1H1FvxSpGW>_YxSK^pdb@o)YbBz<#a@tn34 z7Rt=X;vUPy7+JUp{q!B2e@X72DQi+u(3HlI#S^K{mNTJ>brTvHg-om}m$RWN+g7of zBCF-H9;#SK%1RGaGyT(kHF8s>J&o7VM8Gp<@ETf_QcTnjamaJ0IU)D(4N4l8neJ~= z%CLW(!3|1TVkhw}N;2)hzmU7j4UI_@f{B9=cS+G%r{u;sGBF^=Xx&>1mQBDq$lhHA z*1N$Sd>@4}F^-sYtorL{*wehf-5z@v85lW;r)A>#Df6=|lg-cB_pl1|PM)aXqA@?+ wXF6;6PFSMf4c~8u8PD%={e3>=2TkVZM_d?O{FtAg;Z3e`lzxt1@_H8j2L#1$p#T5? literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/domain/CartItem.class b/backend/target/classes/com/maternalmall/domain/CartItem.class new file mode 100644 index 0000000000000000000000000000000000000000..b60aacf463391de2830365be41f661cd23a8bb6d GIT binary patch literal 3847 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 r8@Gg)8fuR>fD4K5IL+|FDAO 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 literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/domain/MerchantApplication.class b/backend/target/classes/com/maternalmall/domain/MerchantApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..46203abcdd6c688766a6aa77ddb3429af9baaccd GIT binary patch literal 5573 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 literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/domain/OrderStatus.class b/backend/target/classes/com/maternalmall/domain/OrderStatus.class new file mode 100644 index 0000000000000000000000000000000000000000..62b615f17cb179e182377982117102f4d545501a GIT binary patch literal 1458 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 DbD#k$ literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/domain/Review.class b/backend/target/classes/com/maternalmall/domain/Review.class new file mode 100644 index 0000000000000000000000000000000000000000..0ea2ddac5ee5815bb8faf956e5dff4d892d939b7 GIT binary patch literal 5491 zcmbVQ+jARN8UL+y*_FJu6sJz2#)OzU#j)cm5D0V~8&W4}1a=%^WQNHMm!T=sVLJ4s%<#+;ed-(k0WXB|JA2MbYiS!BX0+PxeBbZ< z&N<)tzV96AZ~uAeWdJkyRTh0n=t!DKA*~^Q-QKc`HQQY)F0Nj8%D#s5sj6G`XEh|o zkFI23AfqE|q93M)LuIdCtlPfRaP3;%uGNYauWnb};w5LR>O7&LuUaALe1~=3bJwN? z%b)ajN_<=)SAPUaTeo8 zBhbJxVmBsD921;rM-Vne&xcGDMb9jIE=qR^^*caavnT}&Z{^~ntm#q5b(k$qWkq<7 zFr(><_S(%IdT_kuo0~z8(wVatmP^Z5G>pWKG3V77c{&_pVRmJ*fO!a<^hUJTzbP>Z{&HvF$l_Ni}8u?(GSbqbm&51;=0a zDi`gBU3XX*IgKnksI+Bvu+dIdrYM`86lG$v9VX_!=m23Pi7^CWJ0G_d92GYLBnFD4 z;fNxO$wGnFyzOOCRW7d9^VgjQvrV{kOq6pY&;+6xC{+dLVx*C4eGop0qv$_2+Nb2Lpf~AIYcVDnnOf0u;kn2jRkvCMNjJDrP2Z` zl2#I@i_>h?cn~Dy+Q-_9%Z^2s(LWZQuRaz|&KPS?Bs&%v((paGRou)auhl3!=c*!_ z`{lv;kT@{kEW^&SAow>$?8YWwun3VcE(0)FfIN!xk9eLh@_kcAXfv0_53=|nex&1B zQ5BzK5$GJ$Rd>tVaEfx43^o?Zb9UMH8mtx1MFUd-CFyzFt<;?6*u3X$v^J+X!SFhv zA~T}d)-fnA5u<`X7Ey(VX+)(c=6Lg1s`}2ff#)?WOCOF`D>K@$!)m{T*xoPE=q_ethYuWAu9lzj3>E2Lolk^xyd64$8 zUh4ej6^nKAB3pad`Vilj*j{q5j1N=6>yt9~^S#37@>P6<2Hv6K4ZKFh8+eh5H}K-r zWw=tpRgS@nk}{m%UFBO77o{GeKSKWBe^Lpu`j`WHiVsGDeXN1`$+NCa_Ykx=AAdSdbg=<$KH zPa;$xi7=9x18cZ^apzAH&BbCzkj#%vLY$^kskUw+&KC}yQP6-ye()_c<{n8ep$(>zoNg8d=-0c z!JH~2?_eTCOj#*HrmS=!B{1n%F?0(9g;XIO0Uft=f{t58K^GuHf%JkA0iCci1f8(5 zg^U1Y6)024MnEU6eu7R~W}#nzOa-D80a?}nL6$XG7!aUA1sW&}MnJPxj-Xj9U&ski zUV(Cjd!TivMltyfA0AQFz#7;UCsa+~9%G#~;>r(V#AY3H%F9N=g1C{0DqW zDJ~qC_YCir=@WapQn_^lU&1P9MFw_V^D{27yEcV^J8dtsSU);|0{Ab zL7DQpf-NcjT2Qh4Fg+uxU`j-paA@=pl&7gIg5hZ@&ea>7x)!qE=wjEzk8aZNtnT{* z-&vBrlAk`6oCyMUBtN~*a5eIix1dkm!6T0g{gjmyhC)(V3=zcAblge>CdO~j$ta{k z(+Mjbn9^+%qgwVlY3YH90j+xJp~|C!PK8^)4TEn`k>IV0_)qs z9ej*IbuoInbb7_xR7d^?C>Kb4^b@Q%DgGjiH>>X1H?em{zZZfeT?hJ8?HkM@H%DA6 zkBBf9Ae!^JDyktt_RVGNgWq!hZqBM-XGyXLqtY&E_Zld@?)d-t#@*x%ZxX z&bc#r;XhA4MMPWZ}+At&biqGC8r2F$F+~;omoL$=iur#sE2w5HRS+m4}c~M?wovD zzd-UQaRb0FwWxw^c4t~BV)%o%f-u*87#)t3Ie&YT~gLcy% zEG1~z4T3VDXy`09-Dl8Se9ArAU);anplR+;fN~$Zj5`k+bS-!C?6xVdarbow9rm%) z>^Saxt3hw0w+k|W9jp3ojX$>#@3GnS2HC#ZfvN+WXvUyf!psu$c4>aBFbffJIgz3{ znvYYC<3a`+wOqzG7|qGg*-SVOuG&rL&n+sZZ zPN*JcZ2$Ds#GW0@+|55{1&vG=igVfd+%b-5&&k@=rOe7$;7MYov8xuB@T}n#@G1y` z=!n|=ZIe0I*;8J`L#BDQoZ2}V9<+-&ZtKp7H=lz8*R7ZiForeS`xEZ*5dU{h<_n9* z3JckaUwMg2Dm`}91;yY&gyL?;n=j1nvx_JJC~_D?3k<4hZdv}-yO0eTb|9Ay3R?fq27`%Wh?q%DX#ZK-~ltWC4UJL zh2@o3mm92M!JC)Wp52t;LY|O4QZ!^|8l-S3GgerAPuN?Bqg9I zOGZjrtA?`l3wXt{+L2P!Tp8doWhzTM7D}pgRTrd3U1=*zS>P*{y45Nz1C_o_5eIq{ zq*B@iX-J?az2CEE7Ixc9Is&5mrY3fe!v_<{1vqJx^Eg8^tF_Kl7BiWtu3IuwU7~14 ztw=IcoqQ~#r<%xArWMQRNu)A?;ifWyfhD|x+UD4D`wQjbjB|C4<2K1LaS5Ln&z0%M zk(j*`9HpVzNKO>mh&n$+c_s+Ac5Gjd)9#WA#Zp_0ev_i#((mH*doFcmAF&w0*hf?gE#4qm{4-0T*yX}#}7mVGa>%wFWX??tKS?aPApB8$?C zEa;uVV#T4{s5o?s7{q3ua8c;S@;Hq-r8xaX(8lTtESvB0g^JTlNXjciv0c@w(gipf zHKDe&;1@@J6tx0c9cV4#eiPb7hf3t31Lu9M?!moG&2;=Q-AwN|Oeg4_6Z9@XaM0I~ zb_r>!Xx|MjPW)QC2Cer%ey>CCqxXZW3j6?M9P%}=MbZeL9|!$<1zX^}U$6Ke6gc$P zD|j$?QL0hMi8S#`fmXzDzXhw%#K-up^WHb^l`L4fr}q`EAE0{6Et}iEhdi+JxW~ z^!YH?1`5u~`(BkkP2W*Re=mI@jQ*03~JS&wAmT}O<8RKOub>eiqxvpWDfV)X!Y#OgJB7}Tpl zJ!WqJbkynt=&03i_A#hmgZj*V9~3!-tlS(p=cpY1igKu*!^LU3kG_O+P>*JeqyO5canEAGTu`B6xH;$ECJ zPeba*SP19QOg!-lwZ^ISRqBmX6aEt7Rq9t_@_!+c4!-+pf-k}!!gcDn9A4py9ObBE zX9f?g!Gi1`S^!zU>| za)ypw&;47i2rJBp)+9Knm1?^cl`6!uG)kCJU$xVUNmZ<(LX<13T~=JG5a8M>?yL4% z38_j{R0w-zHDx8G3K6fZlD_JIm6EDdMTI<|tPWW%QiT+tty+B55zCM&qoOi=)ln-g zRmch2DlJuO!$ayewU9cbn#B-3Mvub_Q87SIKx(3t*g#LhGtIO?r055bB6KDF69K^? zbsa+BN04H8M|^^wf)uCQ@%v+3353Zl^faU-JxlZS45SqO9sg4J38WVKH*Kb$LNY`z z_0i8DrFoM#Jx0A}(GkHW7Xlj_-(Jt+@zr({cl1!y2l$xC@-7XF1z=TVZLbE$mxHVL z5QR2L)R|PR!gbmZ|MpW81U~pD=NsYw3Wlq-dFmYc>eE+3Xp)w5lT>*cfHak{B2|^G zF-{Yv8xdAHR|VCyoIK^eg82%6+6${igS_Si;#u0E@)hzHf5;#jxfbv#qCMd3>1y99ICz`jE21UN9Z|e@x1(?(}sck9PM9V7%!CQ am-H(%5@`Gy*B|LmXr&Qm!AIk`Q0instqER1v8~19W#kkd9%K?D}vW_=pv}BzuMf` z5|QgVhLB}Y^Y!)GUe#ilem6LQ1_L)$WOWSV7Rfc-z_%HO3&q+oKjEg!{X=v6;Mi_N zWeuaatzt|^4tFTwZ$Q;=dzATvyKQ^>J;PF=k6m$jt5xm`SW)QshvohZLLk{ z7?LZF??kH%BmGi6=0>ref}&B=6Dg!q(8cQm9S<=h`j5+KQKV4(j|ot&OM+Ypn$-MI z&GGH6ws&BMyZpeV$nsx`C=MPjgHa0_+^zG_5kfsR+2B zLMv#84g0ktCXl(@}))N9yd{_!-7O(d)mIR6G`4#7gM|7O9gbbU%$LrK~1ajZ%u1Ovv;E zMw8#b_N5LaYhUg_wf2<`%}~^PwZ9aWQ0kF)dgO(##5E?RGu%Ig6gyLL?5MFbt;CLY hj{GSyqVle$5%XnaXwEsRc!nBG^vYbuGr}eC{1;Ox`ojPK literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/repository/BannerRepository.class b/backend/target/classes/com/maternalmall/repository/BannerRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..b3b8e20d7ac9b31c916011289ef5a05a60641779 GIT binary patch literal 476 zcmbV}OG?B*5QeMAN1XA240r%{3Sw_`6%`EVVrU#NUZ5uF2_5?pDjg+wvsrim4<)u4 z(cmlu7X?*C@&9~(y}d790pJq$A{a0@$(-Q^y(Ej;pw^sZ}wLC#}`% L*=@s4x7qywIi8l! literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/repository/CartItemRepository.class b/backend/target/classes/com/maternalmall/repository/CartItemRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..aa8438542010dcb7fec122d849e0cd53a329ba75 GIT binary patch literal 1097 zcmbVLO-~y!5FKx!A%T`Oe5BB-hxP&&e5lHSKqWvzO0@_C6^Gu2wX=1)Yp=ZCq8#{R z9QXnKQB`L*QL?b8i8xr6XYBW8p5OfWdv^~22k@>29)q30#5_hPt;Q(EDB@NoCRNVZ z0Y66Tx=zLy<4_Ie7&QC%8+n8}P#$cS1|avJ)<rtxi6{+QM ox=HCb(5j|a=t_gatF*5X1A}#FkFJdf9$lg2kn$~T6x5sl0QFBwuK)l5 literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/repository/FavoriteRepository.class b/backend/target/classes/com/maternalmall/repository/FavoriteRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..d0c0d5e9561066cca4260758f13b3816758fcf9b GIT binary patch literal 1019 zcmbVL%TB{E5ZtstX?edvLLA@%7kof)pnwEOl|VrtKpeO&u^U_xJ93<&9{4d1d;lMX z*rccp6w!)VjL~9Mud6R^9Z|y#8#Jz zpR`Vs*y>Q2Gw#6prrVWpweqW5?LuN@z+4&ID&4yf_EGaIOvUK^u>%z^M^e6#zCxQA z;Xth~;ep#@1lJ)-)iI%ZM53!dAA79$mC-!$Z2IuM9-Y4$xs0sTWIOSeqb|w)zeomb zV2z7_+{*j?y@ieb>UR#TTqnxPP~6E_J_O=eDQz+BhW^5iJ7^LvG?(=j1%@2h|MiwN zsABX|W`-RRcH1py_Pyk6Zb!2ED_!3>4&Y)=DvM?si^`2oj3;U`ps+3Vj?H!&XPOZQ zinNAl{)#k5B^ZSQl<71~6ts=eD!@2F6EOJ&l?xd()q$r8o`Kn3c#b})P4k5%zGsMTqX)_WrPFK zuY_0Ow>-GcQL3+51UIBTYCTVl=2hHS_KR-4>A7zM3c`Pd^LJA{#U`)XI!7L5UY5tn-G!7jMq({4aaH0{*@>=Sg*hYmZ4;I?Q4C#okJ6g!drKa*Gsm3~S zq{g300Gk4a89t%R(WY|rkm&t{fV1XqHj#j z865pM&a*mco}GXVUS0kk9iDFh+t7g?S6z1EZKoDsm!&~l+FMI3H<$KdxVn7s0as}S9C?)l!{UtR&=1h!mo7#xQ>cS#})({_%C#Yh6c{dcHb)nASNSm1FnUN~)8%)KcHupS2i+q@2Rp27S z86ksaU#h4(3M|?pXRy2P&*%eN(s>7yqr%FB`!cs4dFTl{(9so^Qq%~&tmjjTMj&HF zY(_8$enHpvzvU6>6s4M%g?CEo+v16Ah~2;b0UTfI}fBi9{lS z1fm$(^NRPG_x|zv1^{O;jUi&dGiSKLAiYIxP;2gG=_(bRU-7vY($9i4cVAcx>kOs^ zJ|Wj=b3VT>WENuBU~uAm&a2WZn=d^Y`Rx277Z{KiB@St?OY8|5OqR-ui&ZBk7#tj? zJ%8$Ko*Z>R9hBy&szO2*Ze)0H;uaeva$hlsU2B&Z#tW5O47HaGu6|`ZO~2hhID^Xe z`IB>!d>Pp4VkhapBL78jpw*tBF|f68wa?_G(v)lehjNaayHOJi)@VjlGo!X**n|ki Vw2g>^o^6^4c4)fa1a@0w?-NpKgu|z z1nNh`aM(0Euk+s9dGr49`UU`}uxCTbfuI0s3}7%7BU!UjQF$CkIYAdY!qXW^CreQA+ZgF^v}l^ zX<}5w^|f!oe%qZ(xJm}jL0(9W2$?Gq<4|9ho1yY=F%u*Ai3N49KvL_AK%z-Cx8QOi zVaNS3246)eGn-|-uTt&tD-lvYt?zqwl;uxLWkH!vh1Ru9i)X_sl%Php j3K7s}jZO&~1g#gLjW5t9H0NDgGiaNNdH)XVPIy|M-+}M? literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/repository/ProductRepository.class b/backend/target/classes/com/maternalmall/repository/ProductRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..916fc3f561dfbae873116d43b47e0ae8e26dca37 GIT binary patch literal 733 zcmbVKOHRWu5FMA6(DExEaR9^u7T^`D0#c~}6{S#7E-=Jynv&R&;}limWGpxUheAvm z(MBp#D;AdR8T-9AZ|3X!;}ZZbVc&rggHumOJVGmt!Z5-x(TxEQD869c70V>-T3=WInuKrtR zEh_xDS@>f)G4BP7@)kARf3;^ZU4dy*V&+Ayr&Hs}+aM&159WsGEbAXl41*O~6&l78 mO_c*{P=YGmDuh7WdX|6pBQhp9A|k}ymk|rJ3)=`TqzZAGC@#XGA~Bmt^@b_Y1dkF@D<4+YVgVhHXw z@?H9CCQum@F6IV!*S2CV^fpVijG3}Zt3e@Xv3Daa?M%ChTKGoLMN{Uxn0LOWXtm?j zxc!X4l{qnjj{=;J!1MoQ|Hj4(g=UkmMpOqfQvti@kukTqx*}(Gc4i+Cl*}ocO)Ht5 wabX7>sFjstrJsuiLAKTsVShQnsnN^oE=J|(kG>eXZ0fcGRRR910 literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/repository/UserRepository.class b/backend/target/classes/com/maternalmall/repository/UserRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..077a313cee7f3e016edc9db68ad9da59d4895381 GIT binary patch literal 583 zcmbVJK~BRk5L^dBS_-AWCkh9A;uPS(0U;DrMBo8S;s&RQ9XSptU*o_7cobsYNNo@Y z5C>~z&+g24_T%&I9RSYZAOMfSaiU9Jq7_DAQDRYWBPv}>t4+%vYhmtJ*8nydOmcid zUZBeO>?s$C4PcAGsWus}D(KjIvqu045rg) zEf8Br7#?*`jgDif9RKn57B@6oL;rET5xGZTs p3^wTaw04hH7{CBL7}CwBaVjGRzz#vXeQ3Of2=1EpV6tr9{{ou^u3P{B literal 0 HcmV?d00001 diff --git a/backend/target/classes/com/maternalmall/service/AuthService.class b/backend/target/classes/com/maternalmall/service/AuthService.class new file mode 100644 index 0000000000000000000000000000000000000000..718b8cf4b676f1f0e1179f6f2550dce0b90aba96 GIT binary patch literal 4628 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 + + + + + 萌贝母婴商城 + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..01bc18c --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,1684 @@ +{ + "name": "maternal-mall-frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "maternal-mall-frontend", + "version": "1.0.0", + "dependencies": { + "@arco-design/web-vue": "^2.57.0", + "axios": "^1.7.9", + "echarts": "^6.0.0", + "pinia": "^2.3.1", + "vue": "^3.5.13", + "vue-echarts": "^8.0.1", + "vue-router": "^4.5.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.1", + "vite": "^6.0.7" + } + }, + "node_modules/@arco-design/color": { + "version": "0.4.0", + "license": "MIT", + "dependencies": { + "color": "^3.1.3" + } + }, + "node_modules/@arco-design/web-vue": { + "version": "2.57.0", + "license": "MIT", + "dependencies": { + "@arco-design/color": "^0.4.0", + "b-tween": "^0.3.3", + "b-validate": "^1.5.3", + "compute-scroll-into-view": "^1.0.20", + "dayjs": "^1.11.13", + "number-precision": "^1.6.0", + "resize-observer-polyfill": "^1.5.1", + "scroll-into-view-if-needed": "^2.2.31", + "vue": "^3.1.0" + }, + "peerDependencies": { + "vue": "^3.1.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "license": "MIT" + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.28", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@vue/shared": "3.5.28", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.28", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.28", + "@vue/shared": "3.5.28" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.28", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@vue/compiler-core": "3.5.28", + "@vue/compiler-dom": "3.5.28", + "@vue/compiler-ssr": "3.5.28", + "@vue/shared": "3.5.28", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.28", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.28", + "@vue/shared": "3.5.28" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.28", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.28" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.28", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.28", + "@vue/shared": "3.5.28" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.28", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.28", + "@vue/runtime-core": "3.5.28", + "@vue/shared": "3.5.28", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.28", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.28", + "@vue/shared": "3.5.28" + }, + "peerDependencies": { + "vue": "3.5.28" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.28", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.5", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/b-tween": { + "version": "0.3.3", + "license": "MIT" + }, + "node_modules/b-validate": { + "version": "1.5.3", + "license": "MIT" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/color": { + "version": "3.2.1", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.20", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.19", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/echarts": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz", + "integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "6.0.0" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/number-precision": { + "version": "1.6.0", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinia": { + "version": "2.3.1", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.57.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup/node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/scroll-into-view-if-needed": { + "version": "2.2.31", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^1.0.20" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, + "node_modules/vite": { + "version": "6.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.28", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.28", + "@vue/compiler-sfc": "3.5.28", + "@vue/runtime-dom": "3.5.28", + "@vue/server-renderer": "3.5.28", + "@vue/shared": "3.5.28" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-demi": { + "version": "0.14.10", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-echarts": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-8.0.1.tgz", + "integrity": "sha512-23rJTFLu1OUEGRWjJGmdGt8fP+8+ja1gVgzMYPIPaHWpXegcO1viIAaeu2H4QHESlVeHzUAHIxKXGrwjsyXAaA==", + "license": "MIT", + "peerDependencies": { + "echarts": "^6.0.0", + "vue": "^3.3.0" + } + }, + "node_modules/vue-router": { + "version": "4.6.4", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/zrender": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz", + "integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..fe9bdbe --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,24 @@ +{ + "name": "maternal-mall-frontend", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@arco-design/web-vue": "^2.57.0", + "axios": "^1.7.9", + "echarts": "^6.0.0", + "pinia": "^2.3.1", + "vue": "^3.5.13", + "vue-echarts": "^8.0.1", + "vue-router": "^4.5.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.1", + "vite": "^6.0.7" + } +} diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..98240ae --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,3 @@ + diff --git a/frontend/src/api/http.js b/frontend/src/api/http.js new file mode 100644 index 0000000..732f576 --- /dev/null +++ b/frontend/src/api/http.js @@ -0,0 +1,24 @@ +import axios from 'axios' + +const http = axios.create({ + baseURL: 'http://localhost:8080', + timeout: 10000 +}) + +http.interceptors.request.use((config) => { + const token = localStorage.getItem('token') + if (token) { + config.headers['X-Token'] = token + } + return config +}) + +http.interceptors.response.use((resp) => { + const data = resp.data + if (data.code !== 0) { + return Promise.reject(new Error(data.message || '请求失败')) + } + return data.data +}) + +export default http diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js new file mode 100644 index 0000000..cc692ea --- /dev/null +++ b/frontend/src/api/index.js @@ -0,0 +1,65 @@ +import http from './http' + +export const api = { + register: (payload) => http.post('/api/auth/register', payload), + login: (payload) => http.post('/api/auth/login', payload), + me: () => http.get('/api/auth/me'), + updateMe: (payload) => http.put('/api/auth/me', payload), + + banners: () => http.get('/api/public/banners'), + products: (keyword = '') => http.get('/api/public/products', { params: { keyword } }), + + customerCart: () => http.get('/api/customer/cart'), + customerCartViews: () => http.get('/api/customer/cart/views'), + addCart: (payload) => http.post('/api/customer/cart', payload), + delCart: (productId) => http.delete(`/api/customer/cart/${productId}`), + checkout: (payload) => http.post('/api/customer/orders/checkout', payload), + customerBuyNow: (payload) => http.post('/api/customer/orders/buy-now', payload), + customerOrders: () => http.get('/api/customer/orders'), + refundOrder: (id, payload) => http.put(`/api/customer/orders/${id}/refund`, payload), + updateOrderAddress: (id, payload) => http.put(`/api/customer/orders/${id}/address`, payload), + deleteOrder: (id) => http.delete(`/api/customer/orders/${id}`), + orderLogistics: (id) => http.get(`/api/customer/orders/${id}/logistics`), + customerFavorites: () => http.get('/api/customer/favorites'), + customerFavoriteViews: () => http.get('/api/customer/favorites/views'), + addFavorite: (payload) => http.post('/api/customer/favorites', payload), + deleteFavorite: (productId) => http.delete(`/api/customer/favorites/${productId}`), + addReview: (payload) => http.post('/api/customer/reviews', payload), + orderItems: (orderId) => http.get(`/api/customer/orders/${orderId}/items`), + applyMerchant: (payload) => http.post('/api/customer/merchant-applications', payload), + + merchantOverview: () => http.get('/api/merchant/overview'), + merchantProducts: () => http.get('/api/merchant/products'), + saveMerchantProduct: (payload) => http.post('/api/merchant/products', payload), + deleteMerchantProduct: (id) => http.delete(`/api/merchant/products/${id}`), + merchantOrders: () => http.get('/api/merchant/orders'), + shipOrder: (id, payload) => http.put(`/api/merchant/orders/${id}/ship`, payload), + merchantRefund: (id, payload) => http.put(`/api/merchant/orders/${id}/refund`, payload), + merchantReviews: () => http.get('/api/merchant/reviews'), + merchantLogistics: () => http.get('/api/merchant/logistics'), + merchantInventory: () => http.get('/api/merchant/inventory'), + deleteMerchantInventory: (id) => http.delete(`/api/merchant/inventory/${id}`), + + adminOverview: () => http.get('/api/admin/overview'), + adminUsers: () => http.get('/api/admin/users'), + adminSaveUser: (payload) => http.post('/api/admin/users', payload), + adminDeleteUser: (id) => http.delete(`/api/admin/users/${id}`), + adminOrders: () => http.get('/api/admin/orders'), + adminUpdateOrder: (id, payload) => http.put(`/api/admin/orders/${id}`, payload), + adminOrderRisks: () => http.get('/api/admin/orders/risk'), + adminAuditRefund: (id, payload) => http.put(`/api/admin/orders/${id}/refund-audit`, payload), + adminAuditShipment: (id, payload) => http.put(`/api/admin/orders/${id}/ship-audit`, payload), + adminMerchantApplications: () => http.get('/api/admin/merchant-applications'), + adminAuditMerchantApplication: (id, payload) => http.put(`/api/admin/merchant-applications/${id}`, payload), + adminBanners: () => http.get('/api/admin/banners'), + adminSaveBanner: (payload) => http.post('/api/admin/banners', payload), + adminDeleteBanner: (id) => http.delete(`/api/admin/banners/${id}`), + adminProducts: () => http.get('/api/admin/products'), + adminProductViews: () => http.get('/api/admin/products/views'), + adminSaveProduct: (payload) => http.post('/api/admin/products', payload), + adminApproveProduct: (id, payload) => http.put(`/api/admin/products/${id}/approve`, payload), + adminDeleteProduct: (id) => http.delete(`/api/admin/products/${id}`), + adminReviews: () => http.get('/api/admin/reviews'), + adminLogistics: () => http.get('/api/admin/logistics'), + adminInventory: () => http.get('/api/admin/inventory') +} diff --git a/frontend/src/main.js b/frontend/src/main.js new file mode 100644 index 0000000..0355f4e --- /dev/null +++ b/frontend/src/main.js @@ -0,0 +1,9 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import ArcoVue from '@arco-design/web-vue' +import '@arco-design/web-vue/dist/arco.css' +import App from './App.vue' +import router from './router' +import './style.css' + +createApp(App).use(createPinia()).use(router).use(ArcoVue).mount('#app') diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js new file mode 100644 index 0000000..7400f4d --- /dev/null +++ b/frontend/src/router/index.js @@ -0,0 +1,29 @@ +import { createRouter, createWebHistory } from 'vue-router' +import LoginView from '../views/LoginView.vue' +import HomeView from '../views/HomeView.vue' +import CartView from '../views/CartView.vue' +import OrdersView from '../views/OrdersView.vue' +import FavoritesView from '../views/FavoritesView.vue' +import ProfileView from '../views/ProfileView.vue' +import AdminView from '../views/AdminView.vue' +import MerchantView from '../views/MerchantView.vue' + +const routes = [ + { path: '/', redirect: '/products' }, + { path: '/products', component: HomeView }, + { path: '/cart', component: CartView }, + { path: '/orders', component: OrdersView }, + { path: '/favorites', component: FavoritesView }, + { path: '/profile', component: ProfileView }, + { path: '/login', component: LoginView }, + { path: '/admin', component: AdminView }, + { path: '/merchant', component: MerchantView }, + { path: '/customer', redirect: '/products' } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +export default router diff --git a/frontend/src/stores/user.js b/frontend/src/stores/user.js new file mode 100644 index 0000000..2bedcbc --- /dev/null +++ b/frontend/src/stores/user.js @@ -0,0 +1,21 @@ +import { defineStore } from 'pinia' +import { api } from '../api' + +export const useUserStore = defineStore('user', { + state: () => ({ + profile: null + }), + getters: { + role: (s) => s.profile?.role, + loggedIn: (s) => !!s.profile + }, + actions: { + async fetchMe() { + this.profile = await api.me() + }, + async logout() { + localStorage.removeItem('token') + this.profile = null + } + } +}) diff --git a/frontend/src/style.css b/frontend/src/style.css new file mode 100644 index 0000000..992e5ef --- /dev/null +++ b/frontend/src/style.css @@ -0,0 +1,13 @@ +:root { + --bg-main: linear-gradient(135deg, #fff8ed 0%, #f2f7ff 100%); +} +body { + margin: 0; + font-family: "PingFang SC", "Helvetica Neue", sans-serif; + background: var(--bg-main); +} +.container { + max-width: 1200px; + margin: 0 auto; + padding: 16px; +} diff --git a/frontend/src/views/AdminView.vue b/frontend/src/views/AdminView.vue new file mode 100644 index 0000000..24de449 --- /dev/null +++ b/frontend/src/views/AdminView.vue @@ -0,0 +1,644 @@ + + + diff --git a/frontend/src/views/CartView.vue b/frontend/src/views/CartView.vue new file mode 100644 index 0000000..b2a04cf --- /dev/null +++ b/frontend/src/views/CartView.vue @@ -0,0 +1,86 @@ + + + diff --git a/frontend/src/views/CustomerView.vue b/frontend/src/views/CustomerView.vue new file mode 100644 index 0000000..b062b6c --- /dev/null +++ b/frontend/src/views/CustomerView.vue @@ -0,0 +1,173 @@ + + + diff --git a/frontend/src/views/FavoritesView.vue b/frontend/src/views/FavoritesView.vue new file mode 100644 index 0000000..ca6fd8c --- /dev/null +++ b/frontend/src/views/FavoritesView.vue @@ -0,0 +1,86 @@ + + + diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue new file mode 100644 index 0000000..cb7605d --- /dev/null +++ b/frontend/src/views/HomeView.vue @@ -0,0 +1,132 @@ + + + diff --git a/frontend/src/views/LoginView.vue b/frontend/src/views/LoginView.vue new file mode 100644 index 0000000..02bf452 --- /dev/null +++ b/frontend/src/views/LoginView.vue @@ -0,0 +1,57 @@ + + + diff --git a/frontend/src/views/MerchantView.vue b/frontend/src/views/MerchantView.vue new file mode 100644 index 0000000..b0f5339 --- /dev/null +++ b/frontend/src/views/MerchantView.vue @@ -0,0 +1,355 @@ + + + diff --git a/frontend/src/views/OrdersView.vue b/frontend/src/views/OrdersView.vue new file mode 100644 index 0000000..5c8cb10 --- /dev/null +++ b/frontend/src/views/OrdersView.vue @@ -0,0 +1,154 @@ + + + diff --git a/frontend/src/views/ProfileView.vue b/frontend/src/views/ProfileView.vue new file mode 100644 index 0000000..c396910 --- /dev/null +++ b/frontend/src/views/ProfileView.vue @@ -0,0 +1,75 @@ + + + diff --git a/frontend/vite.config.js b/frontend/vite.config.js new file mode 100644 index 0000000..8084277 --- /dev/null +++ b/frontend/vite.config.js @@ -0,0 +1,9 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + server: { + port: 5173 + } +}) diff --git a/sql/init.sql b/sql/init.sql new file mode 100644 index 0000000..12d32a2 --- /dev/null +++ b/sql/init.sql @@ -0,0 +1,144 @@ +CREATE DATABASE IF NOT EXISTS maternal_mall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +USE maternal_mall; + +CREATE TABLE IF NOT EXISTS users ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + username VARCHAR(50) UNIQUE NOT NULL, + password VARCHAR(100) NOT NULL, + role VARCHAR(20) NOT NULL, + nickname VARCHAR(50), + phone VARCHAR(20), + address VARCHAR(255), + token VARCHAR(100), + enabled BIT NOT NULL DEFAULT b'1', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS product ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(120) NOT NULL, + category VARCHAR(50), + description VARCHAR(1000), + price DECIMAL(10,2) NOT NULL, + stock INT NOT NULL, + image_url VARCHAR(255), + merchant_id BIGINT NOT NULL, + approved BIT NOT NULL DEFAULT b'0', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS cart_item ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + customer_id BIGINT NOT NULL, + product_id BIGINT NOT NULL, + quantity INT NOT NULL, + UNIQUE KEY uk_customer_product(customer_id, product_id) +); + +CREATE TABLE IF NOT EXISTS favorite ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + customer_id BIGINT NOT NULL, + product_id BIGINT NOT NULL, + UNIQUE KEY uk_f_customer_product(customer_id, product_id) +); + +CREATE TABLE IF NOT EXISTS orders ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + order_no VARCHAR(50) UNIQUE NOT NULL, + customer_id BIGINT NOT NULL, + merchant_id BIGINT NOT NULL, + total_amount DECIMAL(10,2) NOT NULL, + status VARCHAR(30) NOT NULL, + address VARCHAR(255), + logistics_info VARCHAR(255), + refund_reason VARCHAR(255), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS order_item ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + order_id BIGINT NOT NULL, + product_id BIGINT NOT NULL, + product_name VARCHAR(120) NOT NULL, + quantity INT NOT NULL, + unit_price DECIMAL(10,2) NOT NULL +); + +CREATE TABLE IF NOT EXISTS review ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + order_id BIGINT NOT NULL, + product_id BIGINT NOT NULL, + customer_id BIGINT NOT NULL, + rating INT NOT NULL, + content VARCHAR(1000), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS logistics_record ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + order_id BIGINT NOT NULL, + merchant_id BIGINT NOT NULL, + status VARCHAR(255) NOT NULL, + note VARCHAR(255), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS inventory_record ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + product_id BIGINT NOT NULL, + merchant_id BIGINT NOT NULL, + change_qty INT NOT NULL, + note VARCHAR(255), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS merchant_application ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + user_id BIGINT NOT NULL, + qualification VARCHAR(255), + status VARCHAR(30) NOT NULL, + remark VARCHAR(255), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS banner ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + image_url VARCHAR(255) NOT NULL, + link_url VARCHAR(255), + sort_no INT NOT NULL DEFAULT 0, + enabled BIT NOT NULL DEFAULT b'1' +); + +INSERT INTO users(username,password,role,nickname,enabled) +SELECT 'admin','123456','ADMIN','平台管理员',b'1' FROM dual +WHERE NOT EXISTS (SELECT 1 FROM users WHERE username='admin'); + +INSERT INTO users(username,password,role,nickname,enabled) +SELECT 'merchant1','123456','MERCHANT','示例商家',b'1' FROM dual +WHERE NOT EXISTS (SELECT 1 FROM users WHERE username='merchant1'); + +INSERT INTO users(username,password,role,nickname,enabled) +SELECT 'customer1','123456','CUSTOMER','示例顾客',b'1' FROM dual +WHERE NOT EXISTS (SELECT 1 FROM users WHERE username='customer1'); + +INSERT INTO product(name,category,description,price,stock,image_url,merchant_id,approved) +SELECT '婴儿纸尿裤L码','尿裤湿巾','高吸收防漏,适合10-14kg','89.00',200,'https://picsum.photos/400/260?diaper', + (SELECT id FROM users WHERE username='merchant1' LIMIT 1), b'1' +FROM dual WHERE NOT EXISTS (SELECT 1 FROM product WHERE name='婴儿纸尿裤L码'); + +INSERT INTO product(name,category,description,price,stock,image_url,merchant_id,approved) +SELECT '孕妇营养奶粉','奶粉辅食','富含叶酸和DHA','168.00',80,'https://picsum.photos/400/260?milk', + (SELECT id FROM users WHERE username='merchant1' LIMIT 1), b'1' +FROM dual WHERE NOT EXISTS (SELECT 1 FROM product WHERE name='孕妇营养奶粉'); + +INSERT INTO banner(image_url,link_url,sort_no,enabled) +SELECT 'https://picsum.photos/1200/220?maternity1','#',1,b'1' FROM dual +WHERE NOT EXISTS (SELECT 1 FROM banner WHERE sort_no=1); + +INSERT INTO banner(image_url,link_url,sort_no,enabled) +SELECT 'https://picsum.photos/1200/220?maternity2','#',2,b'1' FROM dual +WHERE NOT EXISTS (SELECT 1 FROM banner WHERE sort_no=2); diff --git a/崔梦雪-开题报告修改版.md b/崔梦雪-开题报告修改版.md new file mode 100644 index 0000000..88fc734 --- /dev/null +++ b/崔梦雪-开题报告修改版.md @@ -0,0 +1,237 @@ +# 大连科技学院 + +# 毕业设计(论文)开题报告 + +学 院 信息科学与技术学院 + +专业班级 网络工程(专升本)24-1 + +学生姓名 崔梦雪 + +学生学号 2406490119 + +指导教师 郭永伟 + +导师职称 副教授 + +# 1 选题的意义和研究现状 + +# 1.1 选题的意义 + +在数字化浪潮席卷全球的当下,互联网技术与传统行业的深度融合成为经济发展的新引擎,母婴用品消费市场也迎来了消费升级与渠道变革的双重机遇。随着居民生活水平的提高和育儿观念的精细化,消费者对母婴用品的品质、安全性和购买便捷性提出了更高要求,而传统母婴用品销售模式依赖线下门店和多层级经销商,存在流通环节繁琐、信息不对称等问题,已难以满足市场需求。 + +本课题旨在开发萌贝母婴用品销售平台,通过整合先进技术与创新运营模式,破解传统行业痛点,实现多重核心目标,一是打破地域限制与渠道壁垒,缩短母婴用品从生产端到消费端的流通链条,显著降低中间运营成本,提升行业整体运转效率。二是为顾客提供丰富多元的产品选择、简洁高效的购物流程。三是为合作商户搭建低成本、高覆盖的销售渠道,配套数据化运营工具与精准营销支持,助力商户快速适配市场变化。四是通过前后端分离架构等技术的落地应用,为母婴行业数字化转型提供可复制的实践方案,推动行业技术标准升级。 + +# 1.2 本课题所涉及问题在国内外设计或研究的现状 + +国内母婴电商市场呈现多元化发展态势,专属母婴销售平台与综合平台母婴专区协同发力。天猫母婴作为综合平台旗下的核心母婴销售板块,凭借阿里体系的庞大用户基础和成熟物流网络,成为覆盖全品类母婴产品的主流渠道。抖音母婴直播则依托短视频场景化展示优势,通过达人实测、育儿知识分享等互动形式,带动母婴用品销量快速增长。国内相关研究多集中在平台架构搭建、用户消费行为分析和物流配送优化等方面,但市场中仍存在明显短板,部分平台功能同质化严重,农村地区物流覆盖不全、售后服务响应滞后等问题也影响着用户体验。 + +国外母婴电商起步早,形成了一批专注母婴领域的成熟销售平台。美国的 BuyBuyBaby 专注母婴产品零售,以丰富的品类选择和细致的购物指引为核心优势,覆盖从孕期到学龄儿童的全阶段需求。日本的西松屋作为知名母婴销售平台,以精细化运营为特色,注重产品设计的实用性与安全性,物流服务贴合母婴用户应急需求,配送时效稳定。国外研究侧重供应链协同管理、消费者深层需求挖掘和服务模式创新,但整体发展中也存在适配性不足的问题。 + +国内外母婴电商研究与实践存在两大不足,一是信任体系有短板,产品质量信息透明度低且部分平台缺乏权威安全认证展示,二是供应链响应效率待提升,难以灵活应对 + +母婴产品周期性需求波动与应急采购场景。这些问题既影响用户体验和品牌信任度,也 + +制约行业可持续发展,需通过优化服务模式、强化需求适配、完善供应链管理等方式解决。 + +# 2 课题设计或研究的内容、预期目标和实施计划 + +# 2.1 要设计或研究的主要内容方案论证分析 + +# 2.1.1 需求分析 + +对系统的顾客、商家、管理人员等进行需求调研,了解他们对管理平台的功能需求和使用习惯,要求本系统有以下功能。 + +# (1)顾客 + +顾客功能如图 2.1。 + +登录注册:用户可以输入账号密码登录和注册。 + +搜索商品:顾客可以搜索并浏览各种商品。 + +购物车管理:顾客可以把商品加入到购物车与把物品从购物车移除,结算等。 + +购买商品:顾客可以购买物品。 + +收藏商品;顾客可以收藏自己喜欢的物品。 + +评价商品: 顾客可以对购买过的物品进行评价。 + +订单管理: 支持顾客对订单申请退款,查看物流信息,修改收货信息,删除订单等。 + +个人信息处理:顾客可以更改自己的个人信息。 + +![image](https://cdn-mineru.openxlab.org.cn/result/2026-02-09/91525fd6-19b8-4562-80b9-de011d5c58f0/8ea1316ad93d6f7cd5207e95d0a82ed095e26fdc032daedcbc480f80d429ef93.jpg) + + + +图 2.1 顾客功能图 + + +# (2)商家 + +商家功能如图 $2 . 2 \circ$ + +数据概览:查看订单量,销售额,热销商品排行图,品类销售占比图,通知公告等。 + +商品管理:对商品信息进行增删改查。 + +订单管理:查看订单信息,发货,查看物流,退货退款处理等。 + +评价管理:查看顾客的评价。 + +物流管理:查看所有商品的物流信息。 + +库存管理:可以查看商品出入库情况,删除记录等。 + +个人信息修改:支持商家更改自己的个人信息。 + +![image](https://cdn-mineru.openxlab.org.cn/result/2026-02-09/91525fd6-19b8-4562-80b9-de011d5c58f0/8142d559153c56786f3f2b43b38194bcfffb94791fd58b5e61d20356f0b82ea5.jpg) + + + +图2.2商家功能图 + + +# (3)管理员 + +管理员功能如图 2.3。 + +数据概览:查看订单量,销售额,热销商品排行图,品类销售占比图等。 + +订单管理:实时监控订单状态、核查修改订单信息、审核发货与跟踪物流的全流程操 + +作、排查违规订单、审核退款的异常管控。 + +审核管理:核查母婴用品基础信息和商家注册信息、核验商家资质经营能力、监管商 + +家违规行为。 + +用户管理:对顾客和商家信息进行增删改查。 + +个人信息修改:支持顾客和商家更改自己的个人信息。 + +轮播图设置:平台的活动页面轮播。 + +![image](https://cdn-mineru.openxlab.org.cn/result/2026-02-09/91525fd6-19b8-4562-80b9-de011d5c58f0/0292eaa5e2c4697169e862fde65800985883ed35bbaed8e77f63470a58c6607f.jpg) + + + +图2.3管理员功能图 + + +# 2.1.2 可行性分析 + +# (1)技术可行性 + +本平台技术方案具备充分可行性,核心技术均为线上交易系统领域成熟应用的主流选择。前端采用 Vue.js 技术,可快速构建适配多终端的响应式界面。后端基于 Spring Boot框架开发,能简化配置并集成安全管控功能,保障用户数据安全。数据存储选用MySQL,可高效管理结构化数据,配合物流接口,能快速实现核心业务流程。开发工具与技术社区资源丰富,无技术壁垒。 + +# (2)经济可行性 + +成本端依托开源技术大幅降低软件授权支出,采用模块化开发模式缩短研发周期,同时按实际业务需求灵活扩容,有效减少初期固定资产投入,整体成本可控性强。收益端路径清晰,通过商户入驻佣金,叠加母婴线上交易系统市场持续增长的行业红利,精准契 + +合目标用户消费需求,盈利空间广阔,投入产出比处于合理区间,经济价值与商业潜力显著。 + +# (3)操作可行性 + +系统贴合多角色使用习惯,操作门槛低。用户端采用主流线上交易系统交互逻辑,购物流程简洁直观。商户端聚焦产品与订单管理,功能设计贴合运营需求。管理员后台模块化布局,权限划分清晰。全平台均配备操作提示,无需专业培训即可上手,适配不同用户的使用场景。 + +# 2.2 本课题选题特色及预期的目标 + +本课题聚焦母婴线上交易平台领域,选题特色体现在三方面,一是紧扣母婴用品高安全、长周期的行业特性,二是针对性优化产品筛选、物流等模块,弥补通用线上交易平台适配不足,三是构建顾客-商家-管理员三方协同的生态化运营模式,强化服务与管控,突破传统线上交易平台重交易轻服务的局限。预期目标是打造贴合母婴用户需求的专业销售平台,强化服务与管控,突破传统线上交易平台重交易轻服务的局限。 + +# 2.3 本课题实施计划 + +
周数进度计划
第1周确定毕业设计题目,在网络上对“母婴用品销售平台”进行调研
第2周根据前期的调研情况,查阅相关资料完成开题报告撰写
第3周选择与课题相关的外文文献,完成外文翻译。进行前期资料自查,进行系统可行性分析和需求分析
第4周完成毕设前期检查。依据系统功能需求和业务流程分析,完成用例图和用例说明
第5周进行系统分析,以用例图为基础进行类图、活动图和顺序图的绘制,确保系统的一致性和可维护性
第6周完成数据库设计、界面设计,根据反馈意见进行修改
第7周系统实现,按功能模块进行编码
第8周完成毕设中期检查。系统实现,按功能模块进行编码
第9周系统测试,测试本系统各业务功能运行是否正常,验证功能需求是否都符合规范要求。完成论文主体部分
第10周按照系统测试结果修改代码完善功能,并完成论文剩余相关内容编写
第11周提交论文初稿,根据反馈意见修改论文
第12周继续修改论文,完成论文查重稿定稿。检查系统功能,为软件验收做好准备
第13周进行软件验收,参加校级论文查重,根据论文内容制作答辩PPT
第14周进行毕业设计答辩,并按照答辩组意见修改论文定稿,完成毕设资料存档
+ +# 3 主要参考文献 + + + +[1] 汤智宏.基于 SpringBoot+Vue 的线上书店设计与实践[J].电脑编程技巧与维护,2025,(09):71-73. + + + + + +[2] 杨文霞,霍甜甜.基于 Vue.js 的学生选课系统的设计与实现[J].办公自动化,2025,30(18):7-9+31. + + + + + +[3] 张磊.基于 B/S 架构的矿区生态监管系统平台设计与实现[J].河南科技,2025,52(16):17-22. + + + + + +[4] 杨玉,刘杰举.基于Spring Boot与Vue的物业管理系统设计与实现[J].鞋类工艺与设计,2025,5(14):114-116. + + + + + +[5] 项露芬,孙佳怡,李梦婷.基于Vue和Node.js的音乐门票管理系统的设计与实现[J].现代信息科技,2025,9(11):96-101. + + + + + +[6] 闫俊甫.基于 Spring Boot 和 Element UI 的中小型超市 ERP 系统的开发[J].电脑知识与技术,2025,21(16):48-50. + + + + + +[7] Gyan O J ,Raheem A T ,Ogundipe O M ,et al.Enhancing Security Practices across the Software Development Lifecycle:The Role of Artificial Intelligence[J].Asian Journal of Research in Computer Science,2025,18(10):101-114. + + + + + +[8] 徐家喜,王小正,朱杰.Java EE 框架技术与案例教程[M].南京大学出版社:202310:32. + + + + + +[9] 吕善雨.多元化信息融合的电商推荐系统设计与实现[D].北京邮电大学,2022. + + + + + +[10]郑阿奇.MySQL 数据库教程[M].人民邮电出版社:202401:465. + + + + + +[11]付世军,卢淞岩,李梦,等.基于B/S架构的智慧农业管理系统的设计与实现[J].湖北农业科学,2025,64(01):154-161. + + + + + +[12]黑马程序员.Spring Boot 企业级开发教程[M].人民邮电出版社:202407:258. + + + + + +[13]刘张榕.MySQL 数据库课程思政实现与建设策略[J].科学咨询,2025,(10):107-110. + + + + + +[14]冯赛赛,郝婷.影院管理系统的设计与实现[J].福建电脑,2025,41(05):68-72. + + + + + +[15]相景丽.MySQL数据库技术在校园信息管理中的应用研究[J].信息记录材料,2025,26(03):104-106+131. + diff --git a/需要实现的功能总结.md b/需要实现的功能总结.md new file mode 100644 index 0000000..3a8ceb6 --- /dev/null +++ b/需要实现的功能总结.md @@ -0,0 +1,44 @@ +# 需要实现的功能(按角色划分) + +## 顾客端(用户端) +- 登录/注册(账号密码) +- 商品搜索与浏览 +- 购物车管理(加入/移除/结算) +- 下单购买 +- 商品收藏 +- 商品评价(对已购买商品评价) +- 订单管理 + - 申请退款 + - 查看物流信息 + - 修改收货信息 + - 删除订单 +- 个人信息管理(修改个人资料) + +## 商家端 +- 数据概览/看板(订单量、销售额、热销排行、品类占比、通知公告) +- 商品管理(增删改查) +- 订单管理(查看订单、发货、查看物流、退货/退款处理) +- 评价管理(查看顾客评价) +- 物流管理(查看商品物流信息) +- 库存管理(查看出入库记录、删除记录等) +- 个人信息管理(修改个人资料) + +## 管理员端 +- 数据概览/看板(订单量、销售额、热销排行、品类占比) +- 订单管理与风控 + - 实时监控订单状态 + - 核查/修改订单信息 + - 审核发货与跟踪物流 + - 排查违规订单 + - 审核退款(异常管控) +- 审核管理 + - 核查母婴用品基础信息 + - 审核商家注册信息/资质与经营能力 + - 监管商家违规行为 +- 用户管理(顾客/商家信息增删改查) +- 轮播图设置(活动页面轮播) +- 个人信息管理(修改个人资料) + +## 平台通用/支撑能力(文中隐含) +- 物流信息对接与查询(订单/商品物流状态展示) +- 角色与权限划分(顾客/商家/管理员三端功能隔离)