init
This commit is contained in:
commit
e682e9c3f8
|
|
@ -0,0 +1,175 @@
|
||||||
|
# ============================================================
|
||||||
|
# snack-mall monorepo .gitignore
|
||||||
|
# 覆盖:server-snack (SpringBoot/Maven)
|
||||||
|
# web-snack / admin-snack (Vue3 + Vite)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# ① Java / Maven (server-snack)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 编译输出
|
||||||
|
**/target/
|
||||||
|
**/out/
|
||||||
|
**/*.class
|
||||||
|
|
||||||
|
# Maven wrapper
|
||||||
|
!**/mvnw
|
||||||
|
!**/mvnw.cmd
|
||||||
|
**/.mvn/repository/
|
||||||
|
|
||||||
|
# 可执行 jar / war
|
||||||
|
**/*.jar
|
||||||
|
**/*.war
|
||||||
|
**/*.nar
|
||||||
|
**/*.ear
|
||||||
|
**/*.zip
|
||||||
|
**/*.tar.gz
|
||||||
|
**/*.rar
|
||||||
|
|
||||||
|
# 日志
|
||||||
|
**/*.log
|
||||||
|
**/logs/
|
||||||
|
|
||||||
|
# 本地上传文件目录(运行时生成,不纳入版本管理)
|
||||||
|
server-snack/uploads/
|
||||||
|
|
||||||
|
# Spring Boot DevTools 重启文件
|
||||||
|
**/.spring-boot-devtools.properties
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# ② Node.js / Vue / Vite (web-snack / admin-snack)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 依赖目录
|
||||||
|
**/node_modules/
|
||||||
|
|
||||||
|
# 构建产物
|
||||||
|
**/dist/
|
||||||
|
**/dist-ssr/
|
||||||
|
**/.output/
|
||||||
|
**/.nuxt/
|
||||||
|
|
||||||
|
# Vite 缓存
|
||||||
|
**/.vite/
|
||||||
|
**/vite.config.*.timestamp-*
|
||||||
|
|
||||||
|
# 类型声明自动生成文件(可按需取消注释)
|
||||||
|
# **/auto-imports.d.ts
|
||||||
|
# **/components.d.ts
|
||||||
|
|
||||||
|
# pnpm / yarn / npm 锁定文件(保留其中一种,其余忽略)
|
||||||
|
# 若统一使用 pnpm,取消下面两行注释:
|
||||||
|
# **/package-lock.json
|
||||||
|
# **/yarn.lock
|
||||||
|
|
||||||
|
# 本地 npm 调试日志
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Cypress / Playwright 测试产物
|
||||||
|
**/cypress/videos/
|
||||||
|
**/cypress/screenshots/
|
||||||
|
**/playwright-report/
|
||||||
|
**/test-results/
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# ③ 环境变量 / 敏感配置
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 本地环境变量文件(含数据库密码、密钥等,绝对不提交)
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# Spring Boot 本地覆盖配置(开发时本地差异化配置)
|
||||||
|
**/application-local.yml
|
||||||
|
**/application-local.yaml
|
||||||
|
**/application-local.properties
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# ④ IDE / 编辑器
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# IntelliJ IDEA
|
||||||
|
.idea/
|
||||||
|
**/*.iws
|
||||||
|
**/*.iml
|
||||||
|
**/*.ipr
|
||||||
|
**/.idea/
|
||||||
|
|
||||||
|
# VS Code
|
||||||
|
.vscode/
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/settings.json
|
||||||
|
|
||||||
|
# WebStorm / JetBrains
|
||||||
|
**/.fleet/
|
||||||
|
|
||||||
|
# Eclipse
|
||||||
|
**/.classpath
|
||||||
|
**/.project
|
||||||
|
**/.settings/
|
||||||
|
**/bin/
|
||||||
|
|
||||||
|
# NetBeans
|
||||||
|
**/nbproject/
|
||||||
|
**/nbbuild/
|
||||||
|
**/nbdist/
|
||||||
|
**/.nb-gradle/
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# ⑤ 操作系统
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
Desktop.ini
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# Linux
|
||||||
|
*~
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# ⑥ 其他工具
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# Kiro / Claude Code 会话目录(本地 AI 工具产生的临时文件)
|
||||||
|
.claude/
|
||||||
|
!.claude/settings.json
|
||||||
|
|
||||||
|
# HTTPS 证书(本地开发用,不提交)
|
||||||
|
**/*.pem
|
||||||
|
**/*.key
|
||||||
|
**/*.crt
|
||||||
|
**/*.p12
|
||||||
|
|
||||||
|
# 临时文件
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
@ -0,0 +1,244 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.snack</groupId>
|
||||||
|
<artifactId>server-snack</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
<name>server-snack</name>
|
||||||
|
<description>零食商城服务端</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>21</java.version>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<spring-boot.version>4.0.6</spring-boot.version>
|
||||||
|
<mybatis-plus.version>3.5.15</mybatis-plus.version>
|
||||||
|
<hutool.version>5.8.44</hutool.version>
|
||||||
|
<sa-token.version>1.45.0</sa-token.version>
|
||||||
|
<knife4j.version>4.6.0</knife4j.version>
|
||||||
|
<fastjson2.version>2.0.62</fastjson2.version>
|
||||||
|
<fastjson2-extension.version>2.0.61</fastjson2-extension.version>
|
||||||
|
<qiniu.version>7.19.0</qiniu.version>
|
||||||
|
<s3.version>2.44.4</s3.version>
|
||||||
|
<minio.version>9.0.0</minio.version>
|
||||||
|
<aliyun-oss.version>3.18.5</aliyun-oss.version>
|
||||||
|
<tencent-cos.version>5.6.269</tencent-cos.version>
|
||||||
|
<weixin-java.version>4.8.3.B</weixin-java.version>
|
||||||
|
<mapstruct.version>1.6.3</mapstruct.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-crypto</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>${hutool.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-spring-boot4-starter</artifactId>
|
||||||
|
<version>${sa-token.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.xingfudeshi</groupId>
|
||||||
|
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||||
|
<version>${knife4j.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
<artifactId>fastjson2</artifactId>
|
||||||
|
<version>${fastjson2.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
<artifactId>fastjson2-extension-spring6</artifactId>
|
||||||
|
<version>${fastjson2-extension.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.qiniu</groupId>
|
||||||
|
<artifactId>qiniu-java-sdk</artifactId>
|
||||||
|
<version>${qiniu.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>software.amazon.awssdk</groupId>
|
||||||
|
<artifactId>s3</artifactId>
|
||||||
|
<version>${s3.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.minio</groupId>
|
||||||
|
<artifactId>minio</artifactId>
|
||||||
|
<version>${minio.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.aliyun.oss</groupId>
|
||||||
|
<artifactId>aliyun-sdk-oss</artifactId>
|
||||||
|
<version>${aliyun-oss.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.qcloud</groupId>
|
||||||
|
<artifactId>cos_api</artifactId>
|
||||||
|
<version>${tencent-cos.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.binarywang</groupId>
|
||||||
|
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
|
||||||
|
<version>${weixin-java.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct</artifactId>
|
||||||
|
<version>${mapstruct.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-dependencies</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-bom</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/java</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**/mapper/*.xml</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<configuration>
|
||||||
|
<source>21</source>
|
||||||
|
<target>21</target>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<compilerArgs>
|
||||||
|
<arg>-parameters</arg>
|
||||||
|
</compilerArgs>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</path>
|
||||||
|
<path>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct-processor</artifactId>
|
||||||
|
<version>${mapstruct.version}</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>com.snack.server.ServerSnackApplication</mainClass>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>repackage</id>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>huaweicloud</id>
|
||||||
|
<name>huawei</name>
|
||||||
|
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>aliyunmaven</id>
|
||||||
|
<name>aliyun</name>
|
||||||
|
<url>https://maven.aliyun.com/repository/public</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.snack.server;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableScheduling
|
||||||
|
public class ServerSnackApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(ServerSnackApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.snack.server.common;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一响应结果封装
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class Result<T> implements Serializable {
|
||||||
|
|
||||||
|
private Integer code;
|
||||||
|
private String message;
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
private Result(Integer code, String message, T data) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> ok() {
|
||||||
|
return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> ok(T data) {
|
||||||
|
return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> ok(String message, T data) {
|
||||||
|
return new Result<>(ResultCode.SUCCESS.getCode(), message, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> fail() {
|
||||||
|
return new Result<>(ResultCode.FAIL.getCode(), ResultCode.FAIL.getMessage(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> fail(String message) {
|
||||||
|
return new Result<>(ResultCode.FAIL.getCode(), message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> fail(Integer code, String message) {
|
||||||
|
return new Result<>(code, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> fail(ResultCode resultCode) {
|
||||||
|
return new Result<>(resultCode.getCode(), resultCode.getMessage(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.snack.server.common;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一响应状态码
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public enum ResultCode {
|
||||||
|
|
||||||
|
SUCCESS(200, "操作成功"),
|
||||||
|
FAIL(400, "操作失败"),
|
||||||
|
UNAUTHORIZED(401, "未登录或Token已过期"),
|
||||||
|
FORBIDDEN(403, "无权限访问"),
|
||||||
|
NOT_FOUND(404, "资源不存在"),
|
||||||
|
METHOD_NOT_ALLOWED(405, "请求方法不允许"),
|
||||||
|
INTERNAL_SERVER_ERROR(500, "服务器内部错误"),
|
||||||
|
|
||||||
|
// 业务错误码 (1xxx)
|
||||||
|
PARAM_ERROR(1000, "请求参数错误"),
|
||||||
|
USER_NOT_EXIST(1001, "用户不存在"),
|
||||||
|
USER_DISABLED(1002, "账号已被禁用"),
|
||||||
|
USERNAME_EXIST(1003, "用户名已存在"),
|
||||||
|
PASSWORD_ERROR(1004, "密码错误"),
|
||||||
|
OLD_PASSWORD_ERROR(1005, "原密码错误"),
|
||||||
|
PRODUCT_NOT_EXIST(1010, "商品不存在"),
|
||||||
|
PRODUCT_OFF_SHELF(1011, "商品已下架"),
|
||||||
|
STOCK_INSUFFICIENT(1012, "库存不足"),
|
||||||
|
ORDER_NOT_EXIST(1020, "订单不存在"),
|
||||||
|
ORDER_STATUS_ERROR(1021, "订单状态异常,无法操作"),
|
||||||
|
COUPON_NOT_EXIST(1030, "优惠券不存在"),
|
||||||
|
COUPON_EXPIRED(1031, "优惠券已过期"),
|
||||||
|
COUPON_USED(1032, "优惠券已使用"),
|
||||||
|
COUPON_CONDITION_NOT_MET(1033, "不满足优惠券使用条件"),
|
||||||
|
COUPON_RECEIVE_LIMIT(1034, "已达到领取上限"),
|
||||||
|
COUPON_STOCK_EMPTY(1035, "优惠券已被领完"),
|
||||||
|
SECKILL_NOT_STARTED(1040, "抢购活动尚未开始"),
|
||||||
|
SECKILL_ENDED(1041, "抢购活动已结束"),
|
||||||
|
SECKILL_STOCK_EMPTY(1042, "商品已抢完"),
|
||||||
|
SECKILL_LIMIT_EXCEEDED(1043, "超过每人限购数量"),
|
||||||
|
FILE_UPLOAD_ERROR(1050, "文件上传失败"),
|
||||||
|
FILE_TYPE_NOT_ALLOWED(1051, "不支持的文件类型");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
ResultCode(Integer code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.snack.server.config;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
|
import io.swagger.v3.oas.models.info.Contact;
|
||||||
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Knife4j / OpenAPI3 接口文档配置
|
||||||
|
* 访问地址:http://localhost:8080/doc.html
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class Knife4jConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OpenAPI openAPI() {
|
||||||
|
return new OpenAPI()
|
||||||
|
.info(new Info()
|
||||||
|
.title("零食商城 API 文档")
|
||||||
|
.description("server-snack 后端接口文档")
|
||||||
|
.version("v1.0.0")
|
||||||
|
.contact(new Contact()
|
||||||
|
.name("snack-mall")
|
||||||
|
.email("admin@snack.com")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.snack.server.config;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.DbType;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyBatis-Plus 配置
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class MybatisPlusConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||||
|
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||||
|
// 分页插件
|
||||||
|
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.snack.server.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||||
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redis 配置 — 解决默认 JDK 序列化导致的可读性问题
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class RedisConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
||||||
|
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||||
|
template.setConnectionFactory(factory);
|
||||||
|
|
||||||
|
StringRedisSerializer stringSerializer = new StringRedisSerializer();
|
||||||
|
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
|
||||||
|
|
||||||
|
// key 使用 String 序列化
|
||||||
|
template.setKeySerializer(stringSerializer);
|
||||||
|
template.setHashKeySerializer(stringSerializer);
|
||||||
|
// value 使用 JSON 序列化
|
||||||
|
template.setValueSerializer(jsonSerializer);
|
||||||
|
template.setHashValueSerializer(jsonSerializer);
|
||||||
|
|
||||||
|
template.afterPropertiesSet();
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.snack.server.config;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||||
|
import cn.dev33.satoken.router.SaRouter;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sa-Token 路由拦截配置
|
||||||
|
* 统一在此处维护需要登录保护的接口路径
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class SaTokenConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
registry.addInterceptor(new SaInterceptor(handler -> {
|
||||||
|
// 用户端:需要登录的接口
|
||||||
|
SaRouter.match("/api/user/**").check(r -> StpUtil.checkLogin());
|
||||||
|
SaRouter.match("/api/cart/**").check(r -> StpUtil.checkLogin());
|
||||||
|
SaRouter.match("/api/orders/**").check(r -> StpUtil.checkLogin());
|
||||||
|
SaRouter.match("/api/seckill/buy").check(r -> StpUtil.checkLogin());
|
||||||
|
SaRouter.match("/api/coupons/*/receive").check(r -> StpUtil.checkLogin());
|
||||||
|
SaRouter.match("/api/user/coupons").check(r -> StpUtil.checkLogin());
|
||||||
|
|
||||||
|
// 管理端:需要登录的接口
|
||||||
|
SaRouter.match("/api/admin/**").check(r -> StpUtil.checkLogin());
|
||||||
|
})).addPathPatterns("/**");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.snack.server.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web MVC 配置:跨域 + 静态资源(本地上传文件访问)
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class WebMvcConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry.addMapping("/**")
|
||||||
|
.allowedOriginPatterns("*")
|
||||||
|
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||||
|
.allowedHeaders("*")
|
||||||
|
.allowCredentials(true)
|
||||||
|
.maxAge(3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
|
// 本地上传文件通过 /uploads/** 访问
|
||||||
|
registry.addResourceHandler("/uploads/**")
|
||||||
|
.addResourceLocations("file:./uploads/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.snack.server.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sa-Token 登录类型常量(区分用户端与管理端)
|
||||||
|
*/
|
||||||
|
public interface LoginType {
|
||||||
|
/** 普通用户 */
|
||||||
|
String USER = "user";
|
||||||
|
/** 管理员 */
|
||||||
|
String ADMIN = "admin";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.snack.server.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redis Key 常量
|
||||||
|
*/
|
||||||
|
public interface RedisKey {
|
||||||
|
|
||||||
|
/** 抢购商品库存:seckill:stock:{activityId}:{productId} */
|
||||||
|
String SECKILL_STOCK = "seckill:stock:%d:%d";
|
||||||
|
|
||||||
|
/** 用户抢购记录:seckill:user:{userId}:{activityId}:{productId} */
|
||||||
|
String SECKILL_USER_RECORD = "seckill:user:%d:%d:%d";
|
||||||
|
|
||||||
|
/** 客服在线用户集合 */
|
||||||
|
String CHAT_ONLINE_USERS = "chat:online:users";
|
||||||
|
|
||||||
|
/** 会话未读消息数:chat:unread:{sessionId} */
|
||||||
|
String CHAT_UNREAD = "chat:unread:%d";
|
||||||
|
|
||||||
|
/** 验证码:captcha:{uuid} */
|
||||||
|
String CAPTCHA = "captcha:%s";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.snack.server.exception;
|
||||||
|
|
||||||
|
import com.snack.server.common.ResultCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务异常
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class BusinessException extends RuntimeException {
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
|
||||||
|
public BusinessException(String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = ResultCode.FAIL.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessException(ResultCode resultCode) {
|
||||||
|
super(resultCode.getMessage());
|
||||||
|
this.code = resultCode.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessException(Integer code, String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
package com.snack.server.handler;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.exception.NotPermissionException;
|
||||||
|
import com.snack.server.common.Result;
|
||||||
|
import com.snack.server.common.ResultCode;
|
||||||
|
import com.snack.server.exception.BusinessException;
|
||||||
|
import jakarta.validation.ConstraintViolation;
|
||||||
|
import jakarta.validation.ConstraintViolationException;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.validation.FieldError;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局异常处理器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
/** 业务异常 */
|
||||||
|
@ExceptionHandler(BusinessException.class)
|
||||||
|
public Result<Void> handleBusinessException(BusinessException e) {
|
||||||
|
log.warn("业务异常:{}", e.getMessage());
|
||||||
|
return Result.fail(e.getCode(), e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @Valid 校验失败(RequestBody) */
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public Result<Void> handleMethodArgumentNotValid(MethodArgumentNotValidException e) {
|
||||||
|
String message = e.getBindingResult().getFieldErrors()
|
||||||
|
.stream()
|
||||||
|
.map(FieldError::getDefaultMessage)
|
||||||
|
.collect(Collectors.joining("; "));
|
||||||
|
return Result.fail(ResultCode.PARAM_ERROR.getCode(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @Valid 校验失败(Form/Path 参数) */
|
||||||
|
@ExceptionHandler(BindException.class)
|
||||||
|
public Result<Void> handleBindException(BindException e) {
|
||||||
|
String message = e.getFieldErrors()
|
||||||
|
.stream()
|
||||||
|
.map(FieldError::getDefaultMessage)
|
||||||
|
.collect(Collectors.joining("; "));
|
||||||
|
return Result.fail(ResultCode.PARAM_ERROR.getCode(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @Validated 方法参数校验失败 */
|
||||||
|
@ExceptionHandler(ConstraintViolationException.class)
|
||||||
|
public Result<Void> handleConstraintViolation(ConstraintViolationException e) {
|
||||||
|
String message = e.getConstraintViolations()
|
||||||
|
.stream()
|
||||||
|
.map(ConstraintViolation::getMessage)
|
||||||
|
.collect(Collectors.joining("; "));
|
||||||
|
return Result.fail(ResultCode.PARAM_ERROR.getCode(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sa-Token 未登录异常 */
|
||||||
|
@ExceptionHandler(NotLoginException.class)
|
||||||
|
public Result<Void> handleNotLoginException(NotLoginException e) {
|
||||||
|
return Result.fail(ResultCode.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sa-Token 无权限异常 */
|
||||||
|
@ExceptionHandler(NotPermissionException.class)
|
||||||
|
public Result<Void> handleNotPermissionException(NotPermissionException e) {
|
||||||
|
return Result.fail(ResultCode.FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 兜底异常 */
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public Result<Void> handleException(Exception e) {
|
||||||
|
log.error("系统异常:", e);
|
||||||
|
return Result.fail(ResultCode.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: server-snack
|
||||||
|
|
||||||
|
# 数据源配置
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://localhost:3306/snack_mall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
|
||||||
|
username: root
|
||||||
|
password: 123456
|
||||||
|
|
||||||
|
# Redis 配置
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
password:
|
||||||
|
database: 0
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
max-active: 8
|
||||||
|
max-idle: 8
|
||||||
|
min-idle: 0
|
||||||
|
max-wait: -1ms
|
||||||
|
|
||||||
|
# 文件上传大小限制
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 10MB
|
||||||
|
max-request-size: 20MB
|
||||||
|
|
||||||
|
# MyBatis-Plus 配置
|
||||||
|
mybatis-plus:
|
||||||
|
mapper-locations: classpath*:com/snack/server/mapper/**/*.xml
|
||||||
|
type-aliases-package: com.snack.server.entity
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
global-config:
|
||||||
|
db-config:
|
||||||
|
id-type: auto
|
||||||
|
logic-delete-field: deleted
|
||||||
|
logic-delete-value: 1
|
||||||
|
logic-not-delete-value: 0
|
||||||
|
|
||||||
|
# Sa-Token 配置
|
||||||
|
sa-token:
|
||||||
|
# token 名称(同时也是 cookie 名称)
|
||||||
|
token-name: Authorization
|
||||||
|
# token 有效期(单位:秒),默认30天,-1 代表永不过期
|
||||||
|
timeout: 2592000
|
||||||
|
# token 最低活跃频率(单位:秒),-1 表示不限制
|
||||||
|
active-timeout: -1
|
||||||
|
# 是否允许同一账号多地同时登录(为 true 时允许一起登录,为 false 时新登录挤掉旧登录)
|
||||||
|
is-concurrent: true
|
||||||
|
# 在多人登录同一账号时,是否共用一个 token
|
||||||
|
is-share: false
|
||||||
|
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||||
|
token-style: uuid
|
||||||
|
# 是否输出操作日志
|
||||||
|
is-log: false
|
||||||
|
|
||||||
|
# Knife4j 配置
|
||||||
|
knife4j:
|
||||||
|
enable: true
|
||||||
|
setting:
|
||||||
|
language: zh_cn
|
||||||
|
|
||||||
|
# 文件上传本地存储路径(本地存储时使用)
|
||||||
|
snack:
|
||||||
|
upload:
|
||||||
|
# 文件上传根路径
|
||||||
|
path: ./uploads/
|
||||||
|
# 访问域名前缀(用于拼接图片 URL)
|
||||||
|
domain: http://localhost:8080/uploads/
|
||||||
|
|
@ -0,0 +1,855 @@
|
||||||
|
# 基于 SpringBoot + Vue 的零食商城系统 — 功能设计文档
|
||||||
|
|
||||||
|
> 文档版本:v1.2
|
||||||
|
> 创建日期:2026-06-01
|
||||||
|
> 更新日期:2026-06-01(新增系统公告、优惠券、限时抢购活动模块)
|
||||||
|
> 适用项目:零食商城毕业设计
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
|
||||||
|
1. [系统概述](#一系统概述)
|
||||||
|
2. [系统架构](#二系统架构)
|
||||||
|
3. [功能模块设计](#三功能模块设计)
|
||||||
|
- 3.1 [用户端功能(前台)](#31-用户端功能前台)
|
||||||
|
- 3.2 [管理端功能(后台)](#32-管理端功能后台)
|
||||||
|
- 3.3 [客服聊天模块](#33-客服聊天模块)
|
||||||
|
- 3.4 [系统公告模块](#34-系统公告模块)
|
||||||
|
- 3.5 [优惠券模块](#35-优惠券模块)
|
||||||
|
- 3.6 [限时抢购活动模块](#36-限时抢购活动模块)
|
||||||
|
4. [数据库设计](#四数据库设计)
|
||||||
|
5. [技术亮点](#五技术亮点)
|
||||||
|
6. [项目目录结构](#六项目目录结构)
|
||||||
|
7. [接口设计规范](#七接口设计规范)
|
||||||
|
8. [毕设论文结构建议](#八毕设论文结构建议)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、系统概述
|
||||||
|
|
||||||
|
本项目是一个基于 **SpringBoot + Vue3** 前后端分离架构的零食电商平台,旨在为用户提供便捷的在线零食购物体验,同时为管理员提供高效的商品与订单管理后台。
|
||||||
|
|
||||||
|
### 核心目标
|
||||||
|
|
||||||
|
- 用户端:商品浏览、搜索、购物车、下单支付、订单追踪、个人中心、在线客服聊天、系统公告、优惠券、限时抢购
|
||||||
|
- 管理端:数据统计可视化、商品管理、订单管理、用户管理、客服消息处理、公告管理、优惠券管理、活动管理、系统配置
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、系统架构
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ 前端层 (Vue3) │
|
||||||
|
├─────────────────────────┬───────────────────────────────┤
|
||||||
|
│ 用户端 (前台) │ 管理端 (后台) │
|
||||||
|
│ - 首页/商品浏览/购物车 │ - 数据可视化仪表盘 │
|
||||||
|
│ - 订单/个人中心 │ - 商品/订单/用户管理 │
|
||||||
|
│ - 登录注册 │ - 系统设置 │
|
||||||
|
│ - 在线客服聊天 │ - 客服消息处理 │
|
||||||
|
└─────────────────────────┴───────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌─────────────────────────┴───────────────────────────────┐
|
||||||
|
│ 后端层 (SpringBoot + MyBatis-Plus) │
|
||||||
|
│ - RESTful API 接口 │
|
||||||
|
│ - WebSocket 实时消息推送 │
|
||||||
|
│ - JWT 认证 / 权限控制 │
|
||||||
|
│ - Redis 缓存 / 消息队列 │
|
||||||
|
│ - MySQL 数据持久化 │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 技术栈
|
||||||
|
|
||||||
|
| 层级 | 技术选型 | 版本建议 |
|
||||||
|
|------|----------|----------|
|
||||||
|
| 前端用户端 | Vue3 + Vant | Vue 3.3+ |
|
||||||
|
| 前端管理端 | Vue3 + Element Plus | Vue 3.3+ |
|
||||||
|
| 后端框架 | SpringBoot | 2.7.x / 3.x |
|
||||||
|
| ORM 框架 | MyBatis-Plus | 3.5.x |
|
||||||
|
| 数据库 | MySQL | 8.0 |
|
||||||
|
| 缓存 | Redis | 6.x / 7.x |
|
||||||
|
| 实时通信 | WebSocket (Spring WebSocket) | — |
|
||||||
|
| 构建工具 | Maven | 3.8+ |
|
||||||
|
| 接口文档 | Knife4j (Swagger) | 4.x |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、功能模块设计
|
||||||
|
|
||||||
|
### 3.1 用户端功能(前台)
|
||||||
|
|
||||||
|
#### 3.1.1 首页模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 轮播图展示 | 顶部广告位,支持点击跳转商品详情,后台可配置 | 高 |
|
||||||
|
| 分类导航 | 零食分类快速入口(坚果、糖果、膨化、肉脯、蜜饯等) | 高 |
|
||||||
|
| 热销推荐 | 基于销量排序的 Top N 商品推荐 | 高 |
|
||||||
|
| 新品上架 | 最新上架商品展示(按上架时间倒序) | 中 |
|
||||||
|
| 限时特惠 | 折扣商品专区展示 | 中 |
|
||||||
|
|
||||||
|
#### 3.1.2 商品模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 分类浏览 | 多级分类筛选(一级:休闲零食 / 二级:坚果炒货 / 三级:夏威夷果) | 高 |
|
||||||
|
| 商品搜索 | 关键词搜索 + 搜索历史记录 + 热门搜索标签 | 高 |
|
||||||
|
| 商品列表 | 综合排序 / 销量排序 / 价格升序降序,分页加载 | 高 |
|
||||||
|
| 商品详情 | 商品轮播图、价格展示、规格选择(口味/重量)、库存显示、商品详情图、用户评价列表 | 高 |
|
||||||
|
|
||||||
|
#### 3.1.3 购物车模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 添加商品 | 选择规格、数量加入购物车,未登录跳转登录 | 高 |
|
||||||
|
| 购物车管理 | 商品全选 / 单选、修改数量、删除单个 / 批量删除、清空购物车 | 高 |
|
||||||
|
| 价格计算 | 实时计算选中商品总价,展示优惠后价格 | 高 |
|
||||||
|
|
||||||
|
#### 3.1.4 订单模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 确认订单 | 选择收货地址、展示商品清单、填写备注信息、计算实付金额 | 高 |
|
||||||
|
| 下单支付 | 模拟支付流程(适合毕设演示),支付成功后扣减库存 | 高 |
|
||||||
|
| 订单列表 | 全部 / 待付款 / 待发货 / 待收货 / 已完成 / 已取消,分页展示 | 高 |
|
||||||
|
| 订单详情 | 订单信息、商品明细、物流信息(模拟)、订单状态时间轴 | 高 |
|
||||||
|
| 确认收货 | 用户点击确认收货,订单状态变更为已完成 | 高 |
|
||||||
|
| 取消订单 | 待付款状态下用户可主动取消订单 | 高 |
|
||||||
|
|
||||||
|
#### 3.1.5 用户中心模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 个人信息 | 查看和修改昵称、头像上传、手机号绑定 | 高 |
|
||||||
|
| 收货地址 | 新增 / 编辑 / 删除收货地址,设置默认地址 | 高 |
|
||||||
|
| 我的收藏 | 收藏商品列表,支持取消收藏,跳转商品详情 | 中 |
|
||||||
|
| 修改密码 | 原密码验证后修改登录密码 | 中 |
|
||||||
|
|
||||||
|
#### 3.1.6 客服聊天模块(用户端)
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 发起会话 | 用户点击"联系客服"按钮,创建或恢复与客服的聊天会话 | 高 |
|
||||||
|
| 实时发送消息 | 基于 WebSocket 实时发送文字消息 | 高 |
|
||||||
|
| 发送图片 | 用户可上传并发送图片(商品截图、问题截图等) | 中 |
|
||||||
|
| 历史消息记录 | 进入聊天页自动加载历史消息(分页上拉加载更多) | 高 |
|
||||||
|
| 消息状态显示 | 显示消息发送时间、已读 / 未读状态 | 中 |
|
||||||
|
| 客服状态显示 | 显示客服在线 / 离线状态,离线时提示留言 | 中 |
|
||||||
|
| 常见问题快捷回复 | 提供常见问题入口(如:订单查询 / 退换货 / 物流查询),点击自动发送预设问题 | 中 |
|
||||||
|
| 会话结束 | 用户可主动结束会话 | 低 |
|
||||||
|
|
||||||
|
#### 3.1.7 系统公告模块(用户端)
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 首页公告横幅 | 首页顶部滚动展示最新公告,点击查看详情 | 高 |
|
||||||
|
| 公告列表 | 公告中心页面,按时间倒序展示所有有效公告 | 高 |
|
||||||
|
| 公告详情 | 查看公告完整内容(富文本),展示发布时间 | 高 |
|
||||||
|
| 未读公告提示 | 用户登录后,若有未读新公告则弹出提示弹窗 | 中 |
|
||||||
|
|
||||||
|
#### 3.1.8 优惠券模块(用户端)
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 领券中心 | 展示当前可领取的优惠券列表(满减券/折扣券/无门槛券) | 高 |
|
||||||
|
| 领取优惠券 | 登录用户点击领取,每人每张限领一次,库存不足则禁止领取 | 高 |
|
||||||
|
| 我的优惠券 | 查看已领取的优惠券(未使用 / 已使用 / 已过期),分 Tab 展示 | 高 |
|
||||||
|
| 下单使用优惠券 | 确认订单页选择可用优惠券,自动计算优惠后价格 | 高 |
|
||||||
|
| 优惠券有效期提示 | 优惠券临近到期(剩余 3 天内)展示"即将过期"红色标签 | 中 |
|
||||||
|
|
||||||
|
#### 3.1.9 限时抢购模块(用户端)
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 抢购活动列表 | 首页展示当前进行中及即将开始的抢购活动场次 | 高 |
|
||||||
|
| 活动倒计时 | 未开始活动显示开始倒计时,进行中活动显示结束倒计时 | 高 |
|
||||||
|
| 抢购商品列表 | 展示活动内所有商品,标注原价、抢购价、库存进度条 | 高 |
|
||||||
|
| 立即抢购 | 点击"立即抢购"跳转订单确认页,使用抢购价下单,库存为 0 时按钮置灰 | 高 |
|
||||||
|
| 防超卖保障 | 基于 Redis 原子操作扣减库存,防止超卖 | 高 |
|
||||||
|
| 购买限制 | 每人每场活动每个商品限购数量可配置(如限购 2 件) | 中 |
|
||||||
|
| 活动历史 | 用户可查看已结束的历史抢购活动 | 低 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.2 管理端功能(后台)
|
||||||
|
|
||||||
|
#### 3.2.1 登录模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 管理员登录 | 账号 + 密码 + 图形验证码登录 | 高 |
|
||||||
|
|
||||||
|
#### 3.2.2 仪表盘模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 核心数据卡片 | 今日订单数、今日销售额、总用户数、总商品数 | 高 |
|
||||||
|
| 销售趋势图 | 近 7 天 / 近 30 天销售额折线图(ECharts) | 高 |
|
||||||
|
| 订单状态分布 | 订单状态饼图(待付款 / 待发货 / 待收货 / 已完成 / 已取消) | 中 |
|
||||||
|
| 热销商品 Top10 | 销量前 10 商品柱状图 | 中 |
|
||||||
|
|
||||||
|
#### 3.2.3 用户管理模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 用户列表 | 分页查询,支持按用户名 / 手机号模糊搜索 | 高 |
|
||||||
|
| 状态管理 | 启用 / 禁用用户账号(禁用后无法登录) | 高 |
|
||||||
|
| 查看详情 | 查看用户基本信息、历史订单记录 | 中 |
|
||||||
|
|
||||||
|
#### 3.2.4 商品分类模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 分类列表 | 树形结构展示一级 / 二级分类 | 高 |
|
||||||
|
| 分类管理 | 新增 / 编辑 / 删除分类 | 高 |
|
||||||
|
| 分类排序 | 手动设置排序号,控制前台展示顺序 | 中 |
|
||||||
|
|
||||||
|
#### 3.2.5 商品管理模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 商品列表 | 分页查询,支持按名称 / 分类 / 上下架状态筛选 | 高 |
|
||||||
|
| 商品发布 | 填写商品信息(名称/简介/详情)、上传主图和详情图、设置 SKU 规格(口味/重量/价格/库存) | 高 |
|
||||||
|
| 商品编辑 | 修改商品所有信息 | 高 |
|
||||||
|
| 上下架控制 | 单独或批量上架 / 下架商品,控制前台展示 | 高 |
|
||||||
|
| 库存管理 | 查看和修改各规格商品库存数量 | 高 |
|
||||||
|
|
||||||
|
#### 3.2.6 订单管理模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 订单列表 | 分页查询,支持按订单号 / 用户名 / 订单状态筛选 | 高 |
|
||||||
|
| 订单详情 | 查看订单完整信息、收货地址、商品明细、金额明细 | 高 |
|
||||||
|
| 订单发货 | 填写物流公司和物流单号,变更订单状态为"待收货" | 高 |
|
||||||
|
| 取消订单 | 管理员主动取消订单(模拟退款流程) | 中 |
|
||||||
|
|
||||||
|
#### 3.2.7 轮播图管理模块
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 轮播图列表 | 增删改查轮播图 | 高 |
|
||||||
|
| 跳转配置 | 设置点击图片后跳转的商品 ID 或外部链接 | 高 |
|
||||||
|
| 排序控制 | 拖拽或手动设置轮播图展示顺序 | 中 |
|
||||||
|
|
||||||
|
#### 3.2.8 系统管理模块(可选)
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 管理员管理 | 超级管理员可添加 / 编辑 / 删除普通管理员账号 | 低 |
|
||||||
|
| 角色权限 | 基于角色的权限控制(RBAC) | 低 |
|
||||||
|
|
||||||
|
#### 3.2.9 客服消息管理模块(后台)
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 会话列表 | 展示所有用户发起的聊天会话,显示用户信息、最新消息、未读消息数 | 高 |
|
||||||
|
| 实时接收消息 | 基于 WebSocket 实时接收用户消息,浏览器标题栏闪烁提示未读 | 高 |
|
||||||
|
| 回复消息 | 客服输入文字或发送图片回复用户 | 高 |
|
||||||
|
| 会话状态管理 | 标记会话为处理中 / 已解决 / 待处理 | 高 |
|
||||||
|
| 历史会话查询 | 查询已结束的历史会话记录,支持按用户名 / 时间筛选 | 中 |
|
||||||
|
| 快捷回复模板 | 管理预设快捷回复话术(如:您好,感谢咨询...),一键插入消息框 | 中 |
|
||||||
|
| 转接会话 | 将当前会话转接给其他在线客服 | 低 |
|
||||||
|
|
||||||
|
#### 3.2.10 系统公告管理模块(后台)
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 公告列表 | 分页查询所有公告,支持按标题 / 状态 / 时间筛选 | 高 |
|
||||||
|
| 发布公告 | 填写公告标题、正文内容(富文本),设置生效时间和失效时间 | 高 |
|
||||||
|
| 编辑公告 | 修改已发布公告的内容和有效期 | 高 |
|
||||||
|
| 公告状态控制 | 手动上线 / 下线公告,下线后前台不再显示 | 高 |
|
||||||
|
| 公告置顶 | 设置重要公告为置顶,优先在前台展示 | 中 |
|
||||||
|
|
||||||
|
#### 3.2.11 优惠券管理模块(后台)
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 优惠券列表 | 分页查询所有优惠券,支持按名称 / 类型 / 状态筛选 | 高 |
|
||||||
|
| 创建优惠券 | 配置优惠券类型(满减/折扣/无门槛)、面额/折扣率、使用门槛、发放总量、每人限领数、有效期 | 高 |
|
||||||
|
| 编辑优惠券 | 修改未开始发放的优惠券信息 | 高 |
|
||||||
|
| 上下架控制 | 控制优惠券是否在领券中心展示 | 高 |
|
||||||
|
| 领取记录查询 | 查询指定优惠券的领取明细(用户名、领取时间、使用状态) | 中 |
|
||||||
|
| 数据统计 | 展示每张优惠券的已领数量 / 总量、已使用数量 | 中 |
|
||||||
|
|
||||||
|
#### 3.2.12 限时抢购活动管理模块(后台)
|
||||||
|
|
||||||
|
| 功能点 | 描述 | 优先级 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 活动列表 | 分页查询所有活动,按状态(未开始 / 进行中 / 已结束)筛选 | 高 |
|
||||||
|
| 创建活动 | 填写活动名称、活动场次时间段(开始时间 / 结束时间) | 高 |
|
||||||
|
| 添加活动商品 | 从商品库中选择商品加入活动,配置抢购价格、抢购库存、每人限购数量 | 高 |
|
||||||
|
| 移除活动商品 | 活动未开始时可移除商品,进行中活动不可删除 | 高 |
|
||||||
|
| 活动状态管理 | 手动提前结束活动(结束后恢复商品正常价格) | 中 |
|
||||||
|
| 抢购数据统计 | 查看每场活动的总参与人数、总销售量、总销售额 | 中 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.3 客服聊天模块
|
||||||
|
|
||||||
|
#### 3.3.1 整体交互流程
|
||||||
|
|
||||||
|
```
|
||||||
|
用户点击"联系客服"
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
检查是否已有进行中的会话
|
||||||
|
│
|
||||||
|
有 ──┤── 无
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
恢复历史会话 创建新会话(生成 session_id)
|
||||||
|
│ │
|
||||||
|
└───┬────┘
|
||||||
|
▼
|
||||||
|
建立 WebSocket 连接
|
||||||
|
│
|
||||||
|
┌──────┴──────┐
|
||||||
|
│ │
|
||||||
|
用户发消息 客服回复消息
|
||||||
|
│ │
|
||||||
|
└──────┬──────┘
|
||||||
|
▼
|
||||||
|
消息实时推送 + 持久化到 MySQL
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3.3.2 消息类型
|
||||||
|
|
||||||
|
| 消息类型 | type 值 | 说明 |
|
||||||
|
|----------|---------|------|
|
||||||
|
| 文本消息 | `text` | 普通文字内容 |
|
||||||
|
| 图片消息 | `image` | 图片 URL |
|
||||||
|
| 系统消息 | `system` | 会话创建 / 结束 / 转接提示 |
|
||||||
|
| 快捷问题 | `quick` | 用户点击快捷问题入口发送的预设文本 |
|
||||||
|
|
||||||
|
#### 3.3.3 技术实现方案
|
||||||
|
|
||||||
|
| 技术点 | 方案 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| 实时通信 | Spring WebSocket + STOMP | 后端实现 WebSocket 服务,前端通过 stomp.js 连接 |
|
||||||
|
| 连接认证 | WebSocket 握手时携带 JWT Token | 握手拦截器中验证 Token,防止未登录用户建立连接 |
|
||||||
|
| 消息持久化 | 每条消息写入 `chat_message` 表 | 保障历史消息可查 |
|
||||||
|
| 在线状态 | Redis 存储在线用户集合 | key: `chat:online:users`,客服登录 / 断开时更新 |
|
||||||
|
| 未读消息数 | Redis 计数器 | key: `chat:unread:{sessionId}`,客服回复后清零 |
|
||||||
|
| 离线消息 | 消息先持久化,用户重连后推送 | 保障消息不丢失 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.4 系统公告模块
|
||||||
|
|
||||||
|
#### 3.4.1 功能流程
|
||||||
|
|
||||||
|
```
|
||||||
|
管理员后台发布公告(设置有效期)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
公告写入数据库,状态为"已上线"
|
||||||
|
│
|
||||||
|
┌────┴────┐
|
||||||
|
▼ ▼
|
||||||
|
首页横幅 公告列表页
|
||||||
|
滚动展示 按时间倒序展示
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
用户登录时检查是否有未读新公告
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
弹出公告提示弹窗(标记已读后不再弹出)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3.4.2 公告类型
|
||||||
|
|
||||||
|
| 类型 | 说明 | 前台展示位置 |
|
||||||
|
|------|------|------------|
|
||||||
|
| 普通公告 | 常规运营通知 | 公告列表 |
|
||||||
|
| 重要公告 | 系统升级、重大活动通知 | 首页横幅 + 弹窗 |
|
||||||
|
| 活动公告 | 促销、节日活动预告 | 首页横幅 + 公告列表 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.5 优惠券模块
|
||||||
|
|
||||||
|
#### 3.5.1 优惠券类型设计
|
||||||
|
|
||||||
|
| 类型 | 字段标识 | 说明 | 举例 |
|
||||||
|
|------|----------|------|------|
|
||||||
|
| 满减券 | `full_reduce` | 满指定金额减固定金额 | 满 100 减 20 |
|
||||||
|
| 折扣券 | `discount` | 按折扣率打折,可设置最高抵扣上限 | 9 折,最高抵扣 50 元 |
|
||||||
|
| 无门槛券 | `no_threshold` | 无需满额,直接抵扣固定金额 | 直减 5 元 |
|
||||||
|
|
||||||
|
#### 3.5.2 优惠券使用规则
|
||||||
|
|
||||||
|
- 每人每张优惠券限领 N 张(后台可配置)
|
||||||
|
- 优惠券有固定有效期(如:领取后 7 天内有效,或指定日期范围)
|
||||||
|
- 下单时同一笔订单只能使用一张优惠券
|
||||||
|
- 取消订单后优惠券自动退回(模拟实现)
|
||||||
|
- 过期未使用的优惠券不可继续使用
|
||||||
|
|
||||||
|
#### 3.5.3 优惠金额计算逻辑
|
||||||
|
|
||||||
|
```
|
||||||
|
用户选择优惠券
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
校验:订单金额 >= 使用门槛?
|
||||||
|
│
|
||||||
|
是 │ 否(不可用,置灰)
|
||||||
|
▼
|
||||||
|
计算优惠金额:
|
||||||
|
满减券:优惠金额 = 券面值
|
||||||
|
折扣券:优惠金额 = min(订单金额 × (1 - 折扣率), 最高抵扣)
|
||||||
|
无门槛券:优惠金额 = 券面值
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
实付金额 = 订单金额 - 优惠金额(最低 0.01 元)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.6 限时抢购活动模块
|
||||||
|
|
||||||
|
#### 3.6.1 活动状态流转
|
||||||
|
|
||||||
|
```
|
||||||
|
创建活动(未开始)
|
||||||
|
│
|
||||||
|
到达开始时间
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
活动进行中 ──── 库存耗尽 ──── 活动结束(售罄)
|
||||||
|
│
|
||||||
|
到达结束时间 / 管理员手动结束
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
活动已结束
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3.6.2 防超卖设计(核心技术点)
|
||||||
|
|
||||||
|
```
|
||||||
|
用户发起抢购请求
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
① Redis DECR 原子操作扣减库存(key: seckill:stock:{activityId}:{productId})
|
||||||
|
│
|
||||||
|
库存 >= 0 ?
|
||||||
|
是 │ 否(返回"已抢完")
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
② 将下单任务写入异步队列(Redis List / MQ)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
③ 后台消费队列,创建订单写入 MySQL
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
④ 返回用户"抢购成功,请尽快付款"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3.6.3 限购校验逻辑
|
||||||
|
|
||||||
|
| 校验项 | 说明 |
|
||||||
|
|--------|------|
|
||||||
|
| 活动状态校验 | 活动必须处于"进行中"状态 |
|
||||||
|
| 库存校验 | Redis 中库存必须 > 0 |
|
||||||
|
| 重复购买校验 | 同一用户同一活动同一商品购买数量不超过限购数 |
|
||||||
|
| 登录校验 | 未登录用户跳转登录页 |
|
||||||
|
|
||||||
|
### 4.1 核心数据表清单
|
||||||
|
|
||||||
|
| 表名 | 中文名 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| `user` | 用户表 | 存储普通注册用户 |
|
||||||
|
| `admin` | 管理员表 | 存储后台管理员 |
|
||||||
|
| `category` | 商品分类表 | 支持多级分类(parent_id) |
|
||||||
|
| `product` | 商品表 | 商品基础信息 |
|
||||||
|
| `product_sku` | 商品 SKU 表 | 商品规格(口味/重量/价格/库存) |
|
||||||
|
| `cart` | 购物车表 | 用户购物车商品 |
|
||||||
|
| `orders` | 订单主表 | 订单基本信息、收货地址、金额 |
|
||||||
|
| `order_item` | 订单商品明细表 | 订单中的商品列表 |
|
||||||
|
| `address` | 收货地址表 | 用户收货地址 |
|
||||||
|
| `favorite` | 收藏表 | 用户收藏的商品 |
|
||||||
|
| `banner` | 轮播图表 | 首页轮播图配置 |
|
||||||
|
| `chat_session` | 客服会话表 | 用户与客服的聊天会话 |
|
||||||
|
| `chat_message` | 聊天消息表 | 每条聊天消息记录 |
|
||||||
|
| `quick_reply` | 快捷回复模板表 | 客服预设快捷话术 |
|
||||||
|
| `notice` | 系统公告表 | 公告标题、内容、有效期 |
|
||||||
|
| `coupon` | 优惠券表 | 优惠券配置信息 |
|
||||||
|
| `user_coupon` | 用户优惠券表 | 用户领取记录及使用状态 |
|
||||||
|
| `seckill_activity` | 限时抢购活动表 | 活动时间、名称、状态 |
|
||||||
|
| `seckill_product` | 抢购商品表 | 活动商品、抢购价、库存、限购数 |
|
||||||
|
| `seckill_order` | 抢购订单表 | 记录用户抢购记录(防重复购买) |
|
||||||
|
|
||||||
|
### 4.2 核心表结构(简化版)
|
||||||
|
|
||||||
|
#### user(用户表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| username | VARCHAR(50) | 用户名 |
|
||||||
|
| password | VARCHAR(100) | 加密密码 |
|
||||||
|
| phone | VARCHAR(20) | 手机号 |
|
||||||
|
| avatar | VARCHAR(255) | 头像 URL |
|
||||||
|
| status | TINYINT | 状态:0-禁用 1-启用 |
|
||||||
|
| create_time | DATETIME | 创建时间 |
|
||||||
|
| update_time | DATETIME | 更新时间 |
|
||||||
|
|
||||||
|
#### category(商品分类表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| name | VARCHAR(50) | 分类名称 |
|
||||||
|
| parent_id | BIGINT | 父分类 ID,0 表示一级分类 |
|
||||||
|
| level | TINYINT | 层级:1-一级 2-二级 3-三级 |
|
||||||
|
| sort | INT | 排序号 |
|
||||||
|
| icon | VARCHAR(255) | 分类图标 |
|
||||||
|
|
||||||
|
#### product(商品表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| name | VARCHAR(200) | 商品名称 |
|
||||||
|
| category_id | BIGINT | 分类 ID |
|
||||||
|
| main_image | VARCHAR(255) | 商品主图 |
|
||||||
|
| detail | TEXT | 商品详情(富文本/HTML) |
|
||||||
|
| status | TINYINT | 状态:0-下架 1-上架 |
|
||||||
|
| create_time | DATETIME | 创建时间 |
|
||||||
|
|
||||||
|
#### product_sku(商品 SKU 表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| product_id | BIGINT | 商品 ID |
|
||||||
|
| sku_name | VARCHAR(100) | 规格名称(如:香辣味/500g) |
|
||||||
|
| price | DECIMAL(10,2) | 售价 |
|
||||||
|
| stock | INT | 库存数量 |
|
||||||
|
|
||||||
|
#### orders(订单主表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| order_no | VARCHAR(32) | 订单编号(唯一) |
|
||||||
|
| user_id | BIGINT | 用户 ID |
|
||||||
|
| total_amount | DECIMAL(10,2) | 订单总金额 |
|
||||||
|
| pay_amount | DECIMAL(10,2) | 实付金额 |
|
||||||
|
| status | TINYINT | 状态:0-待付款 1-待发货 2-待收货 3-已完成 4-已取消 |
|
||||||
|
| receiver_name | VARCHAR(50) | 收货人姓名 |
|
||||||
|
| receiver_phone | VARCHAR(20) | 收货人电话 |
|
||||||
|
| receiver_address | VARCHAR(255) | 收货地址 |
|
||||||
|
| remark | VARCHAR(500) | 订单备注 |
|
||||||
|
| pay_time | DATETIME | 支付时间 |
|
||||||
|
| deliver_time | DATETIME | 发货时间 |
|
||||||
|
| receive_time | DATETIME | 收货时间 |
|
||||||
|
| create_time | DATETIME | 创建时间 |
|
||||||
|
|
||||||
|
#### chat_session(客服会话表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| session_no | VARCHAR(32) | 会话编号(唯一) |
|
||||||
|
| user_id | BIGINT | 发起用户 ID |
|
||||||
|
| admin_id | BIGINT | 接待客服 ID(NULL 表示待分配) |
|
||||||
|
| status | TINYINT | 状态:0-待处理 1-处理中 2-已解决 |
|
||||||
|
| create_time | DATETIME | 会话创建时间 |
|
||||||
|
| update_time | DATETIME | 最后活跃时间 |
|
||||||
|
|
||||||
|
#### chat_message(聊天消息表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| session_id | BIGINT | 所属会话 ID |
|
||||||
|
| sender_id | BIGINT | 发送者 ID |
|
||||||
|
| sender_type | TINYINT | 发送者类型:0-用户 1-客服 |
|
||||||
|
| type | VARCHAR(10) | 消息类型:text / image / system |
|
||||||
|
| content | TEXT | 消息内容(文字或图片URL) |
|
||||||
|
| is_read | TINYINT | 是否已读:0-未读 1-已读 |
|
||||||
|
| create_time | DATETIME | 发送时间 |
|
||||||
|
|
||||||
|
#### notice(系统公告表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| title | VARCHAR(200) | 公告标题 |
|
||||||
|
| content | TEXT | 公告正文(富文本) |
|
||||||
|
| type | TINYINT | 类型:0-普通 1-重要 2-活动 |
|
||||||
|
| is_top | TINYINT | 是否置顶:0-否 1-是 |
|
||||||
|
| status | TINYINT | 状态:0-下线 1-上线 |
|
||||||
|
| start_time | DATETIME | 生效时间 |
|
||||||
|
| end_time | DATETIME | 失效时间 |
|
||||||
|
| create_time | DATETIME | 创建时间 |
|
||||||
|
|
||||||
|
#### coupon(优惠券表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| name | VARCHAR(100) | 优惠券名称 |
|
||||||
|
| type | TINYINT | 类型:0-满减 1-折扣 2-无门槛 |
|
||||||
|
| amount | DECIMAL(10,2) | 满减面值 / 折扣率(如 0.9 表示九折) |
|
||||||
|
| min_amount | DECIMAL(10,2) | 最低使用金额(0 表示无门槛) |
|
||||||
|
| max_discount | DECIMAL(10,2) | 折扣券最高抵扣金额(满减/无门槛填0) |
|
||||||
|
| total | INT | 发放总量(-1 表示不限量) |
|
||||||
|
| remain | INT | 剩余可领数量 |
|
||||||
|
| per_limit | INT | 每人限领数量 |
|
||||||
|
| status | TINYINT | 状态:0-未上线 1-已上线 2-已结束 |
|
||||||
|
| start_time | DATETIME | 领取开始时间 |
|
||||||
|
| end_time | DATETIME | 领取截止时间 |
|
||||||
|
| valid_days | INT | 领取后有效天数(与 valid_end_time 二选一) |
|
||||||
|
|
||||||
|
#### user_coupon(用户优惠券表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| user_id | BIGINT | 用户 ID |
|
||||||
|
| coupon_id | BIGINT | 优惠券 ID |
|
||||||
|
| status | TINYINT | 状态:0-未使用 1-已使用 2-已过期 |
|
||||||
|
| order_id | BIGINT | 使用时关联的订单 ID(NULL 表示未使用) |
|
||||||
|
| receive_time | DATETIME | 领取时间 |
|
||||||
|
| use_time | DATETIME | 使用时间 |
|
||||||
|
| expire_time | DATETIME | 过期时间 |
|
||||||
|
|
||||||
|
#### seckill_activity(限时抢购活动表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| name | VARCHAR(100) | 活动名称 |
|
||||||
|
| start_time | DATETIME | 活动开始时间 |
|
||||||
|
| end_time | DATETIME | 活动结束时间 |
|
||||||
|
| status | TINYINT | 状态:0-未开始 1-进行中 2-已结束 |
|
||||||
|
| create_time | DATETIME | 创建时间 |
|
||||||
|
|
||||||
|
#### seckill_product(抢购商品表)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | BIGINT | 主键,自增 |
|
||||||
|
| activity_id | BIGINT | 活动 ID |
|
||||||
|
| product_id | BIGINT | 商品 ID |
|
||||||
|
| sku_id | BIGINT | SKU ID |
|
||||||
|
| seckill_price | DECIMAL(10,2) | 抢购价格 |
|
||||||
|
| seckill_stock | INT | 抢购总库存 |
|
||||||
|
| remain_stock | INT | 剩余库存 |
|
||||||
|
| per_limit | INT | 每人限购数量 |
|
||||||
|
|
||||||
|
| 技术点 | 应用场景 | 说明 |
|
||||||
|
|--------|----------|------|
|
||||||
|
| **JWT + 拦截器** | 用户认证 | Token 无状态登录,用户端和管理端分别签发不同 Token,区分权限 |
|
||||||
|
| **Redis 缓存** | 热点数据加速 | 缓存轮播图、商品分类、热销商品,减少数据库压力,设置合理过期时间 |
|
||||||
|
| **全局异常处理** | 系统健壮性 | `@ControllerAdvice` + `@ExceptionHandler` 统一处理异常和返回格式 |
|
||||||
|
| **统一返回结果** | 接口规范 | 封装统一的 JSON 响应结构(code / message / data) |
|
||||||
|
| **文件上传** | 图片管理 | 商品图、轮播图、头像上传,本地文件系统存储 |
|
||||||
|
| **MyBatis-Plus** | ORM 增强 | 简化 CRUD,使用分页插件、条件构造器、代码生成器 |
|
||||||
|
| **跨域处理** | 前后端分离 | SpringBoot 配置 CORS,支持本地开发联调 |
|
||||||
|
| **参数校验** | 数据合法性 | `@Valid` + `@NotNull` / `@Size` 等注解进行入参校验 |
|
||||||
|
| **数据可视化** | 后台仪表盘 | ECharts 绘制销售趋势图、订单状态饼图、热销商品柱状图 |
|
||||||
|
| **订单状态机** | 订单流转 | 严格定义订单状态流转规则,防止非法状态变更 |
|
||||||
|
| **WebSocket 实时通信** | 客服聊天 | 使用 Spring WebSocket + STOMP 协议实现双向实时消息推送 |
|
||||||
|
| **Redis 在线状态** | 客服聊天 | Redis Set 维护在线用户集合,计数器管理未读消息数 |
|
||||||
|
| **消息持久化** | 客服聊天 | 所有聊天消息写入 MySQL,保障离线消息不丢失,支持历史查询 |
|
||||||
|
| **Redis 原子操作防超卖** | 限时抢购 | `DECR` 原子递减库存,防止高并发下超卖,结合异步队列削峰 |
|
||||||
|
| **优惠券状态机** | 优惠券 | 严格管控优惠券 未使用→已使用/已过期 的状态流转,防止重复核销 |
|
||||||
|
| **定时任务** | 公告/活动/优惠券 | 使用 `@Scheduled` 定时扫描,自动更新过期公告下线、活动状态、优惠券状态 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、项目目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
snack-mall/
|
||||||
|
├── snack-mall-api/ # SpringBoot 后端项目
|
||||||
|
│ ├── src/main/java/com/snack/
|
||||||
|
│ │ ├── SnackMallApplication.java
|
||||||
|
│ │ ├── controller/ # 控制器层(按模块分包)
|
||||||
|
│ │ │ ├── UserController.java
|
||||||
|
│ │ │ ├── ProductController.java
|
||||||
|
│ │ │ ├── OrderController.java
|
||||||
|
│ │ │ └── AdminController.java
|
||||||
|
│ │ │ └── ChatController.java # 客服聊天控制器
|
||||||
|
│ │ ├── websocket/ # WebSocket 相关
|
||||||
|
│ │ │ ├── ChatWebSocketHandler.java
|
||||||
|
│ │ │ ├── WebSocketConfig.java
|
||||||
|
│ │ │ └── StompMessageHandler.java
|
||||||
|
│ │ ├── service/ # 业务逻辑层
|
||||||
|
│ │ │ ├── UserService.java
|
||||||
|
│ │ │ └── impl/
|
||||||
|
│ │ ├── mapper/ # 数据访问层(MyBatis-Plus)
|
||||||
|
│ │ ├── entity/ # 实体类(对应数据库表)
|
||||||
|
│ │ ├── dto/ # 数据传输对象(请求/响应)
|
||||||
|
│ │ ├── vo/ # 视图对象(返回前端的特定结构)
|
||||||
|
│ │ ├── config/ # 配置类(CORS、MyBatisPlus、Redis)
|
||||||
|
│ │ ├── interceptor/ # 拦截器(JWT 认证)
|
||||||
|
│ │ ├── utils/ # 工具类(JWT、文件上传、MD5)
|
||||||
|
│ │ ├── handler/ # 全局异常处理器
|
||||||
|
│ │ └── enums/ # 枚举类(订单状态、用户状态等)
|
||||||
|
│ └── src/main/resources/
|
||||||
|
│ ├── mapper/ # XML 映射文件
|
||||||
|
│ ├── static/uploads/ # 上传文件存储目录
|
||||||
|
│ └── application.yml # 配置文件
|
||||||
|
│
|
||||||
|
├── snack-mall-admin/ # Vue3 管理端项目
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── views/ # 页面视图
|
||||||
|
│ │ │ ├── login/ # 登录页
|
||||||
|
│ │ │ ├── dashboard/ # 仪表盘
|
||||||
|
│ │ │ ├── user/ # 用户管理
|
||||||
|
│ │ │ ├── product/ # 商品管理
|
||||||
|
│ │ │ ├── order/ # 订单管理
|
||||||
|
│ │ │ ├── chat/ # 客服聊天管理
|
||||||
|
│ │ │ └── system/ # 系统设置
|
||||||
|
│ │ ├── components/ # 公共组件
|
||||||
|
│ │ ├── api/ # API 接口请求(按模块)
|
||||||
|
│ │ ├── router/ # Vue Router 路由配置
|
||||||
|
│ │ ├── store/ # Pinia 状态管理
|
||||||
|
│ │ ├── utils/ # 工具函数(request、token、format)
|
||||||
|
│ │ └── App.vue
|
||||||
|
│ ├── package.json
|
||||||
|
│ └── vite.config.js
|
||||||
|
│
|
||||||
|
└── snack-mall-web/ # Vue3 用户端项目
|
||||||
|
├── src/
|
||||||
|
│ ├── views/
|
||||||
|
│ │ ├── home/ # 首页
|
||||||
|
│ │ ├── category/ # 分类页
|
||||||
|
│ │ ├── product/ # 商品详情
|
||||||
|
│ │ ├── cart/ # 购物车
|
||||||
|
│ │ ├── order/ # 订单相关
|
||||||
|
│ │ ├── chat/ # 在线客服聊天
|
||||||
|
│ │ └── user/ # 用户中心
|
||||||
|
│ ├── components/ # 公共组件
|
||||||
|
│ ├── api/
|
||||||
|
│ ├── router/
|
||||||
|
│ ├── store/
|
||||||
|
│ └── utils/
|
||||||
|
├── package.json
|
||||||
|
└── vite.config.js
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、接口设计规范
|
||||||
|
|
||||||
|
### 7.1 RESTful API 设计
|
||||||
|
|
||||||
|
| 资源 | GET | POST | PUT | DELETE |
|
||||||
|
|------|-----|------|-----|--------|
|
||||||
|
| `/api/products` | 获取商品列表 | — | — | — |
|
||||||
|
| `/api/products/{id}` | 获取商品详情 | — | — | — |
|
||||||
|
| `/api/cart` | 获取购物车 | 添加商品 | 修改数量 | 清空购物车 |
|
||||||
|
| `/api/cart/{id}` | — | — | — | 删除购物车商品 |
|
||||||
|
| `/api/orders` | 获取订单列表 | 创建订单 | — | — |
|
||||||
|
| `/api/orders/{id}` | 获取订单详情 | — | 取消订单 | — |
|
||||||
|
| `/api/orders/{id}/pay` | — | 支付订单 | — | — |
|
||||||
|
| `/api/orders/{id}/receive` | — | 确认收货 | — | — |
|
||||||
|
| `/api/chat/sessions` | 获取会话列表 | 创建/恢复会话 | — | — |
|
||||||
|
| `/api/chat/sessions/{id}/messages` | 获取历史消息 | — | — | — |
|
||||||
|
| `/api/chat/quick-replies` | 获取快捷回复列表 | — | — | — |
|
||||||
|
| `/api/notices` | 获取公告列表 | — | — | — |
|
||||||
|
| `/api/notices/{id}` | 获取公告详情 | — | — | — |
|
||||||
|
| `/api/coupons` | 获取可领取优惠券列表 | — | — | — |
|
||||||
|
| `/api/coupons/{id}/receive` | — | 领取优惠券 | — | — |
|
||||||
|
| `/api/user/coupons` | 查看我的优惠券 | — | — | — |
|
||||||
|
| `/api/seckill/activities` | 获取抢购活动列表 | — | — | — |
|
||||||
|
| `/api/seckill/activities/{id}/products` | 获取活动商品 | — | — | — |
|
||||||
|
| `/api/seckill/buy` | — | 发起抢购下单 | — | — |
|
||||||
|
|
||||||
|
### 7.2 WebSocket 消息格式
|
||||||
|
|
||||||
|
WebSocket 连接地址:`ws://服务器地址/ws/chat?token={JWT_TOKEN}`
|
||||||
|
|
||||||
|
**客户端发送消息:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sessionId": 1,
|
||||||
|
"type": "text",
|
||||||
|
"content": "我的订单什么时候发货?"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**服务端推送消息:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 100,
|
||||||
|
"sessionId": 1,
|
||||||
|
"senderType": 1,
|
||||||
|
"senderName": "客服小美",
|
||||||
|
"type": "text",
|
||||||
|
"content": "您好,您的订单预计明天发货",
|
||||||
|
"createTime": "2026-06-01 14:30:00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 统一响应格式
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "操作成功",
|
||||||
|
"data": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.4 状态码定义
|
||||||
|
|
||||||
|
| 状态码 | 含义 |
|
||||||
|
|--------|------|
|
||||||
|
| 200 | 成功 |
|
||||||
|
| 400 | 请求参数错误 |
|
||||||
|
| 401 | 未登录或 Token 过期 |
|
||||||
|
| 403 | 无权限 |
|
||||||
|
| 404 | 资源不存在 |
|
||||||
|
| 500 | 服务器内部错误 |
|
||||||
|
| 1001 | 业务错误(自定义) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、毕设论文结构建议
|
||||||
|
|
||||||
|
| 章节 | 内容 |
|
||||||
|
|------|------|
|
||||||
|
| 第 1 章 绪论 | 研究背景与意义、国内外研究现状、论文主要研究内容 |
|
||||||
|
| 第 2 章 相关技术介绍 | SpringBoot、Vue3、MySQL、Redis、MyBatis-Plus 等核心技术介绍 |
|
||||||
|
| 第 3 章 系统分析 | 可行性分析(技术/经济/操作)、需求分析(功能需求/非功能需求)、用例图 |
|
||||||
|
| 第 4 章 系统设计 | 系统架构设计、功能模块设计、数据库设计(E-R 图、表结构)、接口设计 |
|
||||||
|
| 第 5 章 系统实现 | 核心功能实现(含关键代码)、界面展示截图 |
|
||||||
|
| 第 6 章 系统测试 | 测试环境、功能测试用例、测试结果 |
|
||||||
|
| 第 7 章 总结与展望 | 工作总结、不足之处、未来展望 |
|
||||||
|
| 参考文献 | |
|
||||||
|
| 致谢 | |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录
|
||||||
|
|
||||||
|
### A. 开发环境建议
|
||||||
|
|
||||||
|
| 工具 | 版本/说明 |
|
||||||
|
|------|-----------|
|
||||||
|
| JDK | 1.8 / 11 / 17 |
|
||||||
|
| IDEA | IntelliJ IDEA 2022+ |
|
||||||
|
| VS Code / WebStorm | 前端开发 |
|
||||||
|
| MySQL | 8.0 |
|
||||||
|
| Redis | 6.x / 7.x |
|
||||||
|
| Navicat / DBeaver | 数据库可视化工具 |
|
||||||
|
| Postman | 接口测试 |
|
||||||
|
|
||||||
|
### B. 功能优先级说明
|
||||||
|
|
||||||
|
- **高**:核心功能,必须实现
|
||||||
|
- **中**:重要功能,建议实现
|
||||||
|
- **低**:扩展功能,时间充裕时实现
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> 本文档随项目迭代持续更新。
|
||||||
Loading…
Reference in New Issue