From d16fc36264eed3da6a5c2358ed5864e747b029c9 Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Wed, 6 May 2026 23:30:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E9=99=A4=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 30 +- README.md | 45 +- .../.env.development | 0 .../.env.production | 0 .../.npmrc | 0 .../README.md | 29 +- community-fresh-group-buy-frontend/index.html | 15 + .../package-lock.json | 4 +- .../package.json | 2 +- .../postcss.config.js | 6 + .../src/App.vue | 10 +- .../src/api/flashsale.ts | 34 ++ .../src/api/modules/address.ts | 52 +++ .../src/api/modules/admin.ts | 220 ++++++++++ .../src/api/modules/cart.ts | 55 +++ .../src/api/modules/favorite.ts | 31 ++ .../src/api/modules/flashsale.ts | 164 +++++++ .../src/api/modules/groupbuying.ts | 115 +++++ .../src/api/modules/notification.ts | 45 ++ .../src/api/modules/order.ts | 145 +++++++ .../src/api/modules/product.ts | 57 +++ .../src/api/modules/return.ts | 61 +++ .../src/api/modules/review.ts | 55 +++ .../src/api/modules/user.ts | 69 +++ .../src/api/request.ts | 154 +++++++ .../src/assets/default-product.svg | 0 .../src/components/business/CountDown.vue | 16 +- .../src/components/business/FlashSaleCard.vue | 66 +-- .../components/business/GroupBuyingCard.vue | 61 ++- .../components/business/GroupMemberList.vue | 16 +- .../src/components/business/ProductCard.vue | 28 +- .../src/components/business/ReturnDialog.vue | 46 +- .../business/ReturnTrackingDialog.vue | 32 +- .../src/components/business/ReviewDialog.vue | 74 ++-- .../src/components/common/AppFooter.vue | 24 +- .../src/components/common/AppHeader.vue | 98 +++-- .../src/components/common/ImageUpload.vue | 150 +++---- .../components/common/NotificationCenter.vue | 160 +++---- .../src/components/common/SafeImage.vue | 24 +- .../src/components/common/SearchComponent.vue | 180 ++++---- .../src/composables/useWebSocket.ts | 219 ++++++++++ .../src/layouts/AdminLayout.vue | 134 +++--- .../src/layouts/MainLayout.vue | 12 +- .../src/main.ts | 8 +- .../src/pages/admin/dashboard.vue | 62 +-- .../src/pages/admin/favorites.vue | 184 ++++++++ .../src/pages/admin/flashsales.vue | 134 +++--- .../src/pages/admin/groupbuying.vue | 144 ++++--- .../src/pages/admin/monitor.vue | 80 ++-- .../src/pages/admin/orders.vue | 100 +++-- .../src/pages/admin/products.vue | 101 +++-- .../src/pages/admin/returns.vue | 334 ++++++++++++++ .../src/pages/admin/reviews.vue | 204 +++++++++ .../src/pages/admin/users.vue | 72 ++-- .../src/pages/cart/index.vue | 180 ++++---- .../src/pages/error/404.vue | 22 +- .../src/pages/flashsale/detail.vue | 89 ++-- .../src/pages/flashsale/index.vue | 134 +++--- .../src/pages/groupbuying/detail.vue | 95 ++-- .../src/pages/groupbuying/group.vue | 73 ++-- .../src/pages/groupbuying/index.vue | 46 +- .../src/pages/home/index.vue | 130 +++--- .../src/pages/order/detail.vue | 273 ++++++++---- .../src/pages/order/index.vue | 231 +++++++--- .../src/pages/product/detail.vue | 174 ++++---- .../src/pages/product/index.vue | 148 +++---- .../src/pages/user/favorites.vue | 39 +- .../src/pages/user/login.vue | 80 ++-- .../src/pages/user/notifications.vue | 63 +-- .../src/pages/user/profile.vue | 237 +++++++--- .../src/pages/user/register.vue | 136 +++--- .../src/pages/user/returns.vue | 75 ++-- .../src/pages/user/reviews.vue | 50 +-- .../src/router/guards.ts | 64 +++ .../src/router/index.ts | 229 ++++++++++ .../src/stores/cart.ts | 167 +++++++ .../src/stores/user.ts | 159 +++++++ .../src/styles/index.scss | 7 +- .../src/types/admin.ts | 165 +++++++ .../src/types/api.d.ts | 247 +++++++++++ .../src/types/flashsale.ts | 29 ++ .../src/types/product.ts | 37 ++ .../src/utils/image.ts | 66 +++ .../src/utils/normalizers.ts | 407 ++++++++++++++++++ .../tailwind.config.js | 29 ++ .../tsconfig.json | 29 +- .../tsconfig.node.json | 4 +- .../vite.config.ts | 53 +++ flash-sale-frontend/index.html | 15 - flash-sale-frontend/postcss.config.js | 6 - flash-sale-frontend/src/api/flashsale.ts | 34 -- .../src/api/modules/address.ts | 52 --- flash-sale-frontend/src/api/modules/admin.ts | 144 ------- flash-sale-frontend/src/api/modules/cart.ts | 55 --- .../src/api/modules/favorite.ts | 31 -- .../src/api/modules/flashsale.ts | 164 ------- .../src/api/modules/groupbuying.ts | 108 ----- .../src/api/modules/notification.ts | 45 -- flash-sale-frontend/src/api/modules/order.ts | 138 ------ .../src/api/modules/product.ts | 57 --- flash-sale-frontend/src/api/modules/return.ts | 52 --- flash-sale-frontend/src/api/modules/review.ts | 50 --- flash-sale-frontend/src/api/modules/user.ts | 64 --- flash-sale-frontend/src/api/request.ts | 154 ------- .../src/composables/useWebSocket.ts | 219 ---------- .../src/pages/admin/favorites.vue | 102 ----- .../src/pages/admin/returns.vue | 220 ---------- .../src/pages/admin/reviews.vue | 119 ----- flash-sale-frontend/src/router/guards.ts | 64 --- flash-sale-frontend/src/router/index.ts | 229 ---------- flash-sale-frontend/src/stores/cart.ts | 167 ------- flash-sale-frontend/src/stores/user.ts | 159 ------- flash-sale-frontend/src/types/admin.ts | 165 ------- flash-sale-frontend/src/types/api.d.ts | 247 ----------- flash-sale-frontend/src/types/flashsale.ts | 29 -- flash-sale-frontend/src/types/product.ts | 37 -- flash-sale-frontend/src/utils/image.ts | 66 --- flash-sale-frontend/src/utils/normalizers.ts | 407 ------------------ flash-sale-frontend/tailwind.config.js | 29 -- flash-sale-frontend/vite.config.ts | 53 --- pom.xml | 5 +- sql/cleanup_demo_data.sql | 2 +- sql/flash_sale_db.sql | 10 +- ...munityFreshGroupBuySystemApplication.java} | 4 +- .../config/GlobalExceptionHandler.java | 8 +- .../config/RedisInitializer.java | 10 +- .../config/RedissonConfig.java | 4 +- .../flashsalesystem/config/SwaggerConfig.java | 10 +- .../controller/AdminController.java | 10 +- .../controller/ApiController.java | 26 +- .../controller/FlashSaleController.java | 144 +++---- .../controller/PageController.java | 12 +- .../org/flashsalesystem/dto/FlashSaleDTO.java | 26 +- .../org/flashsalesystem/entity/FlashSale.java | 8 +- .../com/org/flashsalesystem/entity/Order.java | 4 +- .../repository/FlashSaleRepository.java | 40 +- .../repository/OrderRepository.java | 8 +- .../flashsalesystem/service/AdminService.java | 28 +- .../service/FlashSaleService.java | 218 +++++----- .../service/MessageListenerService.java | 26 +- .../flashsalesystem/service/OrderService.java | 4 +- .../service/RateLimitService.java | 2 +- .../flashsalesystem/service/RedisService.java | 8 +- .../flashsalesystem/util/JSPFunctions.java | 2 +- .../META-INF/flashsale-functions.tld | 2 +- src/main/resources/application.yml | 12 +- src/main/resources/lua/flashsale.lua | 2 +- .../service/FlashSaleServiceTest.java | 20 +- start-system.sh | 2 +- 149 files changed, 6691 insertions(+), 5575 deletions(-) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/.env.development (100%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/.env.production (100%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/.npmrc (100%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/README.md (90%) create mode 100644 community-fresh-group-buy-frontend/index.html rename {flash-sale-frontend => community-fresh-group-buy-frontend}/package-lock.json (99%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/package.json (96%) create mode 100644 community-fresh-group-buy-frontend/postcss.config.js rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/App.vue (66%) create mode 100644 community-fresh-group-buy-frontend/src/api/flashsale.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/address.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/admin.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/cart.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/favorite.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/flashsale.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/groupbuying.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/notification.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/order.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/product.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/return.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/review.ts create mode 100644 community-fresh-group-buy-frontend/src/api/modules/user.ts create mode 100644 community-fresh-group-buy-frontend/src/api/request.ts rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/assets/default-product.svg (100%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/business/CountDown.vue (89%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/business/FlashSaleCard.vue (71%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/business/GroupBuyingCard.vue (72%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/business/GroupMemberList.vue (72%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/business/ProductCard.vue (72%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/business/ReturnDialog.vue (67%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/business/ReturnTrackingDialog.vue (70%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/business/ReviewDialog.vue (70%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/common/AppFooter.vue (87%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/common/AppHeader.vue (79%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/common/ImageUpload.vue (77%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/common/NotificationCenter.vue (76%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/common/SafeImage.vue (76%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/components/common/SearchComponent.vue (79%) create mode 100644 community-fresh-group-buy-frontend/src/composables/useWebSocket.ts rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/layouts/AdminLayout.vue (82%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/layouts/MainLayout.vue (80%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/main.ts (81%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/admin/dashboard.vue (83%) create mode 100644 community-fresh-group-buy-frontend/src/pages/admin/favorites.vue rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/admin/flashsales.vue (81%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/admin/groupbuying.vue (76%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/admin/monitor.vue (85%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/admin/orders.vue (80%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/admin/products.vue (81%) create mode 100644 community-fresh-group-buy-frontend/src/pages/admin/returns.vue create mode 100644 community-fresh-group-buy-frontend/src/pages/admin/reviews.vue rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/admin/users.vue (82%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/cart/index.vue (72%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/error/404.vue (70%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/flashsale/detail.vue (77%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/flashsale/index.vue (71%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/groupbuying/detail.vue (75%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/groupbuying/group.vue (71%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/groupbuying/index.vue (76%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/home/index.vue (77%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/order/detail.vue (51%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/order/index.vue (57%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/product/detail.vue (78%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/product/index.vue (70%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/user/favorites.vue (73%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/user/login.vue (60%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/user/notifications.vue (68%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/user/profile.vue (56%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/user/register.vue (54%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/user/returns.vue (69%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/pages/user/reviews.vue (69%) create mode 100644 community-fresh-group-buy-frontend/src/router/guards.ts create mode 100644 community-fresh-group-buy-frontend/src/router/index.ts create mode 100644 community-fresh-group-buy-frontend/src/stores/cart.ts create mode 100644 community-fresh-group-buy-frontend/src/stores/user.ts rename {flash-sale-frontend => community-fresh-group-buy-frontend}/src/styles/index.scss (98%) create mode 100644 community-fresh-group-buy-frontend/src/types/admin.ts create mode 100644 community-fresh-group-buy-frontend/src/types/api.d.ts create mode 100644 community-fresh-group-buy-frontend/src/types/flashsale.ts create mode 100644 community-fresh-group-buy-frontend/src/types/product.ts create mode 100644 community-fresh-group-buy-frontend/src/utils/image.ts create mode 100644 community-fresh-group-buy-frontend/src/utils/normalizers.ts create mode 100644 community-fresh-group-buy-frontend/tailwind.config.js rename {flash-sale-frontend => community-fresh-group-buy-frontend}/tsconfig.json (57%) rename {flash-sale-frontend => community-fresh-group-buy-frontend}/tsconfig.node.json (81%) create mode 100644 community-fresh-group-buy-frontend/vite.config.ts delete mode 100644 flash-sale-frontend/index.html delete mode 100644 flash-sale-frontend/postcss.config.js delete mode 100644 flash-sale-frontend/src/api/flashsale.ts delete mode 100644 flash-sale-frontend/src/api/modules/address.ts delete mode 100644 flash-sale-frontend/src/api/modules/admin.ts delete mode 100644 flash-sale-frontend/src/api/modules/cart.ts delete mode 100644 flash-sale-frontend/src/api/modules/favorite.ts delete mode 100644 flash-sale-frontend/src/api/modules/flashsale.ts delete mode 100644 flash-sale-frontend/src/api/modules/groupbuying.ts delete mode 100644 flash-sale-frontend/src/api/modules/notification.ts delete mode 100644 flash-sale-frontend/src/api/modules/order.ts delete mode 100644 flash-sale-frontend/src/api/modules/product.ts delete mode 100644 flash-sale-frontend/src/api/modules/return.ts delete mode 100644 flash-sale-frontend/src/api/modules/review.ts delete mode 100644 flash-sale-frontend/src/api/modules/user.ts delete mode 100644 flash-sale-frontend/src/api/request.ts delete mode 100644 flash-sale-frontend/src/composables/useWebSocket.ts delete mode 100644 flash-sale-frontend/src/pages/admin/favorites.vue delete mode 100644 flash-sale-frontend/src/pages/admin/returns.vue delete mode 100644 flash-sale-frontend/src/pages/admin/reviews.vue delete mode 100644 flash-sale-frontend/src/router/guards.ts delete mode 100644 flash-sale-frontend/src/router/index.ts delete mode 100644 flash-sale-frontend/src/stores/cart.ts delete mode 100644 flash-sale-frontend/src/stores/user.ts delete mode 100644 flash-sale-frontend/src/types/admin.ts delete mode 100644 flash-sale-frontend/src/types/api.d.ts delete mode 100644 flash-sale-frontend/src/types/flashsale.ts delete mode 100644 flash-sale-frontend/src/types/product.ts delete mode 100644 flash-sale-frontend/src/utils/image.ts delete mode 100644 flash-sale-frontend/src/utils/normalizers.ts delete mode 100644 flash-sale-frontend/tailwind.config.js delete mode 100644 flash-sale-frontend/vite.config.ts rename src/main/java/com/org/flashsalesystem/{FlashSaleSystemApplication.java => CommunityFreshGroupBuySystemApplication.java} (69%) diff --git a/CLAUDE.md b/CLAUDE.md index 147011f..b2a5b85 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## 项目概述 -FlashSaleSystem 是一个基于 Spring Boot 2.7.6 和 Redis 集群构建的社区生鲜团购系统。系统采用分布式架构设计,通过 Redis 集群实现高并发处理,使用 Lua 脚本保证原子性操作,采用分布式锁防止超卖。 +CommunityFreshGroupBuySystem 是一个基于 Spring Boot 2.7.6 和 Redis 集群构建的社区生鲜团购系统。系统采用分布式架构设计,通过 +Redis 集群实现高并发处理,使用 Lua 脚本保证原子性操作,采用分布式锁防止超卖。 ## 核心架构 @@ -54,8 +55,8 @@ mvn spring-boot:run -Dspring.profiles.active=dev # 开发环境 mvn spring-boot:run -Dspring.profiles.active=cluster # 集群环境 # 运行JAR包 -java -jar target/FlashSaleSystem-0.0.1-SNAPSHOT.jar -java -jar target/FlashSaleSystem-0.0.1-SNAPSHOT.jar --spring.profiles.active=cluster +java -jar target/CommunityFreshGroupBuySystem-0.0.1-SNAPSHOT.jar +java -jar target/CommunityFreshGroupBuySystem-0.0.1-SNAPSHOT.jar --spring.profiles.active=cluster ``` ### 数据库初始化 @@ -84,13 +85,14 @@ mysql -u root -p flash_sale_db < src/main/resources/sql/test-data.sql - **String**: 分布式锁、会话存储、库存计数 - **Hash**: 用户信息、商品信息、购物车数据 - **List**: 订单队列、消息队列 -- **Set**: 秒杀成功用户集合 +- **Set**: 限时活动成功用户集合 - **ZSet**: 商品排行榜、热门活动 ### Key前缀规范 -- `flashsale:` - 秒杀活动数据 + +- `flashsale:` - 限时活动数据 - `flashsale_stock:` - 实时库存信息 -- `flashsale_lock:` - 秒杀分布式锁 +- `flashsale_lock:` - 限时活动分布式锁 - `flashsale_success:` - 成功用户集合 - `user:` - 用户信息缓存 - `product:` - 商品信息缓存 @@ -106,16 +108,17 @@ mysql -u root -p flash_sale_db < src/main/resources/sql/test-data.sql ## 核心业务流程 -### 秒杀流程 +### 限时活动流程 1. **库存预热**: 活动前30分钟通过 `FlashSaleService.preloadStock()` 预加载库存到Redis 2. **限流检查**: `RateLimitService.checkFlashSaleRateLimit()` 检查用户请求频率(10次/分钟) 3. **分布式锁**: `RedissonLockService.tryLock()` 获取分布式锁,防止并发超卖 4. **库存扣减**: 执行 `flashsale.lua` 脚本原子性扣减库存 5. **订单创建**: 数据库创建订单,Redis记录成功用户 -6. **消息发布**: 通过Pub/Sub发布秒杀结果通知 +6. **消息发布**: 通过Pub/Sub发布限时活动结果通知 ### 关键服务类 -- **FlashSaleService**: 秒杀核心逻辑,包含库存预热、分布式锁、Lua脚本执行 + +- **FlashSaleService**: 限时活动核心逻辑,包含库存预热、分布式锁、Lua脚本执行 - **RedisService**: Redis基础操作封装,支持各种数据类型和TTL设置 - **RedissonLockService**: Redisson分布式锁实现,支持自动续期 - **RateLimitService**: 基于Redis的滑动窗口限流 @@ -135,7 +138,7 @@ mysql -u root -p flash_sale_db < src/main/resources/sql/test-data.sql spring.redis.host: localhost # 单节点配置 spring.redis.cluster.nodes: ... # 集群配置(取消注释启用) -# 秒杀业务配置 +# 限时活动业务配置 flashsale.seckill.rate-limit.max-requests-per-minute: 10 # 限流 flashsale.seckill.max-quantity-per-user: 1 # 每人限购 flashsale.seckill.stock-preload.advance-minutes: 30 # 预热时间 @@ -143,7 +146,7 @@ flashsale.seckill.stock-preload.advance-minutes: 30 # 预热时间 # 缓存过期时间 flashsale.cache.user-expire-minutes: 30 # 用户信息 flashsale.cache.product-expire-minutes: 60 # 商品信息 -flashsale.cache.flashsale-expire-minutes: 10 # 秒杀活动 +flashsale.cache.flashsale-expire-minutes: 10 # 限时活动 ``` ## 性能优化策略 @@ -163,7 +166,8 @@ flashsale.cache.flashsale-expire-minutes: 10 # 秒杀活动 - **Prometheus监控**: http://localhost:8080/actuator/prometheus ### 日志配置 -- **日志文件**: logs/flash-sale-system.log + +- **日志文件**: logs/community-fresh-group-buy-system.log - **日志级别**: DEBUG (com.org.flashsalesystem, Redis, SQL) ## 测试策略 @@ -175,7 +179,7 @@ flashsale.cache.flashsale-expire-minutes: 10 # 秒杀活动 ### 性能测试 - 推荐工具: JMeter, Gatling -- 测试场景: 高并发秒杀、库存扣减准确性、分布式锁效果 +- 测试场景: 高并发限时活动、库存扣减准确性、分布式锁效果 ## 安全考虑 diff --git a/README.md b/README.md index a61fde6..daa55f9 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,11 @@ -# 社区生鲜团购系统 (FlashSaleSystem) - -退货 -评价 +# 社区生鲜团购系统 (CommunityFreshGroupBuySystem) 基于 Spring Boot + Redis 构建的社区生鲜团购系统 ## 📖 项目概述 本项目是一个完整的社区生鲜团购系统,采用分布式架构设计,通过 Redis -集群实现高并发处理能力。系统支持用户管理、商品管理、购物车功能和秒杀活动,具备防超卖、限流控制、分布式锁等核心技术特性。 +集群实现高并发处理能力。系统支持用户管理、商品管理、购物车、拼团活动和订单管理,具备防超卖、限流控制、分布式锁等核心技术特性。 **项目完成度**: 90% @@ -34,9 +31,9 @@ ## 🏗️ 项目结构 ``` -FlashSaleSystem/ +CommunityFreshGroupBuySystem/ ├── src/main/java/com/org/flashsalesystem/ -│ ├── FlashSaleSystemApplication.java # 启动类 +│ ├── CommunityFreshGroupBuySystemApplication.java # 启动类 │ ├── config/ # 配置类 │ │ ├── RedissonConfig.java # Redis集群配置 │ │ ├── SwaggerConfig.java # API文档配置 @@ -44,7 +41,7 @@ FlashSaleSystem/ │ ├── controller/ # 控制器层 │ │ ├── AdminController.java # 管理员控制器 │ │ ├── CartController.java # 购物车控制器 -│ │ ├── FlashSaleController.java # 秒杀控制器 +│ │ ├── FlashSaleController.java # 限时活动控制器 │ │ ├── OrderController.java # 订单控制器 │ │ ├── PageController.java # 页面控制器 │ │ ├── ProductController.java # 商品控制器 @@ -52,17 +49,17 @@ FlashSaleSystem/ │ │ └── UserController.java # 用户控制器 │ ├── dto/ # 数据传输对象 │ │ ├── CartDTO.java # 购物车DTO -│ │ ├── FlashSaleDTO.java # 秒杀DTO +│ │ ├── FlashSaleDTO.java # 限时活动DTO │ │ ├── OrderDTO.java # 订单DTO │ │ ├── ProductDTO.java # 商品DTO │ │ └── UserDTO.java # 用户DTO │ ├── entity/ # 实体类 -│ │ ├── FlashSale.java # 秒杀实体 +│ │ ├── FlashSale.java # 限时活动实体 │ │ ├── Order.java # 订单实体 │ │ ├── Product.java # 商品实体 │ │ └── User.java # 用户实体 │ ├── repository/ # 数据访问层 -│ │ ├── FlashSaleRepository.java # 秒杀数据访问 +│ │ ├── FlashSaleRepository.java # 限时活动数据访问 │ │ ├── OrderRepository.java # 订单数据访问 │ │ ├── ProductRepository.java # 商品数据访问 │ │ └── UserRepository.java # 用户数据访问 @@ -70,7 +67,7 @@ FlashSaleSystem/ │ │ ├── AdminService.java # 管理员服务 │ │ ├── CartService.java # 购物车服务 │ │ ├── DistributedLockService.java # 分布式锁服务 -│ │ ├── FlashSaleService.java # 秒杀服务 +│ │ ├── FlashSaleService.java # 限时活动服务 │ │ ├── MessageListenerService.java # 消息监听服务 │ │ ├── OrderService.java # 订单服务 │ │ ├── ProductService.java # 商品服务 @@ -88,7 +85,7 @@ FlashSaleSystem/ │ ├── lua/ # Lua脚本 │ │ ├── cart_operation.lua # 购物车操作脚本 │ │ ├── distributed_lock.lua # 分布式锁脚本 -│ │ ├── flashsale.lua # 秒杀脚本 +│ │ ├── flashsale.lua # 活动脚本 │ │ ├── rate_limit.lua # 限流脚本 │ │ └── unlock.lua # 解锁脚本 │ ├── sql/ # SQL脚本 @@ -128,7 +125,7 @@ FlashSaleSystem/ - 批量操作支持 - 持久化策略 -### 4. 秒杀核心模块 +### 4. 限时活动核心模块 - 分布式锁防超卖 - Lua 脚本原子操作 @@ -154,7 +151,7 @@ FlashSaleSystem/ ### Lua 脚本实现 -- **秒杀脚本**: 原子性库存扣减,防止超卖 +- **活动脚本**: 原子性库存扣减,防止超卖 - **分布式锁脚本**: 原子性设置锁和过期时间 - **限流脚本**: 滑动窗口精确限流 - **购物车脚本**: 原子性购物车操作 @@ -164,7 +161,7 @@ FlashSaleSystem/ - 订单状态变更通知 - 库存变化通知 -- 秒杀结果通知 +- 限时活动结果通知 - 用户行为监听 ### 性能优化技术 @@ -174,19 +171,19 @@ FlashSaleSystem/ - 数据预热机制 - 连接池优化 -## 🚀 秒杀流程 +## 🚀 限时活动流程 1. **库存预热**: 将商品库存加载到 Redis -2. **用户请求**: 前端发起秒杀请求 +2. **用户请求**: 前端发起限时活动请求 3. **限流检查**: 检查用户请求频率 4. **分布式锁**: 获取商品锁防止并发 5. **库存扣减**: Lua 脚本原子性扣减 -6. **订单创建**: 创建秒杀订单 -7. **消息通知**: 发布秒杀结果消息 +6. **订单创建**: 创建限时订单 +7. **消息通知**: 发布限时活动结果消息 ## 🛡️ 防超卖机制 -- **分布式锁**: 串行化处理秒杀请求 +- **分布式锁**: 串行化处理限时活动请求 - **Lua 脚本**: 原子性检查和扣减库存 - **重复检查**: 防止用户重复参与 - **数据一致性**: Redis 和数据库双重保障 @@ -221,7 +218,7 @@ FlashSaleSystem/ ```bash git clone -cd FlashSaleSystem +cd CommunityFreshGroupBuySystem ``` 2. **配置数据库** @@ -275,7 +272,7 @@ mvn test -Dtest=RedisServiceTest ### 压力测试 - 使用 JMeter 或其他工具进行压力测试 -- 测试并发秒杀场景 +- 测试并发限时活动场景 - 验证系统性能指标 ## 📈 项目亮点 @@ -329,4 +326,4 @@ mvn test -Dtest=RedisServiceTest **项目完成时间**: 2025-06-28 **总体完成度**: 90% -**核心技术**: Redis集群 + Spring Boot + 分布式锁 + Lua脚本 \ No newline at end of file +**核心技术**: Redis集群 + Spring Boot + 分布式锁 + Lua脚本 diff --git a/flash-sale-frontend/.env.development b/community-fresh-group-buy-frontend/.env.development similarity index 100% rename from flash-sale-frontend/.env.development rename to community-fresh-group-buy-frontend/.env.development diff --git a/flash-sale-frontend/.env.production b/community-fresh-group-buy-frontend/.env.production similarity index 100% rename from flash-sale-frontend/.env.production rename to community-fresh-group-buy-frontend/.env.production diff --git a/flash-sale-frontend/.npmrc b/community-fresh-group-buy-frontend/.npmrc similarity index 100% rename from flash-sale-frontend/.npmrc rename to community-fresh-group-buy-frontend/.npmrc diff --git a/flash-sale-frontend/README.md b/community-fresh-group-buy-frontend/README.md similarity index 90% rename from flash-sale-frontend/README.md rename to community-fresh-group-buy-frontend/README.md index 3c2ed66..60312d6 100644 --- a/flash-sale-frontend/README.md +++ b/community-fresh-group-buy-frontend/README.md @@ -1,4 +1,4 @@ -# 社区生鲜团购系统前端 (Flash Sale Frontend) +# 社区生鲜团购系统前端 (Group Buy Frontend) 基于 Vue 3 + Vite + TypeScript 构建的现代化社区生鲜团购系统前端应用。 @@ -18,19 +18,22 @@ ## ✨ 功能特性 ### 用户端功能 -- 🏠 **首页展示**: 轮播图、秒杀活动、热门商品推荐 + +- 🏠 **首页展示**: 轮播图、限时活动、热门商品推荐 - 🔐 **用户认证**: 登录、注册、个人中心管理 -- ⚡ **秒杀抢购**: 实时倒计时、库存显示、防重复提交 +- ⚡ **限时活动抢购**: 实时倒计时、库存显示、防重复提交 - 🛍️ **商品浏览**: 分类筛选、价格排序、关键词搜索 - 🛒 **购物车**: 商品管理、批量操作、结算功能 - 📦 **订单管理**: 订单列表、详情查看、状态跟踪 ### 管理后台 + - 📊 **数据仪表盘**: 实时统计、图表展示 -- 📝 **内容管理**: 商品、秒杀、订单、用户管理 +- 📝 **内容管理**: 商品、限时活动、订单、用户管理 - 📈 **数据分析**: 销售趋势、用户行为分析 ### 高级特性 + - 🔔 **实时通知**: WebSocket消息推送、消息中心 - 🔍 **智能搜索**: 搜索历史、热门推荐、实时建议 - 📸 **图片上传**: 拖拽上传、预览下载、进度显示 @@ -40,7 +43,7 @@ ## 📁 项目结构 ``` -flash-sale-frontend/ +community-fresh-group-buy-frontend/ ├── src/ │ ├── api/ # API接口封装 │ ├── assets/ # 静态资源 @@ -65,10 +68,12 @@ flash-sale-frontend/ ## 🔧 安装使用 ### 环境要求 + - Node.js >= 16.0 - npm >= 8.0 或 yarn >= 1.22 ### 安装依赖 + ```bash npm install # 或 @@ -76,14 +81,17 @@ yarn install ``` ### 开发环境 + ```bash npm run dev # 或 yarn dev ``` + 访问 http://localhost:3000 ### 生产构建 + ```bash npm run build # 或 @@ -91,6 +99,7 @@ yarn build ``` ### 预览构建结果 + ```bash npm run preview # 或 @@ -105,6 +114,7 @@ yarn preview ## 📝 配置说明 ### 环境变量 + 修改 `.env.development` 或 `.env.production` 文件: ```env @@ -119,6 +129,7 @@ VITE_UPLOAD_URL=http://localhost:8080/upload ``` ### 代理配置 + 在 `vite.config.ts` 中配置开发环境代理: ```typescript @@ -135,6 +146,7 @@ server: { ## 🚢 部署 ### Nginx配置示例 + ```nginx server { listen 80; @@ -154,6 +166,7 @@ server { ``` ### Docker部署 + ```dockerfile FROM nginx:alpine COPY dist /usr/share/nginx/html @@ -181,9 +194,9 @@ MIT License ## 📧 联系我们 -- 邮箱: contact@flashsale.com -- 官网: https://flashsale.com +- 邮箱: contact@community-fresh-groupbuy.example +- 官网: https://community-fresh-groupbuy.example --- -Made with ❤️ by Flash Sale Team \ No newline at end of file +Made with ❤️ by Group Buy Team diff --git a/community-fresh-group-buy-frontend/index.html b/community-fresh-group-buy-frontend/index.html new file mode 100644 index 0000000..3c2eff3 --- /dev/null +++ b/community-fresh-group-buy-frontend/index.html @@ -0,0 +1,15 @@ + + + + + + + 社区生鲜团购系统 + + + + +
+ + + diff --git a/flash-sale-frontend/package-lock.json b/community-fresh-group-buy-frontend/package-lock.json similarity index 99% rename from flash-sale-frontend/package-lock.json rename to community-fresh-group-buy-frontend/package-lock.json index 4ad8e25..be6b685 100644 --- a/flash-sale-frontend/package-lock.json +++ b/community-fresh-group-buy-frontend/package-lock.json @@ -1,11 +1,11 @@ { - "name": "flash-sale-frontend", + "name": "community-fresh-group-buy-frontend", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "flash-sale-frontend", + "name": "community-fresh-group-buy-frontend", "version": "1.0.0", "dependencies": { "@element-plus/icons-vue": "^2.3.1", diff --git a/flash-sale-frontend/package.json b/community-fresh-group-buy-frontend/package.json similarity index 96% rename from flash-sale-frontend/package.json rename to community-fresh-group-buy-frontend/package.json index 598d260..16d9390 100644 --- a/flash-sale-frontend/package.json +++ b/community-fresh-group-buy-frontend/package.json @@ -1,5 +1,5 @@ { - "name": "flash-sale-frontend", + "name": "community-fresh-group-buy-frontend", "private": true, "version": "1.0.0", "type": "module", diff --git a/community-fresh-group-buy-frontend/postcss.config.js b/community-fresh-group-buy-frontend/postcss.config.js new file mode 100644 index 0000000..5aa5df0 --- /dev/null +++ b/community-fresh-group-buy-frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} \ No newline at end of file diff --git a/flash-sale-frontend/src/App.vue b/community-fresh-group-buy-frontend/src/App.vue similarity index 66% rename from flash-sale-frontend/src/App.vue rename to community-fresh-group-buy-frontend/src/App.vue index c6c4c5b..babf12f 100644 --- a/flash-sale-frontend/src/App.vue +++ b/community-fresh-group-buy-frontend/src/App.vue @@ -1,14 +1,14 @@ - - diff --git a/flash-sale-frontend/src/pages/admin/flashsales.vue b/community-fresh-group-buy-frontend/src/pages/admin/flashsales.vue similarity index 81% rename from flash-sale-frontend/src/pages/admin/flashsales.vue rename to community-fresh-group-buy-frontend/src/pages/admin/flashsales.vue index c48f136..06ad7a7 100644 --- a/flash-sale-frontend/src/pages/admin/flashsales.vue +++ b/community-fresh-group-buy-frontend/src/pages/admin/flashsales.vue @@ -7,11 +7,15 @@
- + + + 刷新 - + + + 创建限时
@@ -38,13 +42,17 @@
- + - - - - + + + + 搜索 重置 @@ -52,11 +60,12 @@
- + - + - - + + - + - + @@ -98,13 +112,13 @@
@@ -112,50 +126,51 @@ - - + + - + - +
- +

{{ currentItem.productName }}

@@ -170,7 +185,7 @@
开始时间:{{ formatTime(currentItem.startTime) }}
结束时间:{{ formatTime(currentItem.endTime) }}
- +
- - diff --git a/community-fresh-group-buy-frontend/src/pages/admin/reviews.vue b/community-fresh-group-buy-frontend/src/pages/admin/reviews.vue new file mode 100644 index 0000000..fa93acc --- /dev/null +++ b/community-fresh-group-buy-frontend/src/pages/admin/reviews.vue @@ -0,0 +1,204 @@ + + + + + diff --git a/flash-sale-frontend/src/pages/admin/users.vue b/community-fresh-group-buy-frontend/src/pages/admin/users.vue similarity index 82% rename from flash-sale-frontend/src/pages/admin/users.vue rename to community-fresh-group-buy-frontend/src/pages/admin/users.vue index 5cc30a5..283fabe 100644 --- a/flash-sale-frontend/src/pages/admin/users.vue +++ b/community-fresh-group-buy-frontend/src/pages/admin/users.vue @@ -7,11 +7,15 @@
- + + + 刷新 - + + + 导出
@@ -38,11 +42,15 @@
- + - - + + 搜索 重置 @@ -50,13 +58,16 @@
- - - - - + + + + + @@ -69,10 +80,10 @@ {{ row.isOnline ? '在线' : '离线' }} - + - + @@ -85,13 +96,13 @@
@@ -112,12 +123,12 @@
- - diff --git a/flash-sale-frontend/src/pages/admin/returns.vue b/flash-sale-frontend/src/pages/admin/returns.vue deleted file mode 100644 index 8ffce98..0000000 --- a/flash-sale-frontend/src/pages/admin/returns.vue +++ /dev/null @@ -1,220 +0,0 @@ - - - - - diff --git a/flash-sale-frontend/src/pages/admin/reviews.vue b/flash-sale-frontend/src/pages/admin/reviews.vue deleted file mode 100644 index e9961f2..0000000 --- a/flash-sale-frontend/src/pages/admin/reviews.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - - - diff --git a/flash-sale-frontend/src/router/guards.ts b/flash-sale-frontend/src/router/guards.ts deleted file mode 100644 index 92dfd92..0000000 --- a/flash-sale-frontend/src/router/guards.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { Router } from 'vue-router' -import { useUserStore } from '@/stores/user' -import { ElMessage } from 'element-plus' - -export function setupGuards(router: Router) { - // 路由前置守卫 - router.beforeEach(async (to, from, next) => { - const userStore = useUserStore() - - if (userStore.token && !userStore.user) { - await userStore.getUserInfo() - } - - // 设置页面标题 - document.title = `${to.meta.title || '社区生鲜团购系统'} - 社区生鲜团购平台` - - // 需要登录的页面 - if (to.meta.requiresAuth && !userStore.isLoggedIn) { - ElMessage.warning('请先登录') - next({ - path: '/login', - query: { redirect: to.fullPath } - }) - return - } - - // 需要管理员权限的页面 - if (to.meta.requiresAdmin && !userStore.isAdmin) { - ElMessage.error('无权访问') - next('/') - return - } - - const adminBlockedFrontPaths = [ - '/cart', - '/orders', - '/favorites', - '/reviews', - '/returns', - '/notifications', - '/addresses', - '/profile', - ] - const isAdminFrontPath = adminBlockedFrontPaths.some((path) => to.path === path || to.path.startsWith(`${path}/`)) - if (userStore.isAdmin && isAdminFrontPath) { - next('/admin') - return - } - - // 已登录用户访问登录/注册页面 - if ((to.path === '/login' || to.path === '/register') && userStore.isLoggedIn) { - next('/') - return - } - - next() - }) - - // 路由后置守卫 - router.afterEach((to, from) => { - // 页面切换后滚动到顶部 - window.scrollTo(0, 0) - }) -} diff --git a/flash-sale-frontend/src/router/index.ts b/flash-sale-frontend/src/router/index.ts deleted file mode 100644 index f1ebadb..0000000 --- a/flash-sale-frontend/src/router/index.ts +++ /dev/null @@ -1,229 +0,0 @@ -import { createRouter, createWebHistory } from 'vue-router' -import type { RouteRecordRaw } from 'vue-router' -import { setupGuards } from './guards' - -// 路由配置 -const routes: RouteRecordRaw[] = [ - { - path: '/', - component: () => import('@/layouts/MainLayout.vue'), - children: [ - { - path: '', - name: 'Home', - component: () => import('@/pages/home/index.vue'), - meta: { title: '首页' } - }, - { - path: 'flashsale', - name: 'FlashSale', - component: () => import('@/pages/flashsale/index.vue'), - meta: {title: '限时活动'} - }, - { - path: 'flashsales', - redirect: '/flashsale' - }, - { - path: 'flashsale/:id', - name: 'FlashSaleDetail', - component: () => import('@/pages/flashsale/detail.vue'), - meta: {title: '限时详情'} - }, - { - path: 'products', - name: 'Products', - component: () => import('@/pages/product/index.vue'), - meta: { title: '商品列表' } - }, - { - path: 'search', - redirect: (to) => ({ path: '/products', query: to.query }) - }, - { - path: 'category/:category', - redirect: (to) => ({ path: '/products', query: { category: String(to.params.category || '') } }) - }, - { - path: 'product/:id', - name: 'ProductDetail', - component: () => import('@/pages/product/detail.vue'), - meta: { title: '商品详情' } - }, - { - path: 'cart', - name: 'Cart', - component: () => import('@/pages/cart/index.vue'), - meta: { title: '购物车', requiresAuth: true } - }, - { - path: 'orders', - name: 'Orders', - component: () => import('@/pages/order/index.vue'), - meta: { title: '我的订单', requiresAuth: true } - }, - { - path: 'order/:id', - name: 'OrderDetail', - component: () => import('@/pages/order/detail.vue'), - meta: { title: '订单详情', requiresAuth: true } - }, - { - path: 'profile', - name: 'Profile', - component: () => import('@/pages/user/profile.vue'), - meta: { title: '个人中心', requiresAuth: true } - }, - { - path: 'favorites', - name: 'Favorites', - component: () => import('@/pages/user/favorites.vue'), - meta: { title: '我的收藏', requiresAuth: true } - }, - { - path: 'reviews', - name: 'MyReviews', - component: () => import('@/pages/user/reviews.vue'), - meta: { title: '我的评价', requiresAuth: true } - }, - { - path: 'returns', - name: 'MyReturns', - component: () => import('@/pages/user/returns.vue'), - meta: { title: '我的退货', requiresAuth: true } - }, - { - path: 'notifications', - name: 'Notifications', - component: () => import('@/pages/user/notifications.vue'), - meta: { title: '消息通知', requiresAuth: true } - }, - { - path: 'groupbuying', - name: 'GroupBuying', - component: () => import('@/pages/groupbuying/index.vue'), - meta: { title: '拼团活动' } - }, - { - path: 'groupbuying/:id', - name: 'GroupBuyingDetail', - component: () => import('@/pages/groupbuying/detail.vue'), - meta: { title: '拼团详情' } - }, - { - path: 'groupbuying/group/:id', - name: 'GroupBuyingGroupDetail', - component: () => import('@/pages/groupbuying/group.vue'), - meta: { title: '团组详情', requiresAuth: true } - }, - { - path: 'addresses', - name: 'Addresses', - component: () => import('@/pages/user/profile.vue'), - meta: { title: '地址管理', requiresAuth: true } - } - ] - }, - { - path: '/login', - name: 'Login', - component: () => import('@/pages/user/login.vue'), - meta: { title: '登录' } - }, - { - path: '/register', - name: 'Register', - component: () => import('@/pages/user/register.vue'), - meta: { title: '注册' } - }, - { - path: '/admin', - component: () => import('@/layouts/AdminLayout.vue'), - meta: { requiresAuth: true, requiresAdmin: true }, - children: [ - { - path: '', - name: 'AdminDashboard', - component: () => import('@/pages/admin/dashboard.vue'), - meta: { title: '管理后台' } - }, - { - path: 'products', - name: 'AdminProducts', - component: () => import('@/pages/admin/products.vue'), - meta: { title: '商品管理' } - }, - { - path: 'flashsales', - name: 'AdminFlashSales', - component: () => import('@/pages/admin/flashsales.vue'), - meta: {title: '限时管理'} - }, - { - path: 'groupbuying', - name: 'AdminGroupBuying', - component: () => import('@/pages/admin/groupbuying.vue'), - meta: { title: '拼团管理' } - }, - { - path: 'orders', - name: 'AdminOrders', - component: () => import('@/pages/admin/orders.vue'), - meta: { title: '订单管理' } - }, - { - path: 'users', - name: 'AdminUsers', - component: () => import('@/pages/admin/users.vue'), - meta: { title: '用户管理' } - }, - { - path: 'reviews', - name: 'AdminReviews', - component: () => import('@/pages/admin/reviews.vue'), - meta: { title: '评价管理' } - }, - { - path: 'returns', - name: 'AdminReturns', - component: () => import('@/pages/admin/returns.vue'), - meta: { title: '退货管理' } - }, - { - path: 'favorites', - name: 'AdminFavorites', - component: () => import('@/pages/admin/favorites.vue'), - meta: { title: '收藏管理' } - }, - { - path: 'monitor', - name: 'AdminMonitor', - component: () => import('@/pages/admin/monitor.vue'), - meta: { title: '系统监控' } - } - ] - }, - { - path: '/:pathMatch(.*)*', - name: 'NotFound', - component: () => import('@/pages/error/404.vue'), - meta: { title: '页面未找到' } - } -] - -const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), - routes, - scrollBehavior(to, from, savedPosition) { - if (savedPosition) { - return savedPosition - } else { - return { top: 0 } - } - } -}) - -// 设置路由守卫 -setupGuards(router) - -export default router diff --git a/flash-sale-frontend/src/stores/cart.ts b/flash-sale-frontend/src/stores/cart.ts deleted file mode 100644 index 025a3f7..0000000 --- a/flash-sale-frontend/src/stores/cart.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { defineStore } from 'pinia' -import { ref, computed } from 'vue' -import type { CartItem } from '@/types/api' -import { cartApi } from '@/api/modules/cart' -import { ElMessage } from 'element-plus' - -export const useCartStore = defineStore('cart', () => { - // 状态 - const items = ref([]) - const loading = ref(false) - - // 计算属性 - const itemCount = computed(() => items.value.length) - const totalQuantity = computed(() => - items.value.reduce((sum, item) => sum + item.quantity, 0) - ) - const selectedItems = computed(() => - items.value.filter(item => item.selected) - ) - const selectedCount = computed(() => selectedItems.value.length) - const totalPrice = computed(() => - selectedItems.value.reduce((sum, item) => sum + item.price * item.quantity, 0) - ) - const isAllSelected = computed(() => - items.value.length > 0 && items.value.every(item => item.selected) - ) - - // 获取购物车数据 - const fetchCart = async () => { - loading.value = true - try { - const res = await cartApi.getCart() - if (res.success) { - items.value = res.data || [] - } - } catch (error) { - console.error('获取购物车失败:', error) - } finally { - loading.value = false - } - } - - // 添加到购物车 - const addToCart = async (productId: number, quantity = 1) => { - try { - const res = await cartApi.addToCart({ productId, quantity }) - if (res.success) { - ElMessage.success('已添加到购物车') - await fetchCart() - return true - } - return false - } catch (error) { - console.error('添加到购物车失败:', error) - return false - } - } - - // 更新数量 - const updateQuantity = async (itemId: string, quantity: number) => { - if (quantity < 1) return - - try { - const res = await cartApi.updateQuantity(itemId, quantity) - if (res.success) { - const item = items.value.find(i => i.id === itemId) - if (item) { - item.quantity = quantity - } - } - } catch (error) { - console.error('更新数量失败:', error) - } - } - - // 删除商品 - const removeItem = async (itemId: string) => { - try { - const res = await cartApi.removeItem(itemId) - if (res.success) { - items.value = items.value.filter(i => i.id !== itemId) - ElMessage.success('已删除') - } - } catch (error) { - console.error('删除失败:', error) - } - } - - // 批量删除 - const removeSelected = async () => { - const ids = selectedItems.value.map(item => item.id) - if (ids.length === 0) return - - try { - const res = await cartApi.batchRemove(ids) - if (res.success) { - items.value = items.value.filter(i => !ids.includes(i.id)) - ElMessage.success('已批量删除') - } - } catch (error) { - console.error('批量删除失败:', error) - } - } - - // 清空购物车 - const clearCart = async () => { - try { - const res = await cartApi.clearCart() - if (res.success) { - items.value = [] - ElMessage.success('购物车已清空') - } - } catch (error) { - console.error('清空购物车失败:', error) - } - } - - // 切换选中状态 - const toggleSelect = (itemId: string) => { - const item = items.value.find(i => i.id === itemId) - if (item) { - item.selected = !item.selected - } - } - - // 全选/取消全选 - const toggleSelectAll = () => { - const allSelected = isAllSelected.value - items.value.forEach(item => { - item.selected = !allSelected - }) - } - - // 获取购物车数量(仅数量) - const getCartCount = async () => { - try { - const res = await cartApi.getCount() - if (res.success) { - return res.data.count || 0 - } - return 0 - } catch (error) { - console.error('获取购物车数量失败:', error) - return 0 - } - } - - return { - items, - loading, - itemCount, - totalQuantity, - selectedItems, - selectedCount, - totalPrice, - isAllSelected, - fetchCart, - addToCart, - updateQuantity, - removeItem, - removeSelected, - clearCart, - toggleSelect, - toggleSelectAll, - getCartCount, - } -}) \ No newline at end of file diff --git a/flash-sale-frontend/src/stores/user.ts b/flash-sale-frontend/src/stores/user.ts deleted file mode 100644 index ce3c1dd..0000000 --- a/flash-sale-frontend/src/stores/user.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { defineStore } from 'pinia' -import { ref, computed } from 'vue' -import type { User, LoginParams, RegisterParams } from '@/types/api' -import { userApi } from '@/api/modules/user' -import { ElMessage } from 'element-plus' -import router from '@/router' - -export const useUserStore = defineStore('user', () => { - // 状态 - const user = ref(null) - const token = ref('') - - // 计算属性 - const isLoggedIn = computed(() => !!token.value) - const isAdmin = computed(() => user.value?.role === 'ADMIN' || user.value?.username === 'admin') - const username = computed(() => user.value?.username || '') - - // 从localStorage恢复登录状态 - const initializeAuth = () => { - const savedToken = localStorage.getItem('token') - const savedUser = localStorage.getItem('user') - - if (savedToken && savedUser) { - token.value = savedToken - user.value = JSON.parse(savedUser) - } - } - - // 登录 - const login = async (params: LoginParams) => { - try { - const res = await userApi.login(params) - - if (res.success) { - token.value = res.data.token - user.value = res.data.user - - localStorage.setItem('token', token.value) - localStorage.setItem('user', JSON.stringify(user.value)) - - try { - const profile = await userApi.getInfo() - if (profile.success) { - user.value = { - ...profile.data, - avatar: profile.data.avatar || user.value?.avatar || '', - } - localStorage.setItem('user', JSON.stringify(user.value)) - } - } catch (sessionError) { - console.error('登录成功但会话校验失败:', sessionError) - user.value = null - token.value = '' - localStorage.removeItem('token') - localStorage.removeItem('user') - ElMessage.error('登录成功但会话未建立,请检查 Cookie / 代理配置') - return false - } - - ElMessage.success('登录成功') - const redirect = router.currentRoute.value.query.redirect as string - await router.push(redirect || '/') - - return true - } - - return false - } catch (error) { - console.error('登录失败:', error) - return false - } - } - - // 注册 - const register = async (params: RegisterParams) => { - try { - const res = await userApi.register(params) - - if (res.success) { - ElMessage.success('注册成功,请登录') - router.push('/login') - return true - } - - return false - } catch (error) { - console.error('注册失败:', error) - return false - } - } - - // 退出登录 - const logout = async () => { - try { - if (token.value) { - await userApi.logout() - } - } catch (error) { - console.error('退出登录失败:', error) - } - - user.value = null - token.value = '' - - // 清除localStorage - localStorage.removeItem('token') - localStorage.removeItem('user') - - ElMessage.success('已退出登录') - router.push('/login') - } - - // 获取用户信息 - const getUserInfo = async () => { - if (!token.value) return - - try { - const res = await userApi.getInfo() - - if (res.success) { - user.value = { - ...res.data, - avatar: res.data.avatar || user.value?.avatar || '', - } - localStorage.setItem('user', JSON.stringify(user.value)) - } - } catch (error) { - console.error('获取用户信息失败:', error) - user.value = null - token.value = '' - localStorage.removeItem('token') - localStorage.removeItem('user') - } - } - - // 更新用户信息 - const updateUserInfo = (info: Partial) => { - if (user.value) { - user.value = { ...user.value, ...info } - localStorage.setItem('user', JSON.stringify(user.value)) - } - } - - // 初始化 - initializeAuth() - - return { - user, - token, - isLoggedIn, - isAdmin, - username, - login, - register, - logout, - getUserInfo, - updateUserInfo, - } -}) diff --git a/flash-sale-frontend/src/types/admin.ts b/flash-sale-frontend/src/types/admin.ts deleted file mode 100644 index 2c8193b..0000000 --- a/flash-sale-frontend/src/types/admin.ts +++ /dev/null @@ -1,165 +0,0 @@ -export interface AdminDashboardStats { - totalUsers: number - totalProducts: number - totalOrders: number - totalAmount: number - todayOrders: number - paidOrders: number - pendingOrders: number - activeFlashSales: number -} - -export interface AdminUserStats { - totalUsers: number - activeUsers: number - newUsers: number - onlineUsers: number -} - -export interface AdminOrderStats { - totalOrders: number - paidOrders: number - pendingOrders: number - completedOrders: number - cancelledOrders: number - totalAmount: number -} - -export interface AdminProductStats { - totalProducts: number - activeProducts: number - inactiveProducts: number - lowStockProducts: number -} - -export interface AdminFlashSaleStats { - totalFlashSales: number - activeFlashSales: number - upcomingFlashSales: number - endedFlashSales: number -} - -export interface AdminRecentOrderRow { - id: number - orderNo: string - username: string - productName: string - quantity: number - totalAmount: number - status: string - orderType: 'NORMAL' | 'FLASH_SALE' | 'GROUP_BUYING' - createdAt: string - isFlashSale: boolean -} - -export interface AdminHotProductRow { - id: number - name: string - price: number - stock: number - sales: number -} - -export interface AdminUserRow { - id: number - username: string - email: string - phone: string - status: number - statusText: string - role: 'USER' | 'ADMIN' - isOnline: boolean - createdAt: string - lastLogin?: string -} - -export interface AdminOrderRow { - id: number - orderNo: string - username: string - productName: string - productId?: number - quantity: number - totalAmount: number - status: string - orderType: 'NORMAL' | 'FLASH_SALE' | 'GROUP_BUYING' - createdAt: string - isFlashSale: boolean -} - -export interface AdminProductRow { - id: number - name: string - description: string - category: string - price: number - stock: number - status: number - imageUrl: string - createdAt: string - updatedAt?: string - totalSales?: number - totalRevenue?: number - viewCount?: number - rating?: number -} - -export interface MonitorSystemStatus { - status: string - cpuUsage: number - memoryUsage: number - diskUsage: number - availableProcessors?: number - totalMemory?: string - usedMemory?: string - dbStatus?: string - redisStatus?: string - requestCountToday?: number -} - -export interface RedisNodeStatus { - node: string - status: string - memory: string - connections: number -} - -export interface AdminReviewStats { - totalReviews: number - todayReviews: number - averageRating: number - fiveStarReviews: number -} - -export interface AdminFavoriteStats { - totalFavorites: number - favoriteUsers: number - favoriteProducts: number - todayFavorites: number -} - -export interface AdminReviewRow { - id: number - productId: number - userId: number - orderId: number - productName: string - username: string - rating: number - content: string - status: number - statusText: string - adminReply?: string - repliedAt?: string - createdAt: string -} - -export interface AdminFavoriteRow { - id: number - userId: number - productId: number - productName: string - productCategory: string - username: string - createdAt: string -} diff --git a/flash-sale-frontend/src/types/api.d.ts b/flash-sale-frontend/src/types/api.d.ts deleted file mode 100644 index d756e12..0000000 --- a/flash-sale-frontend/src/types/api.d.ts +++ /dev/null @@ -1,247 +0,0 @@ -// API响应基础类型 -export interface ApiResponse { - code: number - success: boolean - message: string - data: T - timestamp?: number -} - -// 分页参数 -export interface PageParams { - page: number - size: number - sort?: string - order?: 'asc' | 'desc' -} - -// 分页响应 -export interface PageResponse { - content: T[] - totalElements: number - totalPages: number - size: number - number: number - first: boolean - last: boolean -} - -// 用户类型 -export interface User { - id: number - username: string - email: string - phone?: string - avatar?: string - role: 'USER' | 'ADMIN' - status: 'ACTIVE' | 'INACTIVE' | 'BANNED' - createdAt: string - updatedAt: string -} - -// 登录参数 -export interface LoginParams { - username: string - password: string - rememberMe?: boolean -} - -// 注册参数 -export interface RegisterParams { - username: string - password: string - email: string - phone?: string -} - -// 商品类型 -export interface Product { - id: number - name: string - description: string - price: number - stock: number - imageUrl: string - images?: string[] - category: string - status: 'ON_SALE' | 'OFF_SALE' | 'SOLD_OUT' - sales: number - views: number - createdAt: string - updatedAt: string -} - -// 限时活动类型 -export interface FlashSale { - id: number - productId: number - productName: string - productImageUrl: string - originalPrice: number - flashPrice: number - flashStock: number - remainingStock: number - startTime: string - endTime: string - status: 'UPCOMING' | 'ACTIVE' | 'ENDED' | 'PAUSED' - limitPerUser: number - description?: string - createdAt: string - updatedAt: string -} - -// 购物车项 -export interface CartItem { - id: string - productId: number - productName: string - productImage: string - price: number - quantity: number - stock: number - selected: boolean - createdAt: string -} - -// 订单类型 -export interface Order { - id: number - orderNo: string - userId: number - username: string - totalAmount: number - paymentAmount: number - paymentMethod?: string - status: 'PENDING' | 'PAID' | 'SHIPPED' | 'COMPLETED' | 'CANCELLED' | 'REFUNDING' | 'REFUNDED' - orderType?: 'NORMAL' | 'FLASH_SALE' | 'GROUP_BUYING' - items: OrderItem[] - address?: OrderAddress - remark?: string - createdAt: string - updatedAt: string - paidAt?: string - shippedAt?: string - completedAt?: string -} - -// 订单项 -export interface OrderItem { - id: number - productId: number - productName: string - productImage: string - price: number - quantity: number - subtotal: number -} - -// 订单地址 -export interface OrderAddress { - name: string - phone: string - province: string - city: string - district: string - address: string - zipCode?: string -} - -// 统计数据 -export interface Statistics { - totalUsers: number - totalProducts: number - totalOrders: number - totalSales: number - todayOrders: number - todaySales: number - activeFlashSales: number - onlineUsers: number -} - -// 退货类型 -export interface OrderReturn { - id: number - returnNo: string - orderId: number - orderNo: string - userId: number - username: string - refundAmount: number - reason: string - description?: string - images?: string - status: 'PENDING' | 'APPROVED' | 'RETURNING' | 'COMPLETED' | 'REJECTED' | 'CANCELLED' - statusText: string - rejectReason?: string - adminRemark?: string - returnTracking?: string - productName?: string - productImage?: string - reviewedAt?: string - shippedAt?: string - completedAt?: string - cancelledAt?: string - createdAt: string - updatedAt: string -} - -// 拼团活动类型 -export interface GroupBuying { - id: number - productId: number - productName: string - productImageUrl: string - productPrice: number - groupPrice: number - requiredMembers: number - durationMinutes: number - totalStock: number - remainingStock: number - maxPerUser: number - status: 'DRAFT' | 'UPCOMING' | 'ACTIVE' | 'ENDED' - statusDescription: string - startTime: string - endTime: string - createdAt: string - updatedAt: string - activeGroupCount: number - discount: number -} - -// 拼团团组类型 -export interface GroupBuyingGroup { - id: number - groupNo: string - groupBuyingId: number - leaderUserId: number - leaderUsername: string - requiredMembers: number - currentMembers: number - status: 'FORMING' | 'SUCCESS' | 'FAILED' - statusDescription: string - expireTime: string - createdAt: string - completedAt?: string - members: GroupBuyingMember[] - groupBuying?: GroupBuying -} - -// 拼团成员类型 -export interface GroupBuyingMember { - id: number - userId: number - username: string - avatar?: string - orderId?: number - status: number - joinedAt: string -} - -// 拼团统计 -export interface GroupBuyingStatistics { - totalActivities: number - activeActivities: number - myGroups: number - successGroups: number - totalSaved: number -} \ No newline at end of file diff --git a/flash-sale-frontend/src/types/flashsale.ts b/flash-sale-frontend/src/types/flashsale.ts deleted file mode 100644 index 0742c0f..0000000 --- a/flash-sale-frontend/src/types/flashsale.ts +++ /dev/null @@ -1,29 +0,0 @@ -export interface FlashSale { - id: number - productId: number - productName: string - productImage: string - originalPrice: number - flashPrice: number - flashStock: number - soldCount: number - startTime: string - endTime: string - status: number // 0-未开始 1-进行中 2-已结束 - description?: string - createdAt: string - updatedAt: string -} - -export interface FlashSaleParams { - status?: number - keyword?: string - sort?: string - page?: number - size?: number -} - -export interface FlashSaleParticipation { - flashSaleId: number - quantity: number -} \ No newline at end of file diff --git a/flash-sale-frontend/src/types/product.ts b/flash-sale-frontend/src/types/product.ts deleted file mode 100644 index 8e6318d..0000000 --- a/flash-sale-frontend/src/types/product.ts +++ /dev/null @@ -1,37 +0,0 @@ -export interface Product { - id: number - name: string - description: string - price: number - stock: number - image: string - category: string - categoryId: number - brand?: string - specifications?: Record - sales: number - rating: number - reviewCount: number - status: number // 0-下架 1-上架 - createdAt: string - updatedAt: string -} - -export interface ProductParams { - categoryId?: number - keyword?: string - minPrice?: number - maxPrice?: number - sort?: string - page?: number - size?: number -} - -export interface Category { - id: number - name: string - parentId?: number - icon?: string - sort: number - children?: Category[] -} \ No newline at end of file diff --git a/flash-sale-frontend/src/utils/image.ts b/flash-sale-frontend/src/utils/image.ts deleted file mode 100644 index b5fe2eb..0000000 --- a/flash-sale-frontend/src/utils/image.ts +++ /dev/null @@ -1,66 +0,0 @@ -import defaultProductImage from '@/assets/default-product.svg' - -export const DEFAULT_PRODUCT_IMAGE = defaultProductImage - -const ABSOLUTE_URL_PATTERN = /^(https?:)?\/\//i -const SPECIAL_URL_PATTERN = /^(data:|blob:)/i - -const normalizeBaseUrl = (value?: string) => { - if (!value) return '' - return value.endsWith('/') ? value.slice(0, -1) : value -} - -export const resolveImageUrl = (value?: string | null) => { - if (!value || !String(value).trim()) { - return DEFAULT_PRODUCT_IMAGE - } - - const imageUrl = String(value).trim() - if (ABSOLUTE_URL_PATTERN.test(imageUrl) || SPECIAL_URL_PATTERN.test(imageUrl)) { - return imageUrl - } - - const baseUrl = normalizeBaseUrl(import.meta.env.VITE_API_BASE_URL) - if (!baseUrl) { - return imageUrl.startsWith('/') ? imageUrl : `/${imageUrl}` - } - - return imageUrl.startsWith('/') ? `${baseUrl}${imageUrl}` : `${baseUrl}/${imageUrl}` -} - -export const normalizeStorageImageUrl = (value?: string | null) => { - if (!value || !String(value).trim()) { - return '' - } - - const imageUrl = String(value).trim() - if (ABSOLUTE_URL_PATTERN.test(imageUrl)) { - try { - const parsed = new URL(imageUrl.startsWith('//') ? `http:${imageUrl}` : imageUrl) - if ( - parsed.pathname.startsWith('/uploads/') || - parsed.pathname.startsWith('/images/') || - parsed.pathname.startsWith('/static/') - ) { - return parsed.pathname - } - } catch { - return imageUrl - } - } - - return imageUrl -} - -export const applyFallbackImage = (event: Event) => { - const target = event.target as HTMLImageElement | null - if (!target) return - - if (target.dataset.fallbackApplied === 'true') { - return - } - - target.dataset.fallbackApplied = 'true' - target.onerror = null - target.src = DEFAULT_PRODUCT_IMAGE -} diff --git a/flash-sale-frontend/src/utils/normalizers.ts b/flash-sale-frontend/src/utils/normalizers.ts deleted file mode 100644 index 970faf2..0000000 --- a/flash-sale-frontend/src/utils/normalizers.ts +++ /dev/null @@ -1,407 +0,0 @@ -import type { - CartItem, - FlashSale, - GroupBuying, - GroupBuyingGroup, - Order, - OrderAddress, - OrderReturn, - PageResponse, - Product, - User, -} from '@/types/api' -import type { - AdminHotProductRow, - AdminOrderRow, - AdminProductRow, - AdminRecentOrderRow, - AdminUserRow, -} from '@/types/admin' -import { DEFAULT_PRODUCT_IMAGE, normalizeStorageImageUrl, resolveImageUrl } from '@/utils/image' - -const toNumber = (value: unknown, fallback = 0) => { - const result = Number(value) - return Number.isFinite(result) ? result : fallback -} - -const toString = (value: unknown, fallback = '') => { - if (value === null || value === undefined) { - return fallback - } - return String(value) -} - -const toIsoLikeString = (value: unknown) => { - const raw = toString(value) - return raw || new Date().toISOString() -} - -export const buildOrderNo = (id: number | string) => { - const numericId = toString(id).padStart(6, '0') - return `FS${numericId}` -} - -export const mapUserStatusText = (status: number) => { - return status === 1 ? '正常' : '禁用' -} - -export const mapOrderStatus = (status: number | string): Order['status'] => { - const value = typeof status === 'string' ? status : toNumber(status) - if (value === 'PENDING' || value === 1) return 'PENDING' - if (value === 'PAID' || value === 2) return 'PAID' - if (value === 'SHIPPED' || value === 3) return 'SHIPPED' - if (value === 'COMPLETED' || value === 4) return 'COMPLETED' - if (value === 'CANCELLED' || value === 5) return 'CANCELLED' - if (value === 'REFUNDING' || value === 6) return 'REFUNDING' - if (value === 'REFUNDED' || value === 7) return 'REFUNDED' - return 'PENDING' -} - -export const mapOrderType = (orderType: number | string | undefined, isFlashSale?: boolean): 'NORMAL' | 'FLASH_SALE' | 'GROUP_BUYING' => { - const value = typeof orderType === 'string' ? orderType : toNumber(orderType) - if (value === 'FLASH_SALE' || value === 2) return 'FLASH_SALE' - if (value === 'GROUP_BUYING' || value === 3) return 'GROUP_BUYING' - if (isFlashSale) return 'FLASH_SALE' - return 'NORMAL' -} - -export const mapFlashSaleStatus = (status: number | string): FlashSale['status'] => { - const value = typeof status === 'string' ? status : toNumber(status) - if (value === 'UPCOMING' || value === 1) return 'UPCOMING' - if (value === 'ACTIVE' || value === 2) return 'ACTIVE' - if (value === 'ENDED' || value === 3) return 'ENDED' - if (value === 'PAUSED' || value === 4) return 'PAUSED' - return 'UPCOMING' -} - -export const mapProductStatus = (status: number | string, stock = 0): Product['status'] => { - const value = typeof status === 'string' ? status : toNumber(status) - if (stock <= 0) return 'SOLD_OUT' - if (value === 'OFF_SALE' || value === 0) return 'OFF_SALE' - return 'ON_SALE' -} - -export const normalizeUser = (user: Record): User => { - const username = toString(user.username) - return { - id: toNumber(user.id), - username, - email: toString(user.email), - phone: toString(user.phone), - avatar: resolveImageUrl(toString(user.avatar, '')), - role: toString(user.role).toUpperCase() === 'ADMIN' ? 'ADMIN' : username === 'admin' ? 'ADMIN' : 'USER', - status: toNumber(user.status, 1) === 1 ? 'ACTIVE' : 'BANNED', - createdAt: toIsoLikeString(user.createdAt), - updatedAt: toIsoLikeString(user.updatedAt || user.createdAt), - } -} - -export const normalizeProduct = (product: Record): Product => { - const stock = toNumber(product.stock) - const imageUrl = resolveImageUrl(toString(product.imageUrl, '')) - return { - id: toNumber(product.id), - name: toString(product.name), - description: toString(product.description), - price: toNumber(product.price), - stock, - imageUrl, - images: imageUrl ? [imageUrl] : [DEFAULT_PRODUCT_IMAGE], - category: toString(product.category, '默认分类'), - status: mapProductStatus(product.status, stock), - sales: toNumber(product.sales), - views: toNumber(product.viewCount ?? product.views), - createdAt: toIsoLikeString(product.createdAt), - updatedAt: toIsoLikeString(product.updatedAt || product.createdAt), - } -} - -export const normalizeFlashSale = (flashSale: Record): FlashSale => { - const flashStock = toNumber(flashSale.flashStock) - const remainingStock = toNumber(flashSale.remainingStock, flashStock) - return { - id: toNumber(flashSale.id), - productId: toNumber(flashSale.productId), - productName: toString(flashSale.productName), - productImageUrl: resolveImageUrl(toString(flashSale.productImageUrl, '')), - originalPrice: toNumber(flashSale.originalPrice), - flashPrice: toNumber(flashSale.flashPrice), - flashStock, - remainingStock, - startTime: toIsoLikeString(flashSale.startTime), - endTime: toIsoLikeString(flashSale.endTime), - status: mapFlashSaleStatus(flashSale.status), - limitPerUser: toNumber(flashSale.limitPerUser, 1), - description: toString(flashSale.description || flashSale.statusDescription), - createdAt: toIsoLikeString(flashSale.createdAt), - updatedAt: toIsoLikeString(flashSale.updatedAt || flashSale.createdAt), - } -} - -const buildOrderAddress = (order: Record): OrderAddress | undefined => { - const name = toString(order.receiverName) - const phone = toString(order.receiverPhone) - const address = toString(order.receiverAddress) - if (!name && !phone && !address) { - return undefined - } - return { - name, - phone, - province: '', - city: '', - district: '', - address, - } -} - -export const normalizeOrder = (order: Record): Order => { - const totalAmount = toNumber(order.totalAmount ?? order.totalPrice) - const quantity = toNumber(order.quantity, 1) - const status = mapOrderStatus(order.status) - const createdAt = toIsoLikeString(order.createdAt) - const updatedAt = toIsoLikeString(order.updatedAt || order.createdAt) - const productImage = resolveImageUrl(toString(order.productImageUrl, '')) - - const fallbackItem = { - id: toNumber(order.productId || order.id), - productId: toNumber(order.productId), - productName: toString(order.productName, '未知商品'), - productImage, - price: quantity > 0 ? Number((totalAmount / quantity).toFixed(2)) : totalAmount, - quantity, - subtotal: totalAmount, - } - - const items = Array.isArray(order.items) && order.items.length > 0 - ? order.items.map((item: Record) => ({ - id: toNumber(item.id || item.productId), - productId: toNumber(item.productId), - productName: toString(item.productName, '未知商品'), - productImage: resolveImageUrl(toString(item.productImageUrl || item.productImage, '')), - price: toNumber(item.price), - quantity: toNumber(item.quantity, 1), - subtotal: toNumber(item.subtotal ?? item.price), - })) - : [fallbackItem] - - return { - id: toNumber(order.id), - orderNo: toString(order.orderNo, buildOrderNo(order.id)), - userId: toNumber(order.userId), - username: toString(order.username), - totalAmount, - paymentAmount: totalAmount, - paymentMethod: toString(order.paymentMethod) || (status === 'PENDING' ? undefined : 'ONLINE'), - status, - orderType: mapOrderType(order.orderType, order.isFlashSale), - items, - address: buildOrderAddress(order), - remark: toString(order.remark), - createdAt, - updatedAt, - paidAt: order.paidAt ? toIsoLikeString(order.paidAt) : (status === 'PAID' || status === 'SHIPPED' || status === 'COMPLETED' ? updatedAt : undefined), - shippedAt: order.shippedAt ? toIsoLikeString(order.shippedAt) : (status === 'SHIPPED' || status === 'COMPLETED' ? updatedAt : undefined), - completedAt: order.completedAt ? toIsoLikeString(order.completedAt) : (status === 'COMPLETED' ? updatedAt : undefined), - } -} - -export const normalizeCartItems = (cart: Record | undefined): CartItem[] => { - const items = Array.isArray(cart?.items) ? cart.items : [] - return items.map((item: Record) => ({ - id: toString(item.productId), - productId: toNumber(item.productId), - productName: toString(item.productName), - productImage: resolveImageUrl(toString(item.productImageUrl || item.productImage, '')), - price: toNumber(item.productPrice), - quantity: toNumber(item.quantity, 1), - stock: toNumber(item.stock), - selected: true, - createdAt: new Date().toISOString(), - })) -} - -export const normalizePage = (payload: Record, mapper: (item: Record) => T): PageResponse => { - const content = Array.isArray(payload.content) ? payload.content.map((item: Record) => mapper(item)) : [] - const size = toNumber(payload.size, content.length || 10) - const pageNumber = toNumber(payload.currentPage ?? payload.number) - const totalElements = toNumber(payload.totalElements, content.length) - const totalPages = toNumber(payload.totalPages, size > 0 ? Math.ceil(totalElements / size) : 1) - return { - content, - totalElements, - totalPages, - size, - number: pageNumber, - first: pageNumber <= 0, - last: totalPages === 0 ? true : pageNumber >= totalPages - 1, - } -} - -export const normalizeAdminRecentOrder = (order: Record): AdminRecentOrderRow => ({ - id: toNumber(order.id), - orderNo: buildOrderNo(order.id), - username: toString(order.username), - productName: toString(order.productName), - quantity: toNumber(order.quantity, 1), - totalAmount: toNumber(order.totalAmount ?? order.totalPrice), - status: mapOrderStatus(order.status), - orderType: mapOrderType(order.orderType, order.isFlashSale), - createdAt: toIsoLikeString(order.createdAt), - isFlashSale: Boolean(order.isFlashSale), -}) - -export const normalizeAdminHotProduct = (product: Record): AdminHotProductRow => ({ - id: toNumber(product.id), - name: toString(product.name), - price: toNumber(product.price), - stock: toNumber(product.stock), - sales: toNumber(product.sales), -}) - -export const normalizeAdminUser = (user: Record): AdminUserRow => ({ - id: toNumber(user.id), - username: toString(user.username), - email: toString(user.email), - phone: toString(user.phone), - status: toNumber(user.status, 1), - statusText: mapUserStatusText(toNumber(user.status, 1)), - role: toString(user.role).toUpperCase() === 'ADMIN' || toString(user.username) === 'admin' ? 'ADMIN' : 'USER', - isOnline: Boolean(user.isOnline), - createdAt: toIsoLikeString(user.createdAt), - lastLogin: user.lastLogin ? toIsoLikeString(user.lastLogin) : undefined, -}) - -export const normalizeAdminOrder = (order: Record): AdminOrderRow => ({ - id: toNumber(order.id), - orderNo: buildOrderNo(order.id), - username: toString(order.username), - productName: toString(order.productName), - productId: toNumber(order.productId), - quantity: toNumber(order.quantity, 1), - totalAmount: toNumber(order.totalAmount), - status: mapOrderStatus(order.status), - orderType: mapOrderType(order.orderType, order.isFlashSale), - createdAt: toIsoLikeString(order.createdAt), - isFlashSale: Boolean(order.isFlashSale), -}) - -export const normalizeAdminProduct = (product: Record): AdminProductRow => ({ - id: toNumber(product.id), - name: toString(product.name), - description: toString(product.description), - category: toString(product.category, '默认分类'), - price: toNumber(product.price), - stock: toNumber(product.stock), - status: toNumber(product.status, 1), - imageUrl: normalizeStorageImageUrl(toString(product.imageUrl, '')), - createdAt: toIsoLikeString(product.createdAt), - updatedAt: product.updatedAt ? toIsoLikeString(product.updatedAt) : undefined, - totalSales: toNumber(product.totalSales), - totalRevenue: toNumber(product.totalRevenue), - viewCount: toNumber(product.viewCount), - rating: toNumber(product.rating), -}) - -export const mapGroupBuyingStatus = (status: number | string): GroupBuying['status'] => { - const value = typeof status === 'string' ? status : toNumber(status) - if (value === 'DRAFT' || value === 0) return 'DRAFT' - if (value === 'UPCOMING' || value === 1) return 'UPCOMING' - if (value === 'ACTIVE' || value === 2) return 'ACTIVE' - if (value === 'ENDED' || value === 3) return 'ENDED' - return 'DRAFT' -} - -export const mapGroupStatus = (status: number | string): GroupBuyingGroup['status'] => { - const value = typeof status === 'string' ? status : toNumber(status) - if (value === 'FORMING' || value === 1) return 'FORMING' - if (value === 'SUCCESS' || value === 2) return 'SUCCESS' - if (value === 'FAILED' || value === 3) return 'FAILED' - return 'FORMING' -} - -export const normalizeGroupBuying = (gb: Record): GroupBuying => ({ - id: toNumber(gb.id), - productId: toNumber(gb.productId), - productName: toString(gb.productName), - productImageUrl: resolveImageUrl(toString(gb.productImageUrl, '')), - productPrice: toNumber(gb.productPrice), - groupPrice: toNumber(gb.groupPrice), - requiredMembers: toNumber(gb.requiredMembers, 2), - durationMinutes: toNumber(gb.durationMinutes, 1440), - totalStock: toNumber(gb.totalStock), - remainingStock: toNumber(gb.remainingStock), - maxPerUser: toNumber(gb.maxPerUser, 1), - status: mapGroupBuyingStatus(gb.status), - statusDescription: toString(gb.statusDescription), - startTime: toIsoLikeString(gb.startTime), - endTime: toIsoLikeString(gb.endTime), - createdAt: toIsoLikeString(gb.createdAt), - updatedAt: toIsoLikeString(gb.updatedAt || gb.createdAt), - activeGroupCount: toNumber(gb.activeGroupCount), - discount: toNumber(gb.discount), -}) - -export const mapReturnStatus = (status: number | string): OrderReturn['status'] => { - const value = typeof status === 'string' ? status : toNumber(status) - if (value === 'PENDING' || value === 1) return 'PENDING' - if (value === 'APPROVED' || value === 2) return 'APPROVED' - if (value === 'RETURNING' || value === 3) return 'RETURNING' - if (value === 'COMPLETED' || value === 4) return 'COMPLETED' - if (value === 'REJECTED' || value === 5) return 'REJECTED' - if (value === 'CANCELLED' || value === 6) return 'CANCELLED' - return 'PENDING' -} - -export const normalizeOrderReturn = (ret: Record): OrderReturn => ({ - id: toNumber(ret.id), - returnNo: toString(ret.returnNo), - orderId: toNumber(ret.orderId), - orderNo: toString(ret.orderNo), - userId: toNumber(ret.userId), - username: toString(ret.username), - refundAmount: toNumber(ret.refundAmount), - reason: toString(ret.reason), - description: toString(ret.description), - images: toString(ret.images), - status: mapReturnStatus(ret.status), - statusText: toString(ret.statusText), - rejectReason: toString(ret.rejectReason), - adminRemark: toString(ret.adminRemark), - returnTracking: toString(ret.returnTracking), - productName: toString(ret.productName), - productImage: resolveImageUrl(toString(ret.productImage, '')), - reviewedAt: ret.reviewedAt ? toIsoLikeString(ret.reviewedAt) : undefined, - shippedAt: ret.shippedAt ? toIsoLikeString(ret.shippedAt) : undefined, - completedAt: ret.completedAt ? toIsoLikeString(ret.completedAt) : undefined, - cancelledAt: ret.cancelledAt ? toIsoLikeString(ret.cancelledAt) : undefined, - createdAt: toIsoLikeString(ret.createdAt), - updatedAt: toIsoLikeString(ret.updatedAt || ret.createdAt), -}) - -export const normalizeGroupBuyingGroup = (group: Record): GroupBuyingGroup => ({ - id: toNumber(group.id), - groupNo: toString(group.groupNo), - groupBuyingId: toNumber(group.groupBuyingId), - leaderUserId: toNumber(group.leaderUserId), - leaderUsername: toString(group.leaderUsername), - requiredMembers: toNumber(group.requiredMembers, 2), - currentMembers: toNumber(group.currentMembers, 1), - status: mapGroupStatus(group.status), - statusDescription: toString(group.statusDescription), - expireTime: toIsoLikeString(group.expireTime), - createdAt: toIsoLikeString(group.createdAt), - completedAt: group.completedAt ? toIsoLikeString(group.completedAt) : undefined, - members: Array.isArray(group.members) - ? group.members.map((m: Record) => ({ - id: toNumber(m.id), - userId: toNumber(m.userId), - username: toString(m.username), - avatar: resolveImageUrl(toString(m.avatar, '')), - orderId: m.orderId ? toNumber(m.orderId) : undefined, - status: toNumber(m.status), - joinedAt: toIsoLikeString(m.joinedAt), - })) - : [], - groupBuying: group.groupBuying ? normalizeGroupBuying(group.groupBuying) : undefined, -}) diff --git a/flash-sale-frontend/tailwind.config.js b/flash-sale-frontend/tailwind.config.js deleted file mode 100644 index 9c70ba1..0000000 --- a/flash-sale-frontend/tailwind.config.js +++ /dev/null @@ -1,29 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - content: [ - "./index.html", - "./src/**/*.{vue,js,ts,jsx,tsx}", - ], - theme: { - extend: { - colors: { - primary: { - 50: '#f7f7f6', - 100: '#efefed', - 200: '#dfdfdc', - 300: '#c6c6c2', - 400: '#9f9f99', - 500: '#7b7b74', - 600: '#5e5e58', - 700: '#44443f', - 800: '#2b2b27', - 900: '#171715', - }, - }, - animation: { - 'pulse-fast': 'pulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite', - }, - }, - }, - plugins: [], -} diff --git a/flash-sale-frontend/vite.config.ts b/flash-sale-frontend/vite.config.ts deleted file mode 100644 index 2439f8a..0000000 --- a/flash-sale-frontend/vite.config.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' -import path from 'path' - -export default defineConfig({ - plugins: [vue()], - resolve: { - alias: { - '@': path.resolve(__dirname, 'src'), - }, - }, - server: { - port: 3000, - proxy: { - '/api': { - target: 'http://localhost:8080', - changeOrigin: true, - rewrite: (proxyPath) => proxyPath.replace(/^\/api/, '/api'), - }, - '/images': { - target: 'http://localhost:8080', - changeOrigin: true, - }, - '/uploads': { - target: 'http://localhost:8080', - changeOrigin: true, - }, - '/static': { - target: 'http://localhost:8080', - changeOrigin: true, - }, - }, - }, - css: { - preprocessorOptions: { - scss: { - api: 'modern-compiler', - }, - }, - }, - build: { - rollupOptions: { - output: { - manualChunks: { - vendor: ['vue', 'vue-router', 'pinia'], - element: ['element-plus', '@element-plus/icons-vue'], - utils: ['axios', 'dayjs', '@vueuse/core'], - charts: ['echarts', 'vue-echarts'], - }, - }, - }, - }, -}) diff --git a/pom.xml b/pom.xml index 3ec653b..d38e1c5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.org - FlashSaleSystem + CommunityFreshGroupBuySystem 0.0.1-SNAPSHOT 社区生鲜团购系统 社区生鲜团购系统 @@ -129,6 +129,7 @@ + community-fresh-group-buy-system-${project.version} org.apache.maven.plugins @@ -145,7 +146,7 @@ spring-boot-maven-plugin ${spring-boot.version} - com.org.flashsalesystem.FlashSaleSystemApplication + com.org.flashsalesystem.CommunityFreshGroupBuySystemApplication true diff --git a/sql/cleanup_demo_data.sql b/sql/cleanup_demo_data.sql index def6516..807fb3a 100644 --- a/sql/cleanup_demo_data.sql +++ b/sql/cleanup_demo_data.sql @@ -1,4 +1,4 @@ --- 清理演示过程数据:秒杀活动、秒杀订单、admin 前台订单/拼团/评价/收藏/通知数据 +-- 清理演示过程数据:限时活动、限时订单、admin 前台订单/拼团/评价/收藏/通知数据 -- 适用于已导入 flash_sale_db.sql 的 MySQL 数据库。 SET @admin_user_id := (SELECT id diff --git a/sql/flash_sale_db.sql b/sql/flash_sale_db.sql index d5c845a..6d5f9f9 100644 --- a/sql/flash_sale_db.sql +++ b/sql/flash_sale_db.sql @@ -23,10 +23,10 @@ SET FOREIGN_KEY_CHECKS = 0; DROP TABLE IF EXISTS `flash_sales`; CREATE TABLE `flash_sales` ( - `id` bigint NOT NULL AUTO_INCREMENT COMMENT '秒杀活动ID', + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '限时活动ID', `product_id` bigint NOT NULL COMMENT '商品ID', - `flash_price` decimal(10, 2) NOT NULL COMMENT '秒杀价格', - `flash_stock` int NOT NULL COMMENT '秒杀库存', + `flash_price` decimal(10, 2) NOT NULL COMMENT '活动价格', + `flash_stock` int NOT NULL COMMENT '活动库存', `start_time` timestamp NOT NULL COMMENT '开始时间', `end_time` timestamp NOT NULL COMMENT '结束时间', `status` tinyint DEFAULT '1' COMMENT '状态:1-未开始,2-进行中,3-已结束', @@ -42,7 +42,7 @@ CREATE TABLE `flash_sales` ) ENGINE = InnoDB AUTO_INCREMENT = 13 DEFAULT CHARSET = utf8mb4 - COLLATE = utf8mb4_unicode_ci COMMENT ='秒杀活动表'; + COLLATE = utf8mb4_unicode_ci COMMENT ='限时活动表'; -- ---------------------------- -- Records of flash_sales @@ -412,7 +412,7 @@ CREATE TABLE `orders` `quantity` int NOT NULL DEFAULT '1' COMMENT '购买数量', `total_price` decimal(10, 2) NOT NULL COMMENT '总价', `status` tinyint DEFAULT '1' COMMENT '状态:1-待支付,2-已支付,3-已发货,4-已完成,5-已取消', - `order_type` tinyint DEFAULT '1' COMMENT '订单类型:1-普通订单,2-秒杀订单', + `order_type` tinyint DEFAULT '1' COMMENT '订单类型:1-普通订单,2-限时订单', `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `completed_at` datetime(6) DEFAULT NULL, diff --git a/src/main/java/com/org/flashsalesystem/FlashSaleSystemApplication.java b/src/main/java/com/org/flashsalesystem/CommunityFreshGroupBuySystemApplication.java similarity index 69% rename from src/main/java/com/org/flashsalesystem/FlashSaleSystemApplication.java rename to src/main/java/com/org/flashsalesystem/CommunityFreshGroupBuySystemApplication.java index f3cd6c5..8b30ef6 100644 --- a/src/main/java/com/org/flashsalesystem/FlashSaleSystemApplication.java +++ b/src/main/java/com/org/flashsalesystem/CommunityFreshGroupBuySystemApplication.java @@ -6,10 +6,10 @@ import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling -public class FlashSaleSystemApplication { +public class CommunityFreshGroupBuySystemApplication { public static void main(String[] args) { - SpringApplication.run(FlashSaleSystemApplication.class, args); + SpringApplication.run(CommunityFreshGroupBuySystemApplication.class, args); } } diff --git a/src/main/java/com/org/flashsalesystem/config/GlobalExceptionHandler.java b/src/main/java/com/org/flashsalesystem/config/GlobalExceptionHandler.java index ca4a4be..6d8eff8 100644 --- a/src/main/java/com/org/flashsalesystem/config/GlobalExceptionHandler.java +++ b/src/main/java/com/org/flashsalesystem/config/GlobalExceptionHandler.java @@ -46,16 +46,16 @@ public class GlobalExceptionHandler { } /** - * 秒杀相关异常处理 + * 限时活动相关异常处理 */ @ExceptionHandler(FlashSaleException.class) public ResponseEntity handleFlashSaleException(FlashSaleException e, HttpServletRequest request) { - log.warn("秒杀异常: {} - {}", e.getErrorCode(), e.getMessage(), e); + log.warn("限时活动异常: {} - {}", e.getErrorCode(), e.getMessage(), e); ErrorResponse errorResponse = ErrorResponse.builder() .timestamp(LocalDateTime.now()) .status(HttpStatus.BAD_REQUEST.value()) - .error("Flash Sale Error") + .error("Group Buy Error") .message(e.getMessage()) .errorCode(e.getErrorCode()) .path(request.getRequestURI()) @@ -379,7 +379,7 @@ public class GlobalExceptionHandler { } /** - * 秒杀异常类 + * 限时活动异常类 */ public static class FlashSaleException extends RuntimeException { private final String errorCode; diff --git a/src/main/java/com/org/flashsalesystem/config/RedisInitializer.java b/src/main/java/com/org/flashsalesystem/config/RedisInitializer.java index b3e171a..2d7ee8d 100644 --- a/src/main/java/com/org/flashsalesystem/config/RedisInitializer.java +++ b/src/main/java/com/org/flashsalesystem/config/RedisInitializer.java @@ -32,7 +32,7 @@ public class RedisInitializer implements ApplicationRunner { // 1. 测试Redis连接 testRedisConnection(); - // 2. 预热活跃的秒杀活动库存 + // 2. 预热活跃的限时活动库存 preloadActiveFlashSales(); // 3. 清理过期数据(可选) @@ -81,18 +81,18 @@ public class RedisInitializer implements ApplicationRunner { } /** - * 预热活跃的秒杀活动库存 + * 预热活跃的限时活动库存 */ private void preloadActiveFlashSales() { try { - log.info("正在预热活跃秒杀活动库存..."); + log.info("正在预热活跃限时活动库存..."); // 调用FlashSaleService的预热方法 flashSaleService.preloadAllActiveFlashSales(); - log.info("活跃秒杀活动库存预热完成"); + log.info("活跃限时活动库存预热完成"); } catch (Exception e) { - log.error("预热秒杀活动库存失败", e); + log.error("预热限时活动库存失败", e); // 不抛出异常,允许应用继续启动 } } diff --git a/src/main/java/com/org/flashsalesystem/config/RedissonConfig.java b/src/main/java/com/org/flashsalesystem/config/RedissonConfig.java index 93a2bc8..4864ff9 100644 --- a/src/main/java/com/org/flashsalesystem/config/RedissonConfig.java +++ b/src/main/java/com/org/flashsalesystem/config/RedissonConfig.java @@ -234,14 +234,14 @@ public class RedissonConfig { } /** - * 秒杀Lua脚本 + * 限时活动Lua脚本 */ @Bean public DefaultRedisScript flashSaleScript() { DefaultRedisScript script = new DefaultRedisScript<>(); script.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/flashsale.lua"))); script.setResultType(Long.class); - log.info("加载秒杀Lua脚本"); + log.info("加载限时活动Lua脚本"); return script; } diff --git a/src/main/java/com/org/flashsalesystem/config/SwaggerConfig.java b/src/main/java/com/org/flashsalesystem/config/SwaggerConfig.java index a771e54..58e894d 100644 --- a/src/main/java/com/org/flashsalesystem/config/SwaggerConfig.java +++ b/src/main/java/com/org/flashsalesystem/config/SwaggerConfig.java @@ -27,8 +27,8 @@ public class SwaggerConfig { .version("1.0.0") .contact(new Contact() .name("开发团队") - .email("dev@flashsale.com") - .url("https://github.com/flashsale")) + .email("dev@community-fresh-groupbuy.example") + .url("https://github.com/community-fresh-group-buy")) .license(new License() .name("MIT License") .url("https://opensource.org/licenses/MIT"))); @@ -57,12 +57,12 @@ public class SwaggerConfig { } /** - * 秒杀管理API分组 + * 限时活动管理API分组 */ @Bean public GroupedOpenApi flashSaleApi() { return GroupedOpenApi.builder() - .group("秒杀管理") + .group("限时活动管理") .pathsToMatch("/api/flashsale/**") .build(); } @@ -110,4 +110,4 @@ public class SwaggerConfig { .pathsToMatch("/**") .build(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/org/flashsalesystem/controller/AdminController.java b/src/main/java/com/org/flashsalesystem/controller/AdminController.java index c89913e..e0b0848 100644 --- a/src/main/java/com/org/flashsalesystem/controller/AdminController.java +++ b/src/main/java/com/org/flashsalesystem/controller/AdminController.java @@ -137,9 +137,9 @@ public class AdminController { } /** - * 获取秒杀统计数据 + * 获取限时活动统计数据 */ - @Operation(summary = "获取秒杀统计数据") + @Operation(summary = "获取限时活动统计数据") @GetMapping("/flashsales/stats") public ResponseEntity> getFlashSaleStats() { try { @@ -147,16 +147,16 @@ public class AdminController { Map response = new HashMap<>(); response.put("success", true); - response.put("message", "获取秒杀统计数据成功"); + response.put("message", "获取限时活动统计数据成功"); response.put("data", stats); return ResponseEntity.ok(response); } catch (Exception e) { - log.error("获取秒杀统计数据失败", e); + log.error("获取限时活动统计数据失败", e); Map response = new HashMap<>(); response.put("success", false); - response.put("message", "获取秒杀统计数据失败"); + response.put("message", "获取限时活动统计数据失败"); return ResponseEntity.badRequest().body(response); } diff --git a/src/main/java/com/org/flashsalesystem/controller/ApiController.java b/src/main/java/com/org/flashsalesystem/controller/ApiController.java index a6802d5..a3ea2fb 100644 --- a/src/main/java/com/org/flashsalesystem/controller/ApiController.java +++ b/src/main/java/com/org/flashsalesystem/controller/ApiController.java @@ -75,10 +75,10 @@ public class ApiController { } /** - * 获取活跃的秒杀活动 + * 获取活跃的限时活动 */ @GetMapping("/flashsales/active") - @Operation(summary = "获取活跃的秒杀活动") + @Operation(summary = "获取活跃的限时活动") public ResponseEntity> getActiveFlashSales() { Map response = new HashMap<>(); @@ -107,19 +107,19 @@ public class ApiController { response.put("data", result); } catch (Exception e) { - log.error("获取活跃秒杀活动失败", e); + log.error("获取活跃限时活动失败", e); response.put("success", false); - response.put("message", "获取活跃秒杀活动失败"); + response.put("message", "获取活跃限时活动失败"); } return ResponseEntity.ok(response); } /** - * 参与秒杀 + * 参与限时活动 */ @PostMapping("/flashsales/participate") - @Operation(summary = "参与秒杀") + @Operation(summary = "参与限时活动") public ResponseEntity> participate( @RequestBody Map request, HttpServletRequest httpRequest) { @@ -143,8 +143,8 @@ public class ApiController { FlashSaleDTO.ParticipateDTO participateDTO = new FlashSaleDTO.ParticipateDTO(); participateDTO.setFlashSaleId(flashSaleId); participateDTO.setQuantity(quantity); - - // 调用秒杀服务 + + // 调用限时活动服务 FlashSaleDTO.ResultDTO result = flashSaleService.participateFlashSale(userId, participateDTO); response.put("success", result.getSuccess()); @@ -154,7 +154,7 @@ public class ApiController { } } catch (Exception e) { - log.error("参与秒杀失败", e); + log.error("参与限时活动失败", e); response.put("success", false); response.put("message", e.getMessage()); } @@ -204,10 +204,10 @@ public class ApiController { } /** - * 获取秒杀活动列表 + * 获取限时活动列表 */ @GetMapping("/flashsales") - @Operation(summary = "获取秒杀活动列表") + @Operation(summary = "获取限时活动列表") public ResponseEntity> getFlashSales() { Map response = new HashMap<>(); @@ -220,9 +220,9 @@ public class ApiController { response.put("total", flashSales.size()); } catch (Exception e) { - log.error("获取秒杀活动列表失败", e); + log.error("获取限时活动列表失败", e); response.put("success", false); - response.put("message", "获取秒杀活动列表失败"); + response.put("message", "获取限时活动列表失败"); } return ResponseEntity.ok(response); diff --git a/src/main/java/com/org/flashsalesystem/controller/FlashSaleController.java b/src/main/java/com/org/flashsalesystem/controller/FlashSaleController.java index 95f916f..0939d96 100644 --- a/src/main/java/com/org/flashsalesystem/controller/FlashSaleController.java +++ b/src/main/java/com/org/flashsalesystem/controller/FlashSaleController.java @@ -22,10 +22,10 @@ import java.util.List; import java.util.Map; /** - * 秒杀控制器 - * 处理秒杀相关的HTTP请求 + * 限时活动控制器 + * 处理限时活动相关的HTTP请求 */ -@Tag(name = "秒杀管理", description = "秒杀活动创建、参与、状态管理等接口") +@Tag(name = "限时活动管理", description = "限时活动创建、参与、状态管理等接口") @RestController @RequestMapping("/api/flashsale") @Slf4j @@ -38,11 +38,11 @@ public class FlashSaleController { private UserService userService; /** - * 创建秒杀活动 + * 创建限时活动 */ - @Operation(summary = "创建秒杀活动", description = "创建新的秒杀活动") + @Operation(summary = "创建限时活动", description = "创建新的限时活动") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "秒杀活动创建成功"), + @ApiResponse(responseCode = "200", description = "限时活动创建成功"), @ApiResponse(responseCode = "400", description = "创建失败,参数验证错误") }) @PostMapping("/create") @@ -52,12 +52,12 @@ public class FlashSaleController { Map response = new HashMap<>(); response.put("success", true); - response.put("message", "秒杀活动创建成功"); + response.put("message", "限时活动创建成功"); response.put("data", flashSale); return ResponseEntity.ok(response); } catch (Exception e) { - log.error("创建秒杀活动失败", e); + log.error("创建限时活动失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -68,7 +68,7 @@ public class FlashSaleController { } /** - * 参与秒杀 + * 参与限时活动 */ @PostMapping("/participate") public ResponseEntity> participateFlashSale(@Validated @RequestBody FlashSaleDTO.ParticipateDTO participateDTO, @@ -92,7 +92,7 @@ public class FlashSaleController { return ResponseEntity.badRequest().body(response); } } catch (Exception e) { - log.error("参与秒杀失败", e); + log.error("参与限时活动失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -103,7 +103,7 @@ public class FlashSaleController { } /** - * 获取秒杀活动列表 + * 获取限时活动列表 */ @PostMapping("/list") public ResponseEntity> getFlashSaleList(@RequestBody FlashSaleDTO.QueryDTO queryDTO) { @@ -116,7 +116,7 @@ public class FlashSaleController { return ResponseEntity.ok(response); } catch (Exception e) { - log.error("获取秒杀活动列表失败", e); + log.error("获取限时活动列表失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -127,7 +127,7 @@ public class FlashSaleController { } /** - * 获取正在进行的秒杀活动 + * 获取正在进行的限时活动 */ @GetMapping("/active") public ResponseEntity> getActiveFlashSales() { @@ -140,7 +140,7 @@ public class FlashSaleController { return ResponseEntity.ok(response); } catch (Exception e) { - log.error("获取正在进行的秒杀活动失败", e); + log.error("获取正在进行的限时活动失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -151,7 +151,7 @@ public class FlashSaleController { } /** - * 获取秒杀活动统计信息 + * 获取限时活动统计信息 */ @GetMapping("/statistics") public ResponseEntity> getFlashSaleStatistics(HttpServletRequest request) { @@ -165,7 +165,7 @@ public class FlashSaleController { return ResponseEntity.ok(response); } catch (Exception e) { - log.error("获取秒杀统计失败", e); + log.error("获取限时活动统计失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -176,18 +176,18 @@ public class FlashSaleController { } /** - * 获取秒杀活动详情 + * 获取限时活动详情 */ - @Operation(summary = "获取秒杀活动详情", description = "根据ID获取秒杀活动的详细信息") + @Operation(summary = "获取限时活动详情", description = "根据ID获取限时活动的详细信息") @GetMapping("/{id}") - public ResponseEntity> getFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) { + public ResponseEntity> getFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) { try { FlashSaleDTO flashSale = flashSaleService.getFlashSaleDTOById(id); if (flashSale == null) { Map response = new HashMap<>(); response.put("success", false); - response.put("message", "秒杀活动不存在"); + response.put("message", "限时活动不存在"); return ResponseEntity.notFound().build(); } @@ -197,7 +197,7 @@ public class FlashSaleController { return ResponseEntity.ok(response); } catch (Exception e) { - log.error("获取秒杀活动详情失败", e); + log.error("获取限时活动详情失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -208,7 +208,7 @@ public class FlashSaleController { } /** - * 预热所有秒杀活动库存(管理员功能) + * 预热所有限时活动库存(管理员功能) */ @PostMapping("/admin/preload-all") public ResponseEntity> preloadAllFlashSales() { @@ -217,11 +217,11 @@ public class FlashSaleController { Map response = new HashMap<>(); response.put("success", true); - response.put("message", "所有秒杀活动库存预热完成"); + response.put("message", "所有限时活动库存预热完成"); return ResponseEntity.ok(response); } catch (Exception e) { - log.error("预热所有秒杀活动库存失败", e); + log.error("预热所有限时活动库存失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -232,28 +232,28 @@ public class FlashSaleController { } /** - * 更新秒杀活动 + * 更新限时活动 */ - @Operation(summary = "更新秒杀活动", description = "更新秒杀活动信息") + @Operation(summary = "更新限时活动", description = "更新限时活动信息") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "秒杀活动更新成功"), + @ApiResponse(responseCode = "200", description = "限时活动更新成功"), @ApiResponse(responseCode = "400", description = "更新失败,参数验证错误"), - @ApiResponse(responseCode = "404", description = "秒杀活动不存在") + @ApiResponse(responseCode = "404", description = "限时活动不存在") }) @PutMapping("/{id}") - public ResponseEntity> updateFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id, + public ResponseEntity> updateFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id, @Validated @RequestBody FlashSaleDTO.UpdateDTO updateDTO) { try { FlashSaleDTO flashSale = flashSaleService.updateFlashSale(id, updateDTO); Map response = new HashMap<>(); response.put("success", true); - response.put("message", "秒杀活动更新成功"); + response.put("message", "限时活动更新成功"); response.put("data", flashSale); return ResponseEntity.ok(response); } catch (Exception e) { - log.error("更新秒杀活动失败", e); + log.error("更新限时活动失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -264,26 +264,26 @@ public class FlashSaleController { } /** - * 删除秒杀活动 + * 删除限时活动 */ - @Operation(summary = "删除秒杀活动", description = "删除指定的秒杀活动") + @Operation(summary = "删除限时活动", description = "删除指定的限时活动") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "秒杀活动删除成功"), + @ApiResponse(responseCode = "200", description = "限时活动删除成功"), @ApiResponse(responseCode = "400", description = "删除失败"), - @ApiResponse(responseCode = "404", description = "秒杀活动不存在") + @ApiResponse(responseCode = "404", description = "限时活动不存在") }) @DeleteMapping("/{id}") - public ResponseEntity> deleteFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) { + public ResponseEntity> deleteFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) { try { boolean success = flashSaleService.deleteFlashSale(id); Map response = new HashMap<>(); response.put("success", success); - response.put("message", success ? "秒杀活动删除成功" : "秒杀活动删除失败"); + response.put("message", success ? "限时活动删除成功" : "限时活动删除失败"); return ResponseEntity.ok(response); } catch (Exception e) { - log.error("删除秒杀活动失败", e); + log.error("删除限时活动失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -294,7 +294,7 @@ public class FlashSaleController { } /** - * 获取秒杀活动剩余库存 + * 获取限时活动剩余库存 */ @GetMapping("/{id}/stock") public ResponseEntity> getFlashSaleStock(@PathVariable Long id) { @@ -310,7 +310,7 @@ public class FlashSaleController { return ResponseEntity.ok(response); } catch (Exception e) { - log.error("获取秒杀活动库存失败", e); + log.error("获取限时活动库存失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -321,7 +321,7 @@ public class FlashSaleController { } /** - * 预热秒杀活动 + * 预热限时活动 */ @PostMapping("/{id}/preload") public ResponseEntity> preloadFlashSale(@PathVariable Long id) { @@ -330,11 +330,11 @@ public class FlashSaleController { Map response = new HashMap<>(); response.put("success", true); - response.put("message", "秒杀活动预热成功"); + response.put("message", "限时活动预热成功"); return ResponseEntity.ok(response); } catch (Exception e) { - log.error("预热秒杀活动失败", e); + log.error("预热限时活动失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -345,7 +345,7 @@ public class FlashSaleController { } /** - * 更新秒杀活动状态(定时任务调用) + * 更新限时活动状态(定时任务调用) */ @PostMapping("/update-status") public ResponseEntity> updateFlashSaleStatus() { @@ -354,11 +354,11 @@ public class FlashSaleController { Map response = new HashMap<>(); response.put("success", true); - response.put("message", "秒杀活动状态更新成功"); + response.put("message", "限时活动状态更新成功"); return ResponseEntity.ok(response); } catch (Exception e) { - log.error("更新秒杀活动状态失败", e); + log.error("更新限时活动状态失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -369,22 +369,22 @@ public class FlashSaleController { } /** - * 发布秒杀活动 + * 发布限时活动 */ - @Operation(summary = "发布秒杀活动", description = "将秒杀活动状态设置为可参与") + @Operation(summary = "发布限时活动", description = "将限时活动状态设置为可参与") @PostMapping("/{id}/publish") - public ResponseEntity> publishFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) { + public ResponseEntity> publishFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) { try { FlashSaleDTO flashSale = flashSaleService.publishFlashSale(id); Map response = new HashMap<>(); response.put("success", true); - response.put("message", "秒杀活动发布成功"); + response.put("message", "限时活动发布成功"); response.put("data", flashSale); return ResponseEntity.ok(response); } catch (Exception e) { - log.error("发布秒杀活动失败", e); + log.error("发布限时活动失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -395,22 +395,22 @@ public class FlashSaleController { } /** - * 暂停秒杀活动 + * 暂停限时活动 */ - @Operation(summary = "暂停秒杀活动", description = "暂停正在进行的秒杀活动") + @Operation(summary = "暂停限时活动", description = "暂停正在进行的限时活动") @PostMapping("/{id}/pause") - public ResponseEntity> pauseFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) { + public ResponseEntity> pauseFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) { try { FlashSaleDTO flashSale = flashSaleService.pauseFlashSale(id); Map response = new HashMap<>(); response.put("success", true); - response.put("message", "秒杀活动暂停成功"); + response.put("message", "限时活动暂停成功"); response.put("data", flashSale); return ResponseEntity.ok(response); } catch (Exception e) { - log.error("暂停秒杀活动失败", e); + log.error("暂停限时活动失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -421,22 +421,22 @@ public class FlashSaleController { } /** - * 恢复秒杀活动 + * 恢复限时活动 */ - @Operation(summary = "恢复秒杀活动", description = "恢复已暂停的秒杀活动") + @Operation(summary = "恢复限时活动", description = "恢复已暂停的限时活动") @PostMapping("/{id}/resume") - public ResponseEntity> resumeFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) { + public ResponseEntity> resumeFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) { try { FlashSaleDTO flashSale = flashSaleService.resumeFlashSale(id); Map response = new HashMap<>(); response.put("success", true); - response.put("message", "秒杀活动恢复成功"); + response.put("message", "限时活动恢复成功"); response.put("data", flashSale); return ResponseEntity.ok(response); } catch (Exception e) { - log.error("恢复秒杀活动失败", e); + log.error("恢复限时活动失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -447,22 +447,22 @@ public class FlashSaleController { } /** - * 结束秒杀活动 + * 结束限时活动 */ - @Operation(summary = "结束秒杀活动", description = "提前结束秒杀活动") + @Operation(summary = "结束限时活动", description = "提前结束限时活动") @PostMapping("/{id}/end") - public ResponseEntity> endFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) { + public ResponseEntity> endFlashSale(@Parameter(description = "限时活动ID", required = true) @PathVariable Long id) { try { FlashSaleDTO flashSale = flashSaleService.endFlashSale(id); Map response = new HashMap<>(); response.put("success", true); - response.put("message", "秒杀活动结束成功"); + response.put("message", "限时活动结束成功"); response.put("data", flashSale); return ResponseEntity.ok(response); } catch (Exception e) { - log.error("结束秒杀活动失败", e); + log.error("结束限时活动失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -473,7 +473,7 @@ public class FlashSaleController { } /** - * 秒杀压力测试接口 + * 限时活动压力测试接口 */ @PostMapping("/stress-test") public ResponseEntity> stressTest(@RequestParam Long flashSaleId, @@ -486,7 +486,7 @@ public class FlashSaleController { } // 这里可以实现压力测试逻辑 - // 模拟多个用户同时参与秒杀 + // 模拟多个用户同时参与限时活动 Map response = new HashMap<>(); response.put("success", true); @@ -498,7 +498,7 @@ public class FlashSaleController { return ResponseEntity.ok(response); } catch (Exception e) { - log.error("秒杀压力测试失败", e); + log.error("限时活动压力测试失败", e); Map response = new HashMap<>(); response.put("success", false); @@ -509,10 +509,10 @@ public class FlashSaleController { } /** - * 修复秒杀活动库存 + * 修复限时活动库存 */ @PostMapping("/{id}/repair-stock") - @Operation(summary = "修复秒杀库存", description = "修复指定秒杀活动的Redis库存数据") + @Operation(summary = "修复活动库存", description = "修复指定限时活动的Redis库存数据") public ResponseEntity> repairFlashSaleStock(@PathVariable Long id) { try { flashSaleService.repairFlashSaleStock(id); @@ -523,7 +523,7 @@ public class FlashSaleController { return ResponseEntity.ok(response); } catch (Exception e) { - log.error("修复秒杀库存失败", e); + log.error("修复活动库存失败", e); Map response = new HashMap<>(); response.put("success", false); diff --git a/src/main/java/com/org/flashsalesystem/controller/PageController.java b/src/main/java/com/org/flashsalesystem/controller/PageController.java index 958133c..21fa08c 100644 --- a/src/main/java/com/org/flashsalesystem/controller/PageController.java +++ b/src/main/java/com/org/flashsalesystem/controller/PageController.java @@ -81,20 +81,20 @@ public class PageController { } /** - * 秒杀活动列表页面 + * 限时活动列表页面 */ @GetMapping("/flashsales") public String flashSales(Model model) { - model.addAttribute("pageTitle", "秒杀活动"); + model.addAttribute("pageTitle", "限时活动"); return "flashsales"; } /** - * 秒杀活动详情页面 + * 限时活动详情页面 */ @GetMapping("/flashsale/{id}") public String flashSaleDetail(@PathVariable Long id, Model model) { - model.addAttribute("pageTitle", "秒杀详情"); + model.addAttribute("pageTitle", "限时活动详情"); model.addAttribute("flashSaleId", id); return "flashsale-detail"; } @@ -179,7 +179,7 @@ public class PageController { } /** - * 秒杀活动管理页面 + * 限时活动管理页面 */ @GetMapping("/admin/flashsales") public String adminFlashSales(Model model, HttpServletRequest request) { @@ -187,7 +187,7 @@ public class PageController { return "redirect:/login?returnUrl=/admin/flashsales"; } - model.addAttribute("pageTitle", "秒杀管理"); + model.addAttribute("pageTitle", "限时活动管理"); return "admin/flashsales"; } diff --git a/src/main/java/com/org/flashsalesystem/dto/FlashSaleDTO.java b/src/main/java/com/org/flashsalesystem/dto/FlashSaleDTO.java index b75611d..62c866e 100644 --- a/src/main/java/com/org/flashsalesystem/dto/FlashSaleDTO.java +++ b/src/main/java/com/org/flashsalesystem/dto/FlashSaleDTO.java @@ -13,7 +13,7 @@ import java.math.BigDecimal; import java.time.LocalDateTime; /** - * 秒杀活动数据传输对象 + * 限时活动数据传输对象 */ @Data @NoArgsConstructor @@ -42,7 +42,7 @@ public class FlashSaleDTO { // 活动状态描述 private String statusDescription; - // 是否可以参与秒杀 + // 是否可以参与限时活动 private Boolean canParticipate; // 距离开始时间(毫秒) private Long timeToStart; @@ -50,7 +50,7 @@ public class FlashSaleDTO { private Long timeToEnd; /** - * 创建秒杀活动DTO + * 创建限时活动DTO */ @Data @NoArgsConstructor @@ -59,11 +59,11 @@ public class FlashSaleDTO { @NotNull(message = "商品ID不能为空") private Long productId; - @NotNull(message = "秒杀价格不能为空") - @DecimalMin(value = "0.01", message = "秒杀价格必须大于0") + @NotNull(message = "活动价格不能为空") + @DecimalMin(value = "0.01", message = "活动价格必须大于0") private BigDecimal flashPrice; - @Min(value = 1, message = "秒杀库存必须大于0") + @Min(value = 1, message = "活动库存必须大于0") private Integer flashStock; @NotNull(message = "开始时间不能为空") @@ -76,16 +76,16 @@ public class FlashSaleDTO { } /** - * 更新秒杀活动DTO + * 更新限时活动DTO */ @Data @NoArgsConstructor @AllArgsConstructor public static class UpdateDTO { - @DecimalMin(value = "0.01", message = "秒杀价格必须大于0") + @DecimalMin(value = "0.01", message = "活动价格必须大于0") private BigDecimal flashPrice; - @Min(value = 1, message = "秒杀库存必须大于0") + @Min(value = 1, message = "活动库存必须大于0") private Integer flashStock; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @@ -100,13 +100,13 @@ public class FlashSaleDTO { } /** - * 秒杀参与DTO + * 限时活动参与DTO */ @Data @NoArgsConstructor @AllArgsConstructor public static class ParticipateDTO { - @NotNull(message = "秒杀活动ID不能为空") + @NotNull(message = "限时活动ID不能为空") private Long flashSaleId; @Min(value = 1, message = "购买数量必须大于0") @@ -118,7 +118,7 @@ public class FlashSaleDTO { } /** - * 秒杀查询DTO + * 限时活动查询DTO */ @Data @NoArgsConstructor @@ -139,7 +139,7 @@ public class FlashSaleDTO { } /** - * 秒杀结果DTO + * 限时活动结果DTO */ @Data @NoArgsConstructor diff --git a/src/main/java/com/org/flashsalesystem/entity/FlashSale.java b/src/main/java/com/org/flashsalesystem/entity/FlashSale.java index dbb7def..1dd3e90 100644 --- a/src/main/java/com/org/flashsalesystem/entity/FlashSale.java +++ b/src/main/java/com/org/flashsalesystem/entity/FlashSale.java @@ -12,7 +12,7 @@ import java.math.BigDecimal; import java.time.LocalDateTime; /** - * 秒杀活动实体类 + * 限时活动实体类 * 对应数据库flash_sales表 */ @Entity @@ -30,12 +30,12 @@ public class FlashSale { @Column(name = "product_id", nullable = false) private Long productId; - @NotNull(message = "秒杀价格不能为空") - @DecimalMin(value = "0.01", message = "秒杀价格必须大于0") + @NotNull(message = "活动价格不能为空") + @DecimalMin(value = "0.01", message = "活动价格必须大于0") @Column(name = "flash_price", nullable = false, precision = 10, scale = 2) private BigDecimal flashPrice; - @Min(value = 1, message = "秒杀库存必须大于0") + @Min(value = 1, message = "活动库存必须大于0") @Column(name = "flash_stock", nullable = false) private Integer flashStock; diff --git a/src/main/java/com/org/flashsalesystem/entity/Order.java b/src/main/java/com/org/flashsalesystem/entity/Order.java index c83660c..85bc06b 100644 --- a/src/main/java/com/org/flashsalesystem/entity/Order.java +++ b/src/main/java/com/org/flashsalesystem/entity/Order.java @@ -62,7 +62,7 @@ public class Order { private Integer status = 1; /** - * 订单类型:1-普通订单,2-秒杀订单 + * 订单类型:1-普通订单,2-限时订单 */ @Column(name = "order_type", nullable = false) private Integer orderType = 1; @@ -153,7 +153,7 @@ public class Order { */ public enum OrderType { NORMAL(1, "普通订单"), - FLASH_SALE(2, "秒杀订单"), + FLASH_SALE(2, "限时订单"), GROUP_BUYING(3, "拼团订单"); private final int code; diff --git a/src/main/java/com/org/flashsalesystem/repository/FlashSaleRepository.java b/src/main/java/com/org/flashsalesystem/repository/FlashSaleRepository.java index cf4873b..84fd909 100644 --- a/src/main/java/com/org/flashsalesystem/repository/FlashSaleRepository.java +++ b/src/main/java/com/org/flashsalesystem/repository/FlashSaleRepository.java @@ -13,71 +13,71 @@ import java.time.LocalDateTime; import java.util.List; /** - * 秒杀活动数据访问层 + * 限时活动数据访问层 */ @Repository public interface FlashSaleRepository extends JpaRepository { /** - * 分页查找指定商品的秒杀活动 + * 分页查找指定商品的限时活动 */ Page findByProductId(Long productId, Pageable pageable); /** - * 查找指定时间点覆盖的商品秒杀活动 + * 查找指定时间点覆盖的商品限时活动 */ @Query("SELECT f FROM FlashSale f WHERE f.productId = :productId AND f.startTime <= :targetTime AND f.endTime >= :targetTime") List findByProductIdAndCoveringTime(@Param("productId") Long productId, @Param("targetTime") LocalDateTime targetTime); /** - * 根据商品ID和状态查找秒杀活动 + * 根据商品ID和状态查找限时活动 */ Page findByProductIdAndStatus(Long productId, Integer status, Pageable pageable); /** - * 查找正在进行的秒杀活动 + * 查找正在进行的限时活动 */ @Query("SELECT f FROM FlashSale f WHERE f.startTime <= :now AND f.endTime > :now AND f.status = 2") List findActiveFlashSales(@Param("now") LocalDateTime now); /** - * 分页查找正在进行的秒杀活动 + * 分页查找正在进行的限时活动 */ @Query("SELECT f FROM FlashSale f WHERE f.startTime <= :now AND f.endTime > :now AND f.status = 2") Page findActiveFlashSales(@Param("now") LocalDateTime now, Pageable pageable); /** - * 查找即将开始的秒杀活动 + * 查找即将开始的限时活动 */ @Query("SELECT f FROM FlashSale f WHERE f.startTime > :now AND f.status = 1") List findUpcomingFlashSales(@Param("now") LocalDateTime now); /** - * 分页查找即将开始的秒杀活动 + * 分页查找即将开始的限时活动 */ @Query("SELECT f FROM FlashSale f WHERE f.startTime > :now AND f.status = 1") Page findUpcomingFlashSales(@Param("now") LocalDateTime now, Pageable pageable); /** - * 查找已结束的秒杀活动 + * 查找已结束的限时活动 */ @Query("SELECT f FROM FlashSale f WHERE f.endTime <= :now OR f.status = 3") List findEndedFlashSales(@Param("now") LocalDateTime now); /** - * 分页查找已结束的秒杀活动 + * 分页查找已结束的限时活动 */ @Query("SELECT f FROM FlashSale f WHERE f.endTime <= :now OR f.status = 3") Page findEndedFlashSales(@Param("now") LocalDateTime now, Pageable pageable); /** - * 按状态分页查找秒杀活动 + * 按状态分页查找限时活动 */ Page findByStatus(Integer status, Pageable pageable); /** - * 更新秒杀库存 + * 更新活动库存 */ @Modifying @Query("UPDATE FlashSale f SET f.flashStock = f.flashStock - :quantity WHERE f.id = :flashSaleId AND f.flashStock" + @@ -85,50 +85,50 @@ public interface FlashSaleRepository extends JpaRepository { int updateFlashStock(@Param("flashSaleId") Long flashSaleId, @Param("quantity") Integer quantity); /** - * 恢复秒杀库存(订单取消时使用) + * 恢复活动库存(订单取消时使用) */ @Modifying @Query("UPDATE FlashSale f SET f.flashStock = f.flashStock + :quantity WHERE f.id = :flashSaleId") int increaseFlashStock(@Param("flashSaleId") Long flashSaleId, @Param("quantity") Integer quantity); /** - * 更新秒杀活动状态 + * 更新限时活动状态 */ @Modifying @Query("UPDATE FlashSale f SET f.status = :status WHERE f.id = :flashSaleId") int updateStatus(@Param("flashSaleId") Long flashSaleId, @Param("status") Integer status); /** - * 查找指定时间范围内的秒杀活动 + * 查找指定时间范围内的限时活动 */ @Query("SELECT f FROM FlashSale f WHERE f.startTime >= :startTime AND f.endTime <= :endTime") List findByTimeRange(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime); /** - * 查找有库存的正在进行的秒杀活动 + * 查找有库存的正在进行的限时活动 */ @Query("SELECT f FROM FlashSale f WHERE f.startTime <= :now AND f.endTime > :now AND f.status = 2 AND f" + ".flashStock > 0") List findActiveFlashSalesWithStock(@Param("now") LocalDateTime now); /** - * 根据商品ID查找所有秒杀活动 + * 根据商品ID查找所有限时活动 */ List findByProductId(Long productId); /** - * 统计指定时间范围内正在进行的秒杀活动数量 + * 统计指定时间范围内正在进行的限时活动数量 */ long countByStartTimeLessThanEqualAndEndTimeGreaterThanEqual(LocalDateTime startTime, LocalDateTime endTime); /** - * 统计指定时间范围内的秒杀活动数量 + * 统计指定时间范围内的限时活动数量 */ long countByStartTimeBetween(LocalDateTime startTime, LocalDateTime endTime); /** - * 统计已结束的秒杀活动数量 + * 统计已结束的限时活动数量 */ long countByEndTimeLessThan(LocalDateTime endTime); } diff --git a/src/main/java/com/org/flashsalesystem/repository/OrderRepository.java b/src/main/java/com/org/flashsalesystem/repository/OrderRepository.java index 8e8ce11..3fbba03 100644 --- a/src/main/java/com/org/flashsalesystem/repository/OrderRepository.java +++ b/src/main/java/com/org/flashsalesystem/repository/OrderRepository.java @@ -68,7 +68,7 @@ public interface OrderRepository extends JpaRepository { Long countByUserIdAndOrderTypeAndStatusNot5(@Param("userId") Long userId, @Param("orderType") Integer orderType); /** - * 查找秒杀订单 + * 查找限时订单 */ @Query("SELECT o FROM Order o WHERE o.orderType = 2") List findFlashSaleOrders(); @@ -128,18 +128,18 @@ public interface OrderRepository extends JpaRepository { List findByUserIdAndProductId(@Param("userId") Long userId, @Param("productId") Long productId); /** - * 查找用户的秒杀订单 + * 查找用户的限时订单 */ @Query("SELECT o FROM Order o WHERE o.userId = :userId AND o.orderType = 2") List findFlashSaleOrdersByUserId(@Param("userId") Long userId); /** - * 检查用户是否已经参与过指定秒杀活动 + * 检查用户是否已经参与过指定限时活动 */ boolean existsByUserIdAndFlashSaleIdAndOrderType(Long userId, Long flashSaleId, Integer orderType); /** - * 检查指定秒杀活动是否已有订单 + * 检查指定限时活动是否已有订单 */ boolean existsByFlashSaleIdAndOrderType(Long flashSaleId, Integer orderType); diff --git a/src/main/java/com/org/flashsalesystem/service/AdminService.java b/src/main/java/com/org/flashsalesystem/service/AdminService.java index 8e1469b..9ddef8a 100644 --- a/src/main/java/com/org/flashsalesystem/service/AdminService.java +++ b/src/main/java/com/org/flashsalesystem/service/AdminService.java @@ -96,7 +96,7 @@ public class AdminService { long totalProducts = productRepository.count(); stats.put("totalProducts", totalProducts); - // 活跃秒杀数 + // 活跃限时活动数 LocalDateTime now = LocalDateTime.now(); long activeFlashSales = flashSaleRepository.countByStartTimeLessThanEqualAndEndTimeGreaterThanEqual(now, now); @@ -272,7 +272,7 @@ public class AdminService { } /** - * 获取秒杀统计数据 + * 获取限时活动统计数据 */ public Map getFlashSaleStats() { Map stats = new HashMap<>(); @@ -280,26 +280,26 @@ public class AdminService { try { LocalDateTime now = LocalDateTime.now(); - // 总秒杀活动数 + // 总限时活动数 long totalFlashSales = flashSaleRepository.count(); stats.put("totalFlashSales", totalFlashSales); - // 活跃秒杀数 + // 活跃限时活动数 long activeFlashSales = flashSaleRepository.countByStartTimeLessThanEqualAndEndTimeGreaterThanEqual(now, now); stats.put("activeFlashSales", activeFlashSales); - // 即将开始的秒杀数 + // 即将开始的限时活动数 LocalDateTime oneHourLater = now.plusHours(1); long upcomingFlashSales = flashSaleRepository.countByStartTimeBetween(now, oneHourLater); stats.put("upcomingFlashSales", upcomingFlashSales); - // 已结束的秒杀数 + // 已结束的限时活动数 long endedFlashSales = flashSaleRepository.countByEndTimeLessThan(now); stats.put("endedFlashSales", endedFlashSales); } catch (Exception e) { - log.error("获取秒杀统计数据失败", e); + log.error("获取限时活动统计数据失败", e); stats.put("totalFlashSales", 0L); stats.put("activeFlashSales", 0L); stats.put("upcomingFlashSales", 0L); @@ -327,7 +327,7 @@ public class AdminService { orderMap.put("status", order.getStatus()); orderMap.put("createdAt", order.getCreatedAt()); orderMap.put("orderType", order.getOrderType()); - orderMap.put("isFlashSale", order.getOrderType() == 2); // 2表示秒杀订单 + orderMap.put("isFlashSale", order.getOrderType() == 2); // 2表示限时订单 return orderMap; }).collect(Collectors.toList()); @@ -502,7 +502,7 @@ public class AdminService { orderMap.put("status", order.getStatus()); orderMap.put("createdAt", order.getCreatedAt()); orderMap.put("orderType", order.getOrderType()); - orderMap.put("isFlashSale", order.getOrderType() == 2); // 2表示秒杀订单 + orderMap.put("isFlashSale", order.getOrderType() == 2); // 2表示限时订单 return orderMap; }).collect(Collectors.toList()); @@ -753,7 +753,7 @@ public class AdminService { fileUploadService.deleteProductImage(oldImageUrl); } - // 清除引用该商品的秒杀活动缓存,确保图片/名称/价格更新及时生效 + // 清除引用该商品的限时活动缓存,确保图片/名称/价格更新及时生效 invalidateFlashSaleCacheByProductId(id); } else { throw new RuntimeException("商品不存在"); @@ -765,7 +765,7 @@ public class AdminService { } /** - * 清除引用指定商品的所有秒杀活动缓存 + * 清除引用指定商品的所有限时活动缓存 */ private void invalidateFlashSaleCacheByProductId(Long productId) { try { @@ -774,10 +774,10 @@ public class AdminService { for (com.org.flashsalesystem.entity.FlashSale fs : flashSales) { String cacheKey = "flashsale:" + fs.getId(); redisService.delete(cacheKey); - log.debug("清除秒杀活动缓存: {}", cacheKey); + log.debug("清除限时活动缓存: {}", cacheKey); } } catch (Exception e) { - log.warn("清除秒杀活动缓存失败: {}", e.getMessage()); + log.warn("清除限时活动缓存失败: {}", e.getMessage()); } } @@ -793,7 +793,7 @@ public class AdminService { fileUploadService.deleteProductImage(product.getImageUrl()); productService.invalidateProductCaches(id); productService.removeProductStockCache(id); - // 清除关联的秒杀活动缓存 + // 清除关联的限时活动缓存 invalidateFlashSaleCacheByProductId(id); productRepository.deleteById(id); } else { diff --git a/src/main/java/com/org/flashsalesystem/service/FlashSaleService.java b/src/main/java/com/org/flashsalesystem/service/FlashSaleService.java index 3d2f857..52c5743 100644 --- a/src/main/java/com/org/flashsalesystem/service/FlashSaleService.java +++ b/src/main/java/com/org/flashsalesystem/service/FlashSaleService.java @@ -30,8 +30,8 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** - * 秒杀服务类 - * 实现秒杀活动管理、库存控制、分布式锁等核心功能 + * 限时活动服务类 + * 实现限时活动管理、库存控制、分布式锁等核心功能 */ @Service @Slf4j @@ -64,11 +64,11 @@ public class FlashSaleService { private int maxQuantityPerUser; /** - * 创建秒杀活动 + * 创建限时活动 */ @Transactional public FlashSaleDTO createFlashSale(FlashSaleDTO.CreateDTO createDTO) { - log.info("创建秒杀活动: 商品ID={}, 秒杀价格={}, 库存={}", + log.info("创建限时活动: 商品ID={}, 活动价格={}, 库存={}", createDTO.getProductId(), createDTO.getFlashPrice(), createDTO.getFlashStock()); // 验证商品是否存在 @@ -87,14 +87,14 @@ public class FlashSaleService { throw new RuntimeException("结束时间必须晚于开始时间"); } - // 创建秒杀活动 + // 创建限时活动 FlashSale flashSale = new FlashSale(); BeanUtils.copyProperties(createDTO, flashSale); flashSale.setStatus(1); // 未开始 flashSale = flashSaleRepository.save(flashSale); - // 缓存秒杀活动信息 + // 缓存限时活动信息 cacheFlashSaleInfo(flashSale, product); // 预热库存到Redis - 确保存储为数字类型 @@ -104,7 +104,7 @@ public class FlashSaleService { // 验证库存设置是否成功 String verifyStock = redisService.getString(stockKey); - log.info("秒杀活动库存初始化验证: flashSaleId={}, stockKey={}, setStock={}, actualStock={}", + log.info("限时活动库存初始化验证: flashSaleId={}, stockKey={}, setStock={}, actualStock={}", flashSale.getId(), stockKey, flashSale.getFlashStock(), verifyStock); if (!flashSale.getFlashStock().toString().equals(verifyStock)) { @@ -112,17 +112,17 @@ public class FlashSaleService { flashSale.getId(), flashSale.getFlashStock(), verifyStock); } - log.info("秒杀活动创建成功: ID={}", flashSale.getId()); + log.info("限时活动创建成功: ID={}", flashSale.getId()); return buildFlashSaleDTO(flashSale, product); } /** - * 参与秒杀 + * 参与限时活动 */ @Transactional public FlashSaleDTO.ResultDTO participateFlashSale(Long userId, FlashSaleDTO.ParticipateDTO participateDTO) { - log.info("用户参与秒杀: 用户ID={}, 秒杀ID={}, 数量={}", + log.info("用户参与限时活动: 用户ID={}, 限时活动ID={}, 数量={}", userId, participateDTO.getFlashSaleId(), participateDTO.getQuantity()); // 限流检查 @@ -130,26 +130,26 @@ public class FlashSaleService { return createFailResult("请求过于频繁,请稍后再试"); } - // 获取秒杀活动信息 + // 获取限时活动信息 FlashSale flashSale = getFlashSaleById(participateDTO.getFlashSaleId()); if (flashSale == null) { - return createFailResult("秒杀活动不存在"); + return createFailResult("限时活动不存在"); } // 检查活动状态 if (!flashSale.isActive()) { - return createFailResult("秒杀活动未开始或已结束"); + return createFailResult("限时活动未开始或已结束"); } // 检查用户是否已经参与过 String successUsersKey = FLASH_SALE_SUCCESS_USERS_PREFIX + flashSale.getId(); if (redisService.sIsMember(successUsersKey, userId)) { - return createFailResult("您已经参与过该秒杀活动"); + return createFailResult("您已经参与过该限时活动"); } // 检查数据库中是否已有订单 if (orderRepository.existsByUserIdAndFlashSaleIdAndOrderType(userId, flashSale.getId(), 2)) { - return createFailResult("您已经参与过该秒杀活动"); + return createFailResult("您已经参与过该限时活动"); } // 检查购买数量限制 @@ -167,16 +167,16 @@ public class FlashSaleService { try { // 二次校验:锁内重新检查用户是否已参与(防止并发竞态) if (redisService.sIsMember(successUsersKey, userId)) { - return createFailResult("您已经参与过该秒杀活动"); + return createFailResult("您已经参与过该限时活动"); } if (orderRepository.existsByUserIdAndFlashSaleIdAndOrderType(userId, flashSale.getId(), 2)) { - return createFailResult("您已经参与过该秒杀活动"); + return createFailResult("您已经参与过该限时活动"); } // 检查并修复库存数据 String stockKey = FLASH_SALE_STOCK_PREFIX + flashSale.getId(); String currentStock = redisService.getString(stockKey); - log.info("秒杀前库存检查: flashSaleId={}, stockKey={}, currentStock={}", + log.info("限时活动前库存检查: flashSaleId={}, stockKey={}, currentStock={}", flashSale.getId(), stockKey, currentStock); if (currentStock == null || currentStock.trim().isEmpty()) { @@ -203,18 +203,18 @@ public class FlashSaleService { currentStock = redisService.getString(stockKey); if (currentStock == null || currentStock.trim().isEmpty()) { log.error("库存数据最终验证失败: flashSaleId={}, stockKey={}", flashSale.getId(), stockKey); - return createFailResult("秒杀活动库存初始化失败,请稍后重试"); + return createFailResult("限时活动库存初始化失败,请稍后重试"); } // 使用Lua脚本原子性扣减库存 - log.info("准备执行秒杀脚本: stockKey={}, quantity={}, userId={}, currentStock={}", + log.info("准备执行活动脚本: stockKey={}, quantity={}, userId={}, currentStock={}", stockKey, participateDTO.getQuantity(), userId, currentStock); Long remainingStock = redisService.executeFlashSaleScript(stockKey, participateDTO.getQuantity()); - log.info("秒杀脚本执行完成: stockKey={}, remainingStock={}", stockKey, remainingStock); + log.info("活动脚本执行完成: stockKey={}, remainingStock={}", stockKey, remainingStock); if (remainingStock < 0) { if (remainingStock == -1) { - log.warn("秒杀库存key不存在或数据异常: flashSaleId={}, stockKey={}", flashSale.getId(), stockKey); + log.warn("活动库存key不存在或数据异常: flashSaleId={}, stockKey={}", flashSale.getId(), stockKey); // 自动修复数据并重试一次 log.info("开始自动修复库存数据: flashSaleId={}", flashSale.getId()); @@ -224,10 +224,10 @@ public class FlashSaleService { // 修复后重新验证库存 String repairedStock = redisService.getString(stockKey); if (repairedStock != null && !repairedStock.trim().isEmpty()) { - log.info("库存修复成功,重新执行秒杀脚本: flashSaleId={}, stock={}", + log.info("库存修复成功,重新执行活动脚本: flashSaleId={}, stock={}", flashSale.getId(), repairedStock); - // 重新执行秒杀脚本 + // 重新执行活动脚本 Long retryResult = redisService.executeFlashSaleScript(stockKey, participateDTO.getQuantity()); log.info("修复后重试结果: flashSaleId={}, result={}", flashSale.getId(), retryResult); @@ -252,14 +252,14 @@ public class FlashSaleService { return createFailResult("系统繁忙,请稍后重试"); } } else if (remainingStock == -2) { - log.info("秒杀库存不足: flashSaleId={}, 剩余库存不足", flashSale.getId()); + log.info("活动库存不足: flashSaleId={}, 剩余库存不足", flashSale.getId()); return createFailResult("商品已售罄"); } else if (remainingStock == -3) { - log.error("秒杀参数异常: flashSaleId={}, quantity={}", flashSale.getId(), + log.error("限时活动参数异常: flashSaleId={}, quantity={}", flashSale.getId(), participateDTO.getQuantity()); return createFailResult("参数异常,请检查购买数量"); } else { - log.error("秒杀脚本执行异常: flashSaleId={}, returnValue={}", flashSale.getId(), remainingStock); + log.error("活动脚本执行异常: flashSaleId={}, returnValue={}", flashSale.getId(), remainingStock); return createFailResult("系统异常,请稍后重试"); } } @@ -274,26 +274,26 @@ public class FlashSaleService { // 更新数据库库存 flashSaleRepository.updateFlashStock(flashSale.getId(), participateDTO.getQuantity()); - // 发布秒杀成功消息 + // 发布限时活动成功消息 publishFlashSaleResult(userId, flashSale, order, true); // 更新商品销量排行榜 productService.increaseSalesRank(flashSale.getProductId(), participateDTO.getQuantity()); - log.info("秒杀成功: 用户ID={}, 订单ID={}, 剩余库存={}", userId, order.getId(), remainingStock); + log.info("限时活动成功: 用户ID={}, 订单ID={}, 剩余库存={}", userId, order.getId(), remainingStock); return createSuccessResult(order, flashSale); } catch (Exception e) { - log.error("秒杀处理异常: 用户ID={}, 秒杀ID={}", userId, participateDTO.getFlashSaleId(), e); - return createFailResult("秒杀失败,请重试"); + log.error("限时活动处理异常: 用户ID={}, 限时活动ID={}", userId, participateDTO.getFlashSaleId(), e); + return createFailResult("限时活动失败,请重试"); } finally { redissonLockService.unlock(lockKey); } } /** - * 获取秒杀活动列表 + * 获取限时活动列表 */ public Map getFlashSaleList(FlashSaleDTO.QueryDTO queryDTO) { // 验证排序字段 @@ -360,7 +360,7 @@ public class FlashSaleService { } /** - * 获取正在进行的秒杀活动 + * 获取正在进行的限时活动 */ public List getActiveFlashSales() { // 尝试从缓存获取 @@ -394,7 +394,7 @@ public class FlashSaleService { } /** - * 根据ID获取秒杀活动 + * 根据ID获取限时活动 */ public FlashSaleDTO getFlashSaleDTOById(Long flashSaleId) { FlashSale flashSale = getFlashSaleById(flashSaleId); @@ -407,21 +407,21 @@ public class FlashSaleService { } /** - * 预热秒杀活动 + * 预热限时活动 */ public void preloadFlashSale(Long flashSaleId) { - log.info("预热秒杀活动: {}", flashSaleId); + log.info("预热限时活动: {}", flashSaleId); Optional flashSaleOpt = flashSaleRepository.findById(flashSaleId); if (!flashSaleOpt.isPresent()) { - log.warn("秒杀活动不存在: {}", flashSaleId); + log.warn("限时活动不存在: {}", flashSaleId); return; } FlashSale flashSale = flashSaleOpt.get(); Product product = productRepository.findById(flashSale.getProductId()).orElse(null); - // 缓存秒杀活动信息 + // 缓存限时活动信息 cacheFlashSaleInfo(flashSale, product); // 预热库存 - 确保存储为数字类型 @@ -431,14 +431,14 @@ public class FlashSaleService { // 验证预热是否成功 String verifyStock = redisService.getString(stockKey); - log.info("秒杀活动预热验证: flashSaleId={}, stockKey={}, setStock={}, actualStock={}", + log.info("限时活动预热验证: flashSaleId={}, stockKey={}, setStock={}, actualStock={}", flashSaleId, stockKey, flashSale.getFlashStock(), verifyStock); if (!flashSale.getFlashStock().toString().equals(verifyStock)) { - log.error("秒杀活动预热失败: flashSaleId={}, expected={}, actual={}", + log.error("限时活动预热失败: flashSaleId={}, expected={}, actual={}", flashSaleId, flashSale.getFlashStock(), verifyStock); } else { - log.info("秒杀活动预热完成: flashSaleId={}, stock={}", flashSaleId, verifyStock); + log.info("限时活动预热完成: flashSaleId={}, stock={}", flashSaleId, verifyStock); } } @@ -446,11 +446,11 @@ public class FlashSaleService { * 修复Redis中的库存数据 */ public void repairFlashSaleStock(Long flashSaleId) { - log.info("修复秒杀活动库存数据: {}", flashSaleId); + log.info("修复限时活动库存数据: {}", flashSaleId); Optional flashSaleOpt = flashSaleRepository.findById(flashSaleId); if (!flashSaleOpt.isPresent()) { - log.warn("秒杀活动不存在: {}", flashSaleId); + log.warn("限时活动不存在: {}", flashSaleId); return; } @@ -501,18 +501,18 @@ public class FlashSaleService { } /** - * 重新预热所有活跃的秒杀活动库存 + * 重新预热所有活跃的限时活动库存 */ public void preloadAllActiveFlashSales() { - log.info("开始预热所有活跃秒杀活动库存"); + log.info("开始预热所有活跃限时活动库存"); try { - // 获取所有秒杀活动(包括未开始、进行中的) + // 获取所有限时活动(包括未开始、进行中的) LocalDateTime now = LocalDateTime.now(); List activeFlashSales = flashSaleRepository.findAll(); if (activeFlashSales.isEmpty()) { - log.info("没有找到任何秒杀活动,跳过预热"); + log.info("没有找到任何限时活动,跳过预热"); return; } @@ -525,13 +525,13 @@ public class FlashSaleService { // 检查活动状态,只预热有效的活动 if (flashSale.getStatus() == 3) { // 已结束的活动跳过 - log.debug("跳过已结束的秒杀活动: flashSaleId={}", flashSale.getId()); + log.debug("跳过已结束的限时活动: flashSaleId={}", flashSale.getId()); continue; } // 验证库存数据有效性 if (flashSale.getFlashStock() == null || flashSale.getFlashStock() < 0) { - log.warn("秒杀活动库存数据无效,跳过: flashSaleId={}, stock={}", + log.warn("限时活动库存数据无效,跳过: flashSaleId={}, stock={}", flashSale.getId(), flashSale.getFlashStock()); failCount++; continue; @@ -567,27 +567,27 @@ public class FlashSaleService { // 验证设置是否成功 String verifyStock = redisService.getString(stockKey); if (flashSale.getFlashStock().toString().equals(verifyStock)) { - log.info("预热秒杀活动库存成功: flashSaleId={}, stock={}, status={}", + log.info("预热限时活动库存成功: flashSaleId={}, stock={}, status={}", flashSale.getId(), flashSale.getFlashStock(), getStatusText(flashSale.getStatus())); successCount++; } else { - log.error("预热秒杀活动库存验证失败: flashSaleId={}, expected={}, actual={}", + log.error("预热限时活动库存验证失败: flashSaleId={}, expected={}, actual={}", flashSale.getId(), flashSale.getFlashStock(), verifyStock); failCount++; } } catch (Exception e) { - log.error("预热秒杀活动库存失败: flashSaleId={}", flashSale.getId(), e); + log.error("预热限时活动库存失败: flashSaleId={}", flashSale.getId(), e); failCount++; } } - log.info("所有秒杀活动库存预热完成: 总数={}, 成功={}, 失败={}", + log.info("所有限时活动库存预热完成: 总数={}, 成功={}, 失败={}", activeFlashSales.size(), successCount, failCount); } catch (Exception e) { - log.error("预热所有秒杀活动库存时发生异常", e); - throw new RuntimeException("预热秒杀活动库存失败: " + e.getMessage(), e); + log.error("预热所有限时活动库存时发生异常", e); + throw new RuntimeException("预热限时活动库存失败: " + e.getMessage(), e); } } @@ -611,13 +611,13 @@ public class FlashSaleService { } /** - * 恢复秒杀库存(订单取消时调用) + * 恢复活动库存(订单取消时调用) * 恢复Redis库存、DB库存,并移除成功用户集合记录 */ @Transactional public void restoreFlashSaleStock(Long flashSaleId, Long productId, LocalDateTime orderCreatedAt, Long userId, Integer quantity) { - log.info("恢复秒杀库存: flashSaleId={}, productId={}, orderCreatedAt={}, userId={}, quantity={}", + log.info("恢复活动库存: flashSaleId={}, productId={}, orderCreatedAt={}, userId={}, quantity={}", flashSaleId, productId, orderCreatedAt, userId, quantity); Optional flashSaleOpt = Optional.empty(); @@ -626,10 +626,10 @@ public class FlashSaleService { orderCreatedAt); if (matchedFlashSales.size() == 1) { flashSaleOpt = Optional.of(matchedFlashSales.get(0)); - log.info("根据商品和下单时间回填秒杀活动: flashSaleId={}, productId={}", + log.info("根据商品和下单时间回填限时活动: flashSaleId={}, productId={}", flashSaleOpt.get().getId(), productId); } else { - log.warn("订单未记录秒杀活动ID且无法唯一匹配历史活动,跳过秒杀库存恢复: productId={}, matches={}", + log.warn("订单未记录限时活动ID且无法唯一匹配历史活动,跳过活动库存恢复: productId={}, matches={}", productId, matchedFlashSales.size()); return; } @@ -638,7 +638,7 @@ public class FlashSaleService { } if (!flashSaleOpt.isPresent()) { - log.warn("未找到对应的秒杀活动,跳过秒杀库存恢复: flashSaleId={}", flashSaleId); + log.warn("未找到对应的限时活动,跳过活动库存恢复: flashSaleId={}", flashSaleId); return; } @@ -655,12 +655,12 @@ public class FlashSaleService { String successUsersKey = FLASH_SALE_SUCCESS_USERS_PREFIX + flashSale.getId(); redisService.sRem(successUsersKey, userId); - log.info("秒杀库存恢复成功: flashSaleId={}, userId={}, quantity={}", + log.info("活动库存恢复成功: flashSaleId={}, userId={}, quantity={}", flashSale.getId(), userId, quantity); } /** - * 获取秒杀活动剩余库存 + * 获取限时活动剩余库存 */ public Integer getFlashSaleStock(Long flashSaleId) { String stockKey = FLASH_SALE_STOCK_PREFIX + flashSaleId; @@ -669,16 +669,16 @@ public class FlashSaleService { } /** - * 更新秒杀活动 + * 更新限时活动 */ @Transactional public FlashSaleDTO updateFlashSale(Long flashSaleId, FlashSaleDTO.UpdateDTO updateDTO) { - log.info("更新秒杀活动: ID={}", flashSaleId); + log.info("更新限时活动: ID={}", flashSaleId); - // 获取现有秒杀活动 + // 获取现有限时活动 Optional flashSaleOpt = flashSaleRepository.findById(flashSaleId); if (!flashSaleOpt.isPresent()) { - throw new RuntimeException("秒杀活动不存在"); + throw new RuntimeException("限时活动不存在"); } FlashSale flashSale = flashSaleOpt.get(); @@ -711,40 +711,40 @@ public class FlashSaleService { String stockKey = FLASH_SALE_STOCK_PREFIX + flashSale.getId(); redisService.set(stockKey, flashSale.getFlashStock()); - log.info("秒杀活动更新成功: ID={}", flashSale.getId()); + log.info("限时活动更新成功: ID={}", flashSale.getId()); return buildFlashSaleDTO(flashSale, product); } /** - * 删除秒杀活动 + * 删除限时活动 */ @Transactional public boolean deleteFlashSale(Long flashSaleId) { - log.info("删除秒杀活动: ID={}", flashSaleId); + log.info("删除限时活动: ID={}", flashSaleId); - // 删除秒杀活动 + // 删除限时活动 flashSaleRepository.deleteById(flashSaleId); // 清除相关缓存 clearFlashSaleCache(flashSaleId); - log.info("秒杀活动删除成功: ID={}", flashSaleId); + log.info("限时活动删除成功: ID={}", flashSaleId); return true; } /** - * 发布秒杀活动 + * 发布限时活动 */ @Transactional public FlashSaleDTO publishFlashSale(Long flashSaleId) { - log.info("发布秒杀活动: ID={}", flashSaleId); + log.info("发布限时活动: ID={}", flashSaleId); - // 获取现有秒杀活动 + // 获取现有限时活动 Optional flashSaleOpt = flashSaleRepository.findById(flashSaleId); if (!flashSaleOpt.isPresent()) { - throw new RuntimeException("秒杀活动不存在"); + throw new RuntimeException("限时活动不存在"); } FlashSale flashSale = flashSaleOpt.get(); @@ -760,22 +760,22 @@ public class FlashSaleService { Product product = productRepository.findById(flashSale.getProductId()).orElse(null); cacheFlashSaleInfo(flashSale, product); - log.info("秒杀活动发布成功: ID={}", flashSaleId); + log.info("限时活动发布成功: ID={}", flashSaleId); return buildFlashSaleDTO(flashSale, product); } /** - * 暂停秒杀活动 + * 暂停限时活动 */ @Transactional public FlashSaleDTO pauseFlashSale(Long flashSaleId) { - log.info("暂停秒杀活动: ID={}", flashSaleId); + log.info("暂停限时活动: ID={}", flashSaleId); - // 获取现有秒杀活动 + // 获取现有限时活动 Optional flashSaleOpt = flashSaleRepository.findById(flashSaleId); if (!flashSaleOpt.isPresent()) { - throw new RuntimeException("秒杀活动不存在"); + throw new RuntimeException("限时活动不存在"); } FlashSale flashSale = flashSaleOpt.get(); @@ -788,22 +788,22 @@ public class FlashSaleService { Product product = productRepository.findById(flashSale.getProductId()).orElse(null); cacheFlashSaleInfo(flashSale, product); - log.info("秒杀活动暂停成功: ID={}", flashSaleId); + log.info("限时活动暂停成功: ID={}", flashSaleId); return buildFlashSaleDTO(flashSale, product); } /** - * 恢复秒杀活动 + * 恢复限时活动 */ @Transactional public FlashSaleDTO resumeFlashSale(Long flashSaleId) { - log.info("恢复秒杀活动: ID={}", flashSaleId); + log.info("恢复限时活动: ID={}", flashSaleId); - // 获取现有秒杀活动 + // 获取现有限时活动 Optional flashSaleOpt = flashSaleRepository.findById(flashSaleId); if (!flashSaleOpt.isPresent()) { - throw new RuntimeException("秒杀活动不存在"); + throw new RuntimeException("限时活动不存在"); } FlashSale flashSale = flashSaleOpt.get(); @@ -816,22 +816,22 @@ public class FlashSaleService { Product product = productRepository.findById(flashSale.getProductId()).orElse(null); cacheFlashSaleInfo(flashSale, product); - log.info("秒杀活动恢复成功: ID={}", flashSaleId); + log.info("限时活动恢复成功: ID={}", flashSaleId); return buildFlashSaleDTO(flashSale, product); } /** - * 结束秒杀活动 + * 结束限时活动 */ @Transactional public FlashSaleDTO endFlashSale(Long flashSaleId) { - log.info("结束秒杀活动: ID={}", flashSaleId); + log.info("结束限时活动: ID={}", flashSaleId); - // 获取现有秒杀活动 + // 获取现有限时活动 Optional flashSaleOpt = flashSaleRepository.findById(flashSaleId); if (!flashSaleOpt.isPresent()) { - throw new RuntimeException("秒杀活动不存在"); + throw new RuntimeException("限时活动不存在"); } FlashSale flashSale = flashSaleOpt.get(); @@ -847,13 +847,13 @@ public class FlashSaleService { Product product = productRepository.findById(flashSale.getProductId()).orElse(null); cacheFlashSaleInfo(flashSale, product); - log.info("秒杀活动结束成功: ID={}", flashSaleId); + log.info("限时活动结束成功: ID={}", flashSaleId); return buildFlashSaleDTO(flashSale, product); } /** - * 获取秒杀活动统计信息(即将开始、正在进行的全局数量 + 用户参与/成功数量) + * 获取限时活动统计信息(即将开始、正在进行的全局数量 + 用户参与/成功数量) */ public Map getFlashSaleStatistics(Long userId) { LocalDateTime now = LocalDateTime.now(); @@ -877,11 +877,11 @@ public class FlashSaleService { } /** - * 定时预热即将开始的秒杀活动库存(每5分钟执行一次) + * 定时预热即将开始的限时活动库存(每5分钟执行一次) */ @Scheduled(fixedRate = 300000) public void scheduledPreloadFlashSales() { - log.info("定时任务:检查即将开始的秒杀活动并预热库存"); + log.info("定时任务:检查即将开始的限时活动并预热库存"); try { LocalDateTime now = LocalDateTime.now(); LocalDateTime threshold = now.plusMinutes(30); @@ -896,18 +896,18 @@ public class FlashSaleService { } if (preloadCount > 0) { - log.info("定时预热完成:预热了{}个即将开始的秒杀活动", preloadCount); + log.info("定时预热完成:预热了{}个即将开始的限时活动", preloadCount); } - // 同时更新秒杀活动状态 + // 同时更新限时活动状态 updateFlashSaleStatus(); } catch (Exception e) { - log.error("定时预热秒杀活动失败", e); + log.error("定时预热限时活动失败", e); } } /** - * 更新秒杀活动状态 + * 更新限时活动状态 */ @Transactional public void updateFlashSaleStatus() { @@ -918,7 +918,7 @@ public class FlashSaleService { for (FlashSale flashSale : upcomingFlashSales) { if (flashSale.isStarted() && !flashSale.isEnded()) { flashSaleRepository.updateStatus(flashSale.getId(), 2); - log.info("秒杀活动开始: {}", flashSale.getId()); + log.info("限时活动开始: {}", flashSale.getId()); } } @@ -927,7 +927,7 @@ public class FlashSaleService { for (FlashSale flashSale : activeFlashSales) { if (flashSale.isEnded()) { flashSaleRepository.updateStatus(flashSale.getId(), 3); - log.info("秒杀活动结束: {}", flashSale.getId()); + log.info("限时活动结束: {}", flashSale.getId()); // 清除相关缓存 clearFlashSaleCache(flashSale.getId()); @@ -936,7 +936,7 @@ public class FlashSaleService { } /** - * 根据ID获取秒杀活动实体 + * 根据ID获取限时活动实体 */ private FlashSale getFlashSaleById(Long flashSaleId) { // 先从缓存获取 @@ -968,7 +968,7 @@ public class FlashSaleService { } /** - * 缓存秒杀活动信息 + * 缓存限时活动信息 */ private void cacheFlashSaleInfo(FlashSale flashSale, Product product) { String cacheKey = FLASH_SALE_CACHE_PREFIX + flashSale.getId(); @@ -991,7 +991,7 @@ public class FlashSaleService { } /** - * 构建秒杀活动DTO + * 构建限时活动DTO */ private FlashSaleDTO buildFlashSaleDTO(FlashSale flashSale, Product product) { FlashSaleDTO dto = new FlashSaleDTO(); @@ -1064,7 +1064,7 @@ public class FlashSaleService { } /** - * 创建秒杀订单 + * 创建限时订单 */ private Order createFlashSaleOrder(Long userId, FlashSale flashSale, FlashSaleDTO.ParticipateDTO participateDTO) { Order order = new Order(); @@ -1075,16 +1075,16 @@ public class FlashSaleService { order.setQuantity(participateDTO.getQuantity()); order.setTotalPrice(flashSale.getFlashPrice().multiply(BigDecimal.valueOf(participateDTO.getQuantity()))); order.setStatus(1); // 待支付 - order.setOrderType(2); // 秒杀订单 + order.setOrderType(2); // 限时订单 order.setReceiverPhone(participateDTO.getPhone()); order.setReceiverAddress(participateDTO.getAddress()); - order.setRemark("秒杀订单"); + order.setRemark("限时订单"); return orderRepository.save(order); } /** - * 发布秒杀结果消息 + * 发布限时活动结果消息 */ private void publishFlashSaleResult(Long userId, FlashSale flashSale, Order order, boolean success) { Map message = new HashMap<>(); @@ -1104,7 +1104,7 @@ public class FlashSaleService { private FlashSaleDTO.ResultDTO createSuccessResult(Order order, FlashSale flashSale) { FlashSaleDTO.ResultDTO result = new FlashSaleDTO.ResultDTO(); result.setSuccess(true); - result.setMessage("秒杀成功"); + result.setMessage("限时活动成功"); result.setOrderId(order.getId()); result.setFlashSaleId(flashSale.getId()); result.setProductId(flashSale.getProductId()); @@ -1126,7 +1126,7 @@ public class FlashSaleService { } /** - * 清除秒杀活动缓存 + * 清除限时活动缓存 */ private void clearFlashSaleCache(Long flashSaleId) { String cacheKey = FLASH_SALE_CACHE_PREFIX + flashSaleId; diff --git a/src/main/java/com/org/flashsalesystem/service/MessageListenerService.java b/src/main/java/com/org/flashsalesystem/service/MessageListenerService.java index bc5ac1b..e4c1b18 100644 --- a/src/main/java/com/org/flashsalesystem/service/MessageListenerService.java +++ b/src/main/java/com/org/flashsalesystem/service/MessageListenerService.java @@ -48,7 +48,7 @@ public class MessageListenerService { new ChannelTopic("stock:change") ); - // 秒杀结果监听 + // 限时活动结果监听 redisMessageListenerContainer.addMessageListener( new FlashSaleResultListener(), new ChannelTopic("flashsale:result") @@ -138,26 +138,26 @@ public class MessageListenerService { } /** - * 处理秒杀结果 + * 处理限时活动结果 */ private void handleFlashSaleResult(Long userId, Long flashSaleId, Boolean success, Map data) { if (userId == null) { - log.warn("秒杀结果缺少用户ID: flashSaleId={}", flashSaleId); + log.warn("限时活动结果缺少用户ID: flashSaleId={}", flashSaleId); return; } String link = "/flashsale/" + flashSaleId; if (success) { - String title = "秒杀成功"; - String message = "恭喜您成功抢购秒杀商品,请尽快完成支付!"; + String title = "限时活动成功"; + String message = "恭喜您成功抢购限时活动商品,请尽快完成支付!"; notificationService.createNotification(userId, "flashsale", title, message, link); - log.info("秒杀成功通知已创建: 用户ID={}, 秒杀ID={}", userId, flashSaleId); + log.info("限时活动成功通知已创建: 用户ID={}, 限时活动ID={}", userId, flashSaleId); } else { - String title = "秒杀未中"; - String message = "很遗憾,本次秒杀未能抢购成功,下次再来吧!"; + String title = "活动未中"; + String message = "很遗憾,本次限时活动未能抢购成功,下次再来吧!"; notificationService.createNotification(userId, "flashsale", title, message, link); - log.info("秒杀失败通知已创建: 用户ID={}, 秒杀ID={}", userId, flashSaleId); + log.info("限时活动失败通知已创建: 用户ID={}, 限时活动ID={}", userId, flashSaleId); } } @@ -390,14 +390,14 @@ public class MessageListenerService { } /** - * 秒杀结果监听器 + * 限时活动结果监听器 */ private class FlashSaleResultListener implements MessageListener { @Override public void onMessage(Message message, byte[] pattern) { try { String messageBody = new String(message.getBody()); - log.debug("接收到秒杀结果消息: {}", messageBody); + log.debug("接收到限时活动结果消息: {}", messageBody); Map data = parseRedissonMessage(messageBody); @@ -405,13 +405,13 @@ public class MessageListenerService { Long flashSaleId = extractLongValue(data.get("flashSaleId")); Boolean success = Boolean.valueOf(data.get("success").toString()); - log.info("秒杀结果: 用户ID={}, 秒杀ID={}, 成功={}", userId, flashSaleId, success); + log.info("限时活动结果: 用户ID={}, 限时活动ID={}, 成功={}", userId, flashSaleId, success); // 这里可以添加具体的业务处理逻辑 handleFlashSaleResult(userId, flashSaleId, success, data); } catch (Exception e) { - log.error("处理秒杀结果消息失败", e); + log.error("处理限时活动结果消息失败", e); } } } diff --git a/src/main/java/com/org/flashsalesystem/service/OrderService.java b/src/main/java/com/org/flashsalesystem/service/OrderService.java index d90ac20..47425fe 100644 --- a/src/main/java/com/org/flashsalesystem/service/OrderService.java +++ b/src/main/java/com/org/flashsalesystem/service/OrderService.java @@ -443,7 +443,7 @@ public class OrderService { // 恢复库存 productService.updateStock(order.getProductId(), order.getQuantity(), "increase"); - // 秒杀订单额外恢复秒杀库存 + // 限时订单额外恢复活动库存 if (order.getOrderType() != null && order.getOrderType() == 2) { flashSaleService.restoreFlashSaleStock(order.getFlashSaleId(), order.getProductId(), order.getCreatedAt(), order.getUserId(), order.getQuantity()); @@ -925,7 +925,7 @@ public class OrderService { case 1: return "普通订单"; case 2: - return "秒杀订单"; + return "限时订单"; case 3: return "拼团订单"; default: diff --git a/src/main/java/com/org/flashsalesystem/service/RateLimitService.java b/src/main/java/com/org/flashsalesystem/service/RateLimitService.java index a9e7219..e52fa30 100644 --- a/src/main/java/com/org/flashsalesystem/service/RateLimitService.java +++ b/src/main/java/com/org/flashsalesystem/service/RateLimitService.java @@ -57,7 +57,7 @@ public class RateLimitService { } /** - * 检查用户秒杀接口限流 + * 检查用户限时活动接口限流 * * @param userId 用户ID * diff --git a/src/main/java/com/org/flashsalesystem/service/RedisService.java b/src/main/java/com/org/flashsalesystem/service/RedisService.java index e2db565..c3d1796 100644 --- a/src/main/java/com/org/flashsalesystem/service/RedisService.java +++ b/src/main/java/com/org/flashsalesystem/service/RedisService.java @@ -369,19 +369,19 @@ public class RedisService { // ========== Lua脚本操作 ========== /** - * 执行秒杀脚本 + * 执行活动脚本 */ public Long executeFlashSaleScript(String stockKey, int quantity) { - log.info("执行秒杀脚本: stockKey={}, quantity={}", stockKey, quantity); + log.info("执行活动脚本: stockKey={}, quantity={}", stockKey, quantity); try { Long result = stringRedisTemplate.execute(flashSaleScript, Collections.singletonList(stockKey), String.valueOf(quantity)); - log.info("秒杀脚本执行结果: result={}", result); + log.info("活动脚本执行结果: result={}", result); return result; } catch (Exception e) { - log.error("执行秒杀脚本异常: stockKey={}, quantity={}", stockKey, quantity, e); + log.error("执行活动脚本异常: stockKey={}, quantity={}", stockKey, quantity, e); throw e; } } diff --git a/src/main/java/com/org/flashsalesystem/util/JSPFunctions.java b/src/main/java/com/org/flashsalesystem/util/JSPFunctions.java index 203220e..22da643 100644 --- a/src/main/java/com/org/flashsalesystem/util/JSPFunctions.java +++ b/src/main/java/com/org/flashsalesystem/util/JSPFunctions.java @@ -168,7 +168,7 @@ public class JSPFunctions { } /** - * 格式化秒杀状态 + * 格式化限时活动状态 * * @param status 状态码 * diff --git a/src/main/resources/META-INF/flashsale-functions.tld b/src/main/resources/META-INF/flashsale-functions.tld index ad85d3d..a9c1946 100644 --- a/src/main/resources/META-INF/flashsale-functions.tld +++ b/src/main/resources/META-INF/flashsale-functions.tld @@ -74,7 +74,7 @@ ${fn:formatOrderStatus(order.status)} - + Format flash sale status formatFlashSaleStatus diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8be3838..3be95c4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,7 +7,7 @@ server: spring: application: - name: flash-sale-system + name: community-fresh-group-buy-system # 数据源配置 datasource: @@ -91,18 +91,18 @@ logging: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" file: - name: logs/flash-sale-system.log + name: logs/community-fresh-group-buy-system.log # 自定义配置 flashsale: # 文件上传配置 upload: # 文件上传路径 - path: ${user.home}/flashsale-uploads/ + path: ${user.home}/group-buying-uploads/ # 访问URL前缀 url-prefix: /uploads/ - # 秒杀配置 + # 限时活动配置 seckill: # 每个用户每个商品最大购买数量 max-quantity-per-user: 1 @@ -129,7 +129,7 @@ flashsale: user-expire-minutes: 30 # 商品信息缓存过期时间(分钟) product-expire-minutes: 60 - # 秒杀活动缓存过期时间(分钟) + # 限时活动缓存过期时间(分钟) flashsale-expire-minutes: 10 # 退货配置 @@ -143,7 +143,7 @@ flashsale: order-status-channel: order:status:change # 库存变更通知频道 stock-change-channel: stock:change - # 秒杀结果通知频道 + # 限时活动结果通知频道 flashsale-result-channel: flashsale:result # 监控配置 diff --git a/src/main/resources/lua/flashsale.lua b/src/main/resources/lua/flashsale.lua index 1aa81b3..12c2d20 100644 --- a/src/main/resources/lua/flashsale.lua +++ b/src/main/resources/lua/flashsale.lua @@ -1,4 +1,4 @@ --- 秒杀Lua脚本 +-- 限时活动Lua脚本 -- 功能:原子性地检查库存并扣减,防止超卖 -- 参数:KEYS[1] = 库存key, ARGV[1] = 扣减数量 -- 返回值:成功返回剩余库存,失败返回负数 diff --git a/src/test/java/com/org/flashsalesystem/service/FlashSaleServiceTest.java b/src/test/java/com/org/flashsalesystem/service/FlashSaleServiceTest.java index 2a35a48..39185c8 100644 --- a/src/test/java/com/org/flashsalesystem/service/FlashSaleServiceTest.java +++ b/src/test/java/com/org/flashsalesystem/service/FlashSaleServiceTest.java @@ -23,10 +23,10 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; /** - * 秒杀服务测试类 + * 限时活动服务测试类 */ @ExtendWith(MockitoExtension.class) -@DisplayName("秒杀服务测试") +@DisplayName("限时活动服务测试") class FlashSaleServiceTest { @Mock @@ -70,7 +70,7 @@ class FlashSaleServiceTest { } @Test - @DisplayName("创建秒杀活动 - 成功") + @DisplayName("创建限时活动 - 成功") void createFlashSale_Success() { FlashSaleDTO.CreateDTO createDTO = new FlashSaleDTO.CreateDTO(); createDTO.setProductId(1L); @@ -92,7 +92,7 @@ class FlashSaleServiceTest { } @Test - @DisplayName("创建秒杀活动 - 同一商品允许多个活动") + @DisplayName("创建限时活动 - 同一商品允许多个活动") void createFlashSale_MultipleActivitiesPerProductAllowed() { FlashSaleDTO.CreateDTO createDTO = new FlashSaleDTO.CreateDTO(); createDTO.setProductId(1L); @@ -123,7 +123,7 @@ class FlashSaleServiceTest { } @Test - @DisplayName("创建秒杀活动 - 商品不存在") + @DisplayName("创建限时活动 - 商品不存在") void createFlashSale_ProductNotFound() { FlashSaleDTO.CreateDTO createDTO = new FlashSaleDTO.CreateDTO(); createDTO.setProductId(999L); @@ -139,7 +139,7 @@ class FlashSaleServiceTest { } @Test - @DisplayName("创建秒杀活动 - 开始时间早于当前时间") + @DisplayName("创建限时活动 - 开始时间早于当前时间") void createFlashSale_StartTimeBeforeNow() { FlashSaleDTO.CreateDTO createDTO = new FlashSaleDTO.CreateDTO(); createDTO.setProductId(1L); @@ -159,7 +159,7 @@ class FlashSaleServiceTest { } @Test - @DisplayName("获取秒杀库存") + @DisplayName("获取活动库存") void getFlashSaleStock_Success() { when(redisService.get(anyString())).thenReturn(50); @@ -171,7 +171,7 @@ class FlashSaleServiceTest { } @Test - @DisplayName("获取秒杀库存 - 无库存数据") + @DisplayName("获取活动库存 - 无库存数据") void getFlashSaleStock_NoData() { when(redisService.get(anyString())).thenReturn(null); @@ -181,7 +181,7 @@ class FlashSaleServiceTest { } @Test - @DisplayName("预热秒杀活动") + @DisplayName("预热限时活动") void preloadFlashSale_Success() { when(flashSaleRepository.findById(1L)).thenReturn(Optional.of(testFlashSale)); when(productRepository.findById(1L)).thenReturn(Optional.of(testProduct)); @@ -195,7 +195,7 @@ class FlashSaleServiceTest { } @Test - @DisplayName("预热秒杀活动 - 活动不存在") + @DisplayName("预热限时活动 - 活动不存在") void preloadFlashSale_ActivityNotFound() { when(flashSaleRepository.findById(999L)).thenReturn(Optional.empty()); diff --git a/start-system.sh b/start-system.sh index 85584fe..9a5a37a 100644 --- a/start-system.sh +++ b/start-system.sh @@ -65,7 +65,7 @@ done # 启动前端 echo "" echo -e "${YELLOW}[4/4] 启动Vue前端...${NC}" -cd flash-sale-frontend +cd community-fresh-group-buy-frontend # 检查依赖 if [ ! -d "node_modules" ]; then