农产品直销平台完整项目
This commit is contained in:
commit
51e2791a49
204
.gitignore
vendored
Normal file
204
.gitignore
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
# Java编译文件
|
||||
*.class
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# Maven
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
# Gradle
|
||||
.gradle
|
||||
/build/
|
||||
gradle-app.setting
|
||||
!gradle-wrapper.jar
|
||||
!gradle-wrapper.properties
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
# Spring Boot
|
||||
spring-boot-starter-parent/
|
||||
|
||||
# Node.js和Vue
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
dist/
|
||||
dist-ssr/
|
||||
*.local
|
||||
.npmrc
|
||||
|
||||
# Vue相关
|
||||
.vuepress/dist
|
||||
.nuxt
|
||||
.nitro
|
||||
|
||||
# Python虚拟环境
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
.venv/
|
||||
.env/
|
||||
.python-version
|
||||
pyenv.cfg
|
||||
|
||||
# Python编译文件
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
*.egg
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
.eggs/
|
||||
|
||||
# Python工具
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
.tox/
|
||||
.nox/
|
||||
htmlcov/
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IDE相关文件
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
*.sublime-project
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# 系统相关文件
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
.fseventsd
|
||||
.TemporaryItems
|
||||
.DocumentRevisions-V100
|
||||
|
||||
# 日志文件
|
||||
logs/
|
||||
*.log
|
||||
log/
|
||||
*.log.*
|
||||
*.out
|
||||
|
||||
# 缓存文件
|
||||
.cache/
|
||||
.temp/
|
||||
.tmp/
|
||||
*.cache
|
||||
.eslintcache
|
||||
.stylelintcache
|
||||
|
||||
# 环境配置文件(包含敏感信息)
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# 敏感配置文件(重要:包含密钥信息)
|
||||
**/application-*.yml
|
||||
!**/application.yml
|
||||
!**/application-prod.yml.template
|
||||
!**/application-dev.yml.template
|
||||
*.key
|
||||
*.pem
|
||||
*.p12
|
||||
*.keystore
|
||||
*.jks
|
||||
|
||||
# 数据库文件
|
||||
*.db
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
|
||||
# 测试覆盖率报告
|
||||
coverage/
|
||||
*.lcov
|
||||
.nyc_output
|
||||
|
||||
# 依赖分析文件
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# 运行时数据
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# 可选的npm缓存目录
|
||||
.npm
|
||||
|
||||
# 可选的eslint缓存
|
||||
.eslintcache
|
||||
|
||||
# 可选的stylelint缓存
|
||||
.stylelintcache
|
||||
|
||||
# TypeScript缓存
|
||||
*.tsbuildinfo
|
||||
|
||||
# 备份文件
|
||||
*.bak
|
||||
*.backup
|
||||
*.old
|
||||
|
||||
# 压缩文件
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.tar
|
||||
|
||||
# 项目特定文件
|
||||
export_code.sh
|
||||
temp_*
|
||||
*.txt.backup*
|
||||
*.yml.backup*
|
||||
*.yml.bak*
|
||||
SunnyFarm_源代码.txt
|
||||
|
||||
# Docker相关
|
||||
.dockerignore
|
||||
docker-compose.override.yml
|
||||
|
||||
# 其他
|
||||
.vite-inspect/
|
||||
.vite/
|
||||
351
README.md
Normal file
351
README.md
Normal file
@ -0,0 +1,351 @@
|
||||
# 🌱 农产品直销平台 (SunnyFarm)
|
||||
|
||||
一个基于 Spring Boot + Vue.js + MySQL 的现代化农产品电商平台,支持多角色管理(用户、商家、管理员),提供完整的电商功能包括商品管理、订单处理、支付集成、库存管理等。
|
||||
|
||||
## 📋 目录
|
||||
|
||||
- [功能特性](#功能特性)
|
||||
- [技术栈](#技术栈)
|
||||
- [环境要求](#环境要求)
|
||||
- [快速开始](#快速开始)
|
||||
- [配置说明](#配置说明)
|
||||
- [部署指南](#部署指南)
|
||||
- [API文档](#api文档)
|
||||
- [贡献指南](#贡献指南)
|
||||
|
||||
## ✨ 功能特性
|
||||
|
||||
### 🛒 核心电商功能
|
||||
- **商品管理**: 分类管理、商品发布、图片上传、库存管理
|
||||
- **购物车**: 添加商品、数量调整、批量操作
|
||||
- **订单系统**: 创建订单、支付集成、状态跟踪、物流管理
|
||||
- **用户系统**: 注册登录、个人中心、收货地址管理
|
||||
- **评价系统**: 商品评价、商家回复、评分统计
|
||||
|
||||
### 👥 多角色支持
|
||||
- **普通用户**: 浏览商品、下单购买、管理订单
|
||||
- **商家**: 商品管理、订单处理、销售统计、客服聊天
|
||||
- **平台管理员**: 用户管理、商家审核、系统配置、数据统计
|
||||
|
||||
### 🔧 技术亮点
|
||||
- **支付集成**: 支付宝沙箱支付,支持异步回调
|
||||
- **文件存储**: 腾讯云COS + CDN加速
|
||||
- **缓存优化**: Redis缓存用户会话和购物车数据
|
||||
- **邮件服务**: 注册验证码、订单通知
|
||||
- **实时聊天**: 用户与商家在线客服
|
||||
- **响应式设计**: 支持PC端和移动端访问
|
||||
|
||||
## 🛠 技术栈
|
||||
|
||||
### 后端技术
|
||||
- **框架**: Spring Boot 2.7.14
|
||||
- **安全**: Spring Security + JWT认证
|
||||
- **数据库**: MySQL 8.0 + MyBatis Plus
|
||||
- **缓存**: Redis
|
||||
- **文件存储**: 腾讯云COS
|
||||
- **支付**: 支付宝开放平台
|
||||
- **邮件**: Spring Boot Mail
|
||||
|
||||
### 前端技术
|
||||
- **框架**: Vue 3 + Vite
|
||||
- **UI组件**: Element Plus
|
||||
- **状态管理**: Pinia
|
||||
- **路由**: Vue Router 4
|
||||
- **图表**: ECharts
|
||||
- **HTTP客户端**: Axios
|
||||
|
||||
### 运维部署
|
||||
- **容器化**: Docker + Docker Compose
|
||||
- **云服务**: 支持各类云平台部署
|
||||
- **反向代理**: Nginx
|
||||
- **进程管理**: PM2(可选)
|
||||
|
||||
## 📋 环境要求
|
||||
|
||||
### 开发环境
|
||||
- **Java**: JDK 8 或以上
|
||||
- **Node.js**: 16.0 或以上
|
||||
- **Maven**: 3.6 或以上
|
||||
- **MySQL**: 8.0 或以上
|
||||
- **Redis**: 6.0 或以上
|
||||
|
||||
### 生产环境
|
||||
- **服务器**: Linux (推荐 Ubuntu 20.04+)
|
||||
- **内存**: 建议 2GB 以上
|
||||
- **存储**: 建议 20GB 以上
|
||||
- **网络**: 需要公网IP或域名(用于支付回调)
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 克隆项目
|
||||
|
||||
```bash
|
||||
git clone https://git.sq0715.com/qin/SunnyFarm.git
|
||||
cd SunnyFarm
|
||||
```
|
||||
|
||||
### 2. 配置文件设置
|
||||
|
||||
#### 后端配置
|
||||
|
||||
复制配置模板并填入你的配置信息:
|
||||
|
||||
```bash
|
||||
# 开发环境配置
|
||||
cp backend/src/main/resources/application-dev.yml.example backend/src/main/resources/application-dev.yml
|
||||
|
||||
# 生产环境配置
|
||||
cp backend/src/main/resources/application-prod.yml.example backend/src/main/resources/application-prod.yml
|
||||
```
|
||||
|
||||
### 3. 数据库初始化
|
||||
|
||||
```bash
|
||||
# 创建数据库
|
||||
mysql -u root -p -e "CREATE DATABASE sunnyfarm CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
|
||||
# 导入数据库结构和初始数据
|
||||
mysql -u your_username -p sunnyfarm < sql/sunnyfarm.sql
|
||||
mysql -u your_username -p sunnyfarm < sql/init_inventory.sql
|
||||
```
|
||||
|
||||
### 4. 启动后端服务
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
mvn clean install
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
服务启动后访问: http://localhost:8080/api/init/status
|
||||
|
||||
### 5. 启动前端服务
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
前端启动后访问: http://localhost:3000
|
||||
|
||||
## ⚙️ 配置说明
|
||||
|
||||
### 数据库配置
|
||||
```yaml
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://your-mysql-host:3306/sunnyfarm
|
||||
username: your-db-username
|
||||
password: your-db-password
|
||||
```
|
||||
|
||||
### Redis配置
|
||||
```yaml
|
||||
spring:
|
||||
redis:
|
||||
host: your-redis-host
|
||||
port: 6379
|
||||
password: your-redis-password
|
||||
```
|
||||
|
||||
### 邮件服务配置 (QQ邮箱示例)
|
||||
```yaml
|
||||
spring:
|
||||
mail:
|
||||
host: smtp.qq.com
|
||||
port: 465
|
||||
username: your-email@qq.com
|
||||
password: your-email-auth-code # QQ邮箱授权码
|
||||
```
|
||||
|
||||
### 腾讯云COS配置
|
||||
```yaml
|
||||
tencent:
|
||||
cos:
|
||||
secret-id: your-tencent-cos-secret-id
|
||||
secret-key: your-tencent-cos-secret-key
|
||||
region: ap-guangzhou
|
||||
bucket: your-bucket-name
|
||||
```
|
||||
|
||||
### 支付宝配置
|
||||
```yaml
|
||||
alipay:
|
||||
app-id: your-alipay-app-id
|
||||
app-private-key: your-alipay-app-private-key
|
||||
alipay-public-key: your-alipay-public-key
|
||||
notify-url: http://your-domain:8080/api/payment/alipay/notify
|
||||
return-url: http://your-domain:3000/orders?payment=success
|
||||
```
|
||||
|
||||
**重要**: 支付宝回调地址必须是公网可访问的地址,不能使用localhost
|
||||
|
||||
### JWT配置
|
||||
```yaml
|
||||
jwt:
|
||||
secret: your-jwt-secret-key
|
||||
expiration: 86400000 # 24小时,单位毫秒
|
||||
```
|
||||
|
||||
## 🐳 Docker部署
|
||||
|
||||
### 使用Docker Compose部署
|
||||
|
||||
```bash
|
||||
# 构建并启动服务
|
||||
docker-compose up -d
|
||||
|
||||
# 查看服务状态
|
||||
docker-compose ps
|
||||
|
||||
# 查看日志
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
### 手动Docker部署
|
||||
|
||||
```bash
|
||||
# 构建后端镜像
|
||||
cd backend
|
||||
docker build -t sunnyfarm-backend .
|
||||
|
||||
# 构建前端镜像
|
||||
cd ../frontend
|
||||
docker build -t sunnyfarm-frontend .
|
||||
|
||||
# 启动服务
|
||||
docker run -d -p 8080:8080 --name sunnyfarm-backend sunnyfarm-backend
|
||||
docker run -d -p 3000:3000 --name sunnyfarm-frontend sunnyfarm-frontend
|
||||
```
|
||||
|
||||
## 🌍 生产环境部署
|
||||
|
||||
### 1. 服务器准备
|
||||
|
||||
```bash
|
||||
# 安装基础软件
|
||||
sudo apt update && sudo apt install -y git docker.io docker-compose nginx
|
||||
|
||||
# 启动Docker服务
|
||||
sudo systemctl start docker
|
||||
sudo systemctl enable docker
|
||||
```
|
||||
|
||||
### 2. 克隆项目并配置
|
||||
|
||||
```bash
|
||||
git clone https://git.sq0715.com/qin/SunnyFarm.git
|
||||
cd SunnyFarm
|
||||
|
||||
# 配置生产环境文件
|
||||
cp backend/src/main/resources/application-prod.yml.example backend/src/main/resources/application-prod.yml
|
||||
# 编辑配置文件,填入生产环境配置信息
|
||||
vim backend/src/main/resources/application-prod.yml
|
||||
```
|
||||
|
||||
### 3. 部署启动
|
||||
|
||||
```bash
|
||||
# 构建并启动
|
||||
docker-compose up -d
|
||||
|
||||
# 检查服务状态
|
||||
docker-compose ps
|
||||
curl http://your-server-ip:8080/api/init/status
|
||||
```
|
||||
|
||||
## 📚 API文档
|
||||
|
||||
### 认证接口
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| POST | `/api/user/login` | 用户登录 |
|
||||
| POST | `/api/user/register` | 用户注册 |
|
||||
| POST | `/api/merchant/login` | 商家登录 |
|
||||
| POST | `/api/admin/login` | 管理员登录 |
|
||||
|
||||
### 商品接口
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/api/product/list` | 商品列表 |
|
||||
| GET | `/api/product/{id}` | 商品详情 |
|
||||
| POST | `/api/product/create` | 创建商品(商家) |
|
||||
| PUT | `/api/product/{id}` | 更新商品(商家) |
|
||||
|
||||
### 订单接口
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| POST | `/api/order/create` | 创建订单 |
|
||||
| GET | `/api/order/list` | 订单列表 |
|
||||
| GET | `/api/order/{id}` | 订单详情 |
|
||||
| POST | `/api/order/{id}/pay` | 订单支付 |
|
||||
|
||||
## 🎯 默认账号
|
||||
|
||||
### 管理员账号
|
||||
- 用户名: `admin`
|
||||
- 密码: `admin123`
|
||||
|
||||
### 测试商家账号
|
||||
- 用户名: `merchant`
|
||||
- 密码: `merchant123`
|
||||
|
||||
### 测试用户账号
|
||||
- 用户名: `user`
|
||||
- 密码: `user123`
|
||||
|
||||
## 📝 开发说明
|
||||
|
||||
### 项目结构
|
||||
|
||||
```
|
||||
SunnyFarm/
|
||||
├── backend/ # 后端Spring Boot项目
|
||||
│ ├── src/main/java/ # Java源码
|
||||
│ ├── src/main/resources/ # 配置文件
|
||||
│ └── pom.xml # Maven配置
|
||||
├── frontend/ # 前端Vue项目
|
||||
│ ├── src/ # Vue源码
|
||||
│ ├── public/ # 静态资源
|
||||
│ └── package.json # NPM配置
|
||||
├── sql/ # 数据库脚本
|
||||
├── docker-compose.yml # Docker编排
|
||||
└── README.md # 项目文档
|
||||
```
|
||||
|
||||
### 开发规范
|
||||
|
||||
- 代码格式化: 使用项目配置的格式化规则
|
||||
- 提交规范: 使用语义化提交信息
|
||||
- 分支管理: 使用Git Flow工作流
|
||||
- 代码审查: 重要功能需要代码审查
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
1. Fork本项目
|
||||
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
|
||||
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. 推送分支 (`git push origin feature/AmazingFeature`)
|
||||
5. 创建Pull Request
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情
|
||||
|
||||
## 📞 联系方式
|
||||
|
||||
- 项目地址: https://git.sq0715.com/qin/SunnyFarm
|
||||
- 问题反馈: 请在GitHub Issues中提交
|
||||
- 邮箱联系: 852326703@qq.com
|
||||
|
||||
## 🙏 致谢
|
||||
|
||||
感谢所有为本项目做出贡献的开发者们!
|
||||
|
||||
---
|
||||
|
||||
**注意**: 本项目仅供学习和研究使用,请勿用于商业用途。使用前请确保遵守相关法律法规。
|
||||
0
backend/README.md
Normal file
0
backend/README.md
Normal file
133
backend/pom.xml
Normal file
133
backend/pom.xml
Normal file
@ -0,0 +1,133 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.14</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>com.sunnyfarm</groupId>
|
||||
<artifactId>sunny-farm</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>sunny-farm</name>
|
||||
<description>Agricultural Product Direct Sales Platform</description>
|
||||
<properties>
|
||||
<java.version>8</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Boot Web Starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Mail Starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis Plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.5.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MySQL Driver -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.33</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Redis Starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON处理 -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache Commons Lang3 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 腾讯云COS -->
|
||||
<dependency>
|
||||
<groupId>com.qcloud</groupId>
|
||||
<artifactId>cos_api</artifactId>
|
||||
<version>5.6.155</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Validation -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- 支付宝SDK -->
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>4.38.10.ALL</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
206
backend/pom.xml.backup2
Normal file
206
backend/pom.xml.backup2
Normal file
@ -0,0 +1,206 @@
|
||||
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.sunnyfarm</groupId>
|
||||
<artifactId>sunny-farm</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>SunnyFarm</name>
|
||||
<description>农产品直销平台</description>
|
||||
|
||||
<properties>
|
||||
<!-- 核心修改:将Java版本统一为17 -->
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
|
||||
<spring.boot.version>2.7.14</spring.boot.version>
|
||||
<mybatis.plus.version>3.5.3</mybatis.plus.version>
|
||||
<mysql.version>8.0.33</mysql.version>
|
||||
<redis.version>2.7.14</redis.version>
|
||||
<jwt.version>0.11.5</jwt.version>
|
||||
<cos.version>5.6.89</cos.version>
|
||||
<alipay.version>4.35.79.ALL</alipay.version>
|
||||
<hutool.version>5.8.20</hutool.version>
|
||||
<fastjson.version>2.0.31</fastjson.version>
|
||||
<lombok.version>1.18.32</lombok.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<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>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot Starters -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</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.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Database -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis.plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>${mysql.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Tencent COS -->
|
||||
<dependency>
|
||||
<groupId>com.qcloud</groupId>
|
||||
<artifactId>cos_api</artifactId>
|
||||
<version>${cos.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Alipay -->
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>${alipay.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Utilities -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Configuration Processor -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
</exclude>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<!-- 建议修改:升级插件版本以更好地支持Java 17 -->
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<!-- 建议修改:引用属性,保持统一 -->
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>0.2.0</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
122
backend/pom.xml.backup3
Normal file
122
backend/pom.xml.backup3
Normal file
@ -0,0 +1,122 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.14</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>com.sunnyfarm</groupId>
|
||||
<artifactId>sunny-farm</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>sunny-farm</name>
|
||||
<description>Agricultural Product Direct Sales Platform</description>
|
||||
<properties>
|
||||
<java.version>8</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!-- Spring Boot Web Starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Mail Starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis Plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.5.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MySQL Driver -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.33</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Redis Starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON处理 -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache Commons Lang3 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 腾讯云COS -->
|
||||
<dependency>
|
||||
<groupId>com.qcloud</groupId>
|
||||
<artifactId>cos_api</artifactId>
|
||||
<version>5.6.155</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Validation -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@ -0,0 +1,12 @@
|
||||
package com.sunnyfarm;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||
|
||||
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
|
||||
public class SunnyFarmApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SunnyFarmApplication.class, args);
|
||||
}
|
||||
}
|
||||
22
backend/src/main/java/com/sunnyfarm/common/PageResult.java
Normal file
22
backend/src/main/java/com/sunnyfarm/common/PageResult.java
Normal file
@ -0,0 +1,22 @@
|
||||
package com.sunnyfarm.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PageResult<T> {
|
||||
private Long total;
|
||||
private Long pages;
|
||||
private Long current;
|
||||
private Long size;
|
||||
private List<T> records;
|
||||
|
||||
public static <T> PageResult<T> of(Long total, Long pages, Long current, Long size, List<T> records) {
|
||||
return new PageResult<>(total, pages, current, size, records);
|
||||
}
|
||||
}
|
||||
42
backend/src/main/java/com/sunnyfarm/common/Result.java
Normal file
42
backend/src/main/java/com/sunnyfarm/common/Result.java
Normal file
@ -0,0 +1,42 @@
|
||||
package com.sunnyfarm.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Result<T> {
|
||||
private Integer code;
|
||||
private String message;
|
||||
private T data;
|
||||
|
||||
public static <T> Result<T> success() {
|
||||
return new Result<>(200, "操作成功", null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> success(T data) {
|
||||
return new Result<>(200, "操作成功", data);
|
||||
}
|
||||
|
||||
public static <T> Result<T> success(String message, T data) {
|
||||
return new Result<>(200, message, data);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error() {
|
||||
return new Result<>(500, "操作失败", null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(String message) {
|
||||
return new Result<>(500, message, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(Integer code, String message) {
|
||||
return new Result<>(code, message, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(String message, T data) {
|
||||
return new Result<>(500, message, data);
|
||||
}
|
||||
}
|
||||
28
backend/src/main/java/com/sunnyfarm/config/AlipayConfig.java
Normal file
28
backend/src/main/java/com/sunnyfarm/config/AlipayConfig.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.sunnyfarm.config;
|
||||
|
||||
import com.alipay.api.AlipayClient;
|
||||
import com.alipay.api.DefaultAlipayClient;
|
||||
import com.sunnyfarm.config.properties.AlipayProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class AlipayConfig {
|
||||
|
||||
@Autowired
|
||||
private AlipayProperties alipayProperties;
|
||||
|
||||
@Bean
|
||||
public AlipayClient alipayClient() {
|
||||
return new DefaultAlipayClient(
|
||||
alipayProperties.getServerUrl(),
|
||||
alipayProperties.getAppId(),
|
||||
alipayProperties.getAppPrivateKey(),
|
||||
alipayProperties.getFormat(),
|
||||
alipayProperties.getCharset(),
|
||||
alipayProperties.getAlipayPublicKey(),
|
||||
alipayProperties.getSignType()
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.sunnyfarm.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
public class AsyncConfig {
|
||||
}
|
||||
33
backend/src/main/java/com/sunnyfarm/config/CosConfig.java
Normal file
33
backend/src/main/java/com/sunnyfarm/config/CosConfig.java
Normal file
@ -0,0 +1,33 @@
|
||||
package com.sunnyfarm.config;
|
||||
|
||||
import com.qcloud.cos.COSClient;
|
||||
import com.qcloud.cos.ClientConfig;
|
||||
import com.qcloud.cos.auth.BasicCOSCredentials;
|
||||
import com.qcloud.cos.auth.COSCredentials;
|
||||
import com.qcloud.cos.region.Region;
|
||||
import com.sunnyfarm.config.properties.CosProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class CosConfig {
|
||||
|
||||
@Autowired
|
||||
private CosProperties cosProperties;
|
||||
|
||||
@Bean
|
||||
public COSClient cosClient() {
|
||||
// 初始化用户身份信息(secretId, secretKey)
|
||||
COSCredentials cred = new BasicCOSCredentials(
|
||||
cosProperties.getSecretId(),
|
||||
cosProperties.getSecretKey()
|
||||
);
|
||||
|
||||
// 设置bucket的区域
|
||||
ClientConfig clientConfig = new ClientConfig(new Region(cosProperties.getRegion()));
|
||||
|
||||
// 生成cos客户端
|
||||
return new COSClient(cred, clientConfig);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.sunnyfarm.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@MapperScan("com.sunnyfarm.mapper")
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 分页插件
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.sunnyfarm.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
@Configuration
|
||||
public class PasswordConfig {
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
93
backend/src/main/java/com/sunnyfarm/config/WebConfig.java
Normal file
93
backend/src/main/java/com/sunnyfarm/config/WebConfig.java
Normal file
@ -0,0 +1,93 @@
|
||||
package com.sunnyfarm.config;
|
||||
|
||||
import com.sunnyfarm.interceptor.JwtAuthInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
private JwtAuthInterceptor jwtAuthInterceptor;
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(true)
|
||||
.maxAge(3600);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
|
||||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
||||
configuration.setAllowedHeaders(Arrays.asList("*"));
|
||||
configuration.setAllowCredentials(true);
|
||||
configuration.setMaxAge(3600L);
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(jwtAuthInterceptor)
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns(
|
||||
// 用户认证相关
|
||||
"/api/user/login",
|
||||
"/api/user/register",
|
||||
"/api/user/send-code",
|
||||
"/api/user/send-email-code", // 新增:邮箱验证码接口
|
||||
"/api/user/verify-code",
|
||||
// 商家认证相关
|
||||
"/api/merchant/register",
|
||||
"/api/merchant/login",
|
||||
// 管理员认证相关
|
||||
"/api/admin/login",
|
||||
// 公开接口 - 商品分类
|
||||
"/api/category/**",
|
||||
// 公开接口 - 商品相关(用户端公共访问)
|
||||
"/api/product/list",
|
||||
"/api/product/*/detail",
|
||||
"/api/product/hot",
|
||||
"/api/product/search",
|
||||
// 初始化和测试接口
|
||||
"/api/init/**",
|
||||
// 公共接口 - 公告相关
|
||||
"/api/announcements/published",
|
||||
"/api/announcements/latest",
|
||||
"/api/announcements/*/detail",
|
||||
"/api/test/**",
|
||||
// 支付回调(无需认证)
|
||||
"/api/payment/alipay/notify",
|
||||
"/api/payment/alipay/return",
|
||||
// 系统相关
|
||||
"/error",
|
||||
"/favicon.ico",
|
||||
// 静态资源
|
||||
"/**/*.css",
|
||||
"/**/*.js",
|
||||
"/**/*.png",
|
||||
"/**/*.jpg",
|
||||
"/**/*.jpeg",
|
||||
"/**/*.gif",
|
||||
"/**/*.ico",
|
||||
"/**/*.html"
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package com.sunnyfarm.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
public class AlipayProperties {
|
||||
|
||||
@Value("${alipay.app-id}")
|
||||
private String appId;
|
||||
|
||||
@Value("${alipay.app-private-key}")
|
||||
private String appPrivateKey;
|
||||
|
||||
@Value("${alipay.alipay-public-key}")
|
||||
private String alipayPublicKey;
|
||||
|
||||
@Value("${alipay.server-url}")
|
||||
private String serverUrl;
|
||||
|
||||
@Value("${alipay.format}")
|
||||
private String format;
|
||||
|
||||
@Value("${alipay.charset}")
|
||||
private String charset;
|
||||
|
||||
@Value("${alipay.sign-type}")
|
||||
private String signType;
|
||||
|
||||
@Value("${alipay.notify-url}")
|
||||
private String notifyUrl;
|
||||
|
||||
@Value("${alipay.return-url}")
|
||||
private String returnUrl;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.sunnyfarm.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "tencent.cos")
|
||||
public class CosProperties {
|
||||
private String region;
|
||||
private String bucket;
|
||||
private String secretId;
|
||||
private String secretKey;
|
||||
private String baseUrl;
|
||||
private String cdnUrl;
|
||||
private Boolean cdnEnabled = false;
|
||||
|
||||
/**
|
||||
* 获取实际使用的URL(CDN优先)
|
||||
*/
|
||||
public String getActualUrl() {
|
||||
return (cdnEnabled != null && cdnEnabled && cdnUrl != null) ? cdnUrl : baseUrl;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.sunnyfarm.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "system.email")
|
||||
public class EmailProperties {
|
||||
private boolean enabled = true;
|
||||
private String from;
|
||||
private String fromName;
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.entity.Address;
|
||||
import com.sunnyfarm.service.AddressService;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/address")
|
||||
@CrossOrigin
|
||||
public class AddressController {
|
||||
|
||||
@Autowired
|
||||
private AddressService addressService;
|
||||
|
||||
/**
|
||||
* 获取用户地址列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<List<Address>> getAddressList() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
List<Address> addresses = addressService.getUserAddresses(userId);
|
||||
return Result.success(addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户默认地址
|
||||
*/
|
||||
@GetMapping("/default")
|
||||
public Result<Address> getDefaultAddress() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Address address = addressService.getUserDefaultAddress(userId);
|
||||
return Result.success(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取地址详情
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<Address> getAddressDetail(@PathVariable Long id) {
|
||||
Address address = addressService.getById(id);
|
||||
if (address != null) {
|
||||
return Result.success(address);
|
||||
} else {
|
||||
return Result.error("地址不存在");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建地址
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
public Result<Address> createAddress(@RequestBody Address address) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
address.setUserId(userId);
|
||||
|
||||
Address createdAddress = addressService.createAddress(address);
|
||||
return Result.success("地址创建成功", createdAddress);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新地址
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public Result<Address> updateAddress(@PathVariable Long id, @RequestBody Address address) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
address.setUserId(userId);
|
||||
|
||||
Address updatedAddress = addressService.updateAddress(id, address);
|
||||
return Result.success("地址更新成功", updatedAddress);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除地址
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<String> deleteAddress(@PathVariable Long id) {
|
||||
try {
|
||||
boolean success = addressService.deleteAddress(id);
|
||||
if (success) {
|
||||
return Result.success("地址删除成功");
|
||||
} else {
|
||||
return Result.error("地址删除失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认地址
|
||||
*/
|
||||
@PutMapping("/{id}/default")
|
||||
public Result<String> setDefaultAddress(@PathVariable Long id) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
boolean success = addressService.setDefaultAddress(userId, id);
|
||||
if (success) {
|
||||
return Result.success("默认地址设置成功");
|
||||
} else {
|
||||
return Result.error("默认地址设置失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户地址数量
|
||||
*/
|
||||
@GetMapping("/count")
|
||||
public Result<Long> getAddressCount() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Long count = addressService.getUserAddressCount(userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,631 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.common.PageResult;
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.dto.AdminLoginRequest;
|
||||
import com.sunnyfarm.dto.AdminUpdateRequest;
|
||||
import com.sunnyfarm.dto.AdminPasswordRequest;
|
||||
import com.sunnyfarm.entity.*;
|
||||
import com.sunnyfarm.service.*;
|
||||
import com.sunnyfarm.util.JwtUtil;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/admin")
|
||||
@CrossOrigin
|
||||
public class AdminController {
|
||||
|
||||
@Autowired
|
||||
private AdminService adminService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private MerchantService merchantService;
|
||||
|
||||
@Autowired
|
||||
private ProductService productService;
|
||||
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
|
||||
@Autowired
|
||||
private StatisticsService statisticsService;
|
||||
|
||||
@Autowired
|
||||
private SystemLogService systemLogService;
|
||||
|
||||
@Autowired
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Autowired
|
||||
private AnnouncementService announcementService;
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
/**
|
||||
* 管理员登录
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public Result<Map<String, Object>> login(@Valid @RequestBody AdminLoginRequest request, HttpServletRequest httpRequest) {
|
||||
try {
|
||||
Admin admin = adminService.login(request);
|
||||
if (admin != null) {
|
||||
String token = jwtUtil.generateAdminToken(admin.getId(), admin.getUsername());
|
||||
|
||||
// 记录登录日志
|
||||
systemLogService.recordSuccessLog(
|
||||
SystemLog.ADMIN_LOGIN,
|
||||
"管理员登录成功",
|
||||
admin.getId(),
|
||||
admin.getUsername(),
|
||||
SystemLog.OPERATOR_ADMIN
|
||||
);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("token", token);
|
||||
result.put("admin", admin);
|
||||
|
||||
return Result.success("登录成功", result);
|
||||
} else {
|
||||
return Result.error("登录失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 记录登录失败日志
|
||||
systemLogService.recordFailLog(
|
||||
SystemLog.ADMIN_LOGIN,
|
||||
"管理员登录失败",
|
||||
null,
|
||||
request.getUsername(),
|
||||
SystemLog.OPERATOR_ADMIN,
|
||||
e.getMessage()
|
||||
);
|
||||
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取管理员信息
|
||||
*/
|
||||
@GetMapping("/profile")
|
||||
public Result<Admin> getProfile(@RequestParam Long adminId) {
|
||||
Admin admin = adminService.getAdminWithRole(adminId);
|
||||
return Result.success(admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员注销
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public Result<String> logout() {
|
||||
return Result.success("注销成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仪表盘统计数据
|
||||
*/
|
||||
@GetMapping("/dashboard/stats")
|
||||
public Result<Map<String, Object>> getDashboardStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
|
||||
// 获取当天的开始和结束时间
|
||||
LocalDateTime todayStart = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
|
||||
LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
|
||||
|
||||
// 用户统计
|
||||
long totalUsers = userService.count();
|
||||
long newUsersToday = userService.count(
|
||||
new QueryWrapper<User>()
|
||||
.between("created_at", todayStart, todayEnd)
|
||||
);
|
||||
|
||||
// 商家统计
|
||||
long totalMerchants = merchantService.count();
|
||||
long pendingMerchants = merchantService.count(
|
||||
new QueryWrapper<Merchant>().eq("status", 0)
|
||||
);
|
||||
|
||||
// 订单统计
|
||||
long totalOrders = orderService.count();
|
||||
long newOrdersToday = orderService.count(
|
||||
new QueryWrapper<Order>()
|
||||
.between("created_at", todayStart, todayEnd)
|
||||
);
|
||||
|
||||
// 商品统计
|
||||
long totalProducts = productService.count();
|
||||
long pendingProducts = productService.count(
|
||||
new QueryWrapper<Product>().eq("status", 0)
|
||||
);
|
||||
|
||||
// 收入统计 - 获取真实的订单金额
|
||||
BigDecimal totalRevenue = orderService.getTotalRevenue();
|
||||
BigDecimal todayRevenue = orderService.getTodayRevenue();
|
||||
|
||||
// 投诉统计(暂时设为0,等后续实现投诉功能)
|
||||
long pendingComplaints = 0;
|
||||
|
||||
stats.put("totalUsers", totalUsers);
|
||||
stats.put("newUsersToday", newUsersToday);
|
||||
stats.put("totalMerchants", totalMerchants);
|
||||
stats.put("pendingMerchants", pendingMerchants);
|
||||
stats.put("totalOrders", totalOrders);
|
||||
stats.put("newOrdersToday", newOrdersToday);
|
||||
stats.put("totalProducts", totalProducts);
|
||||
stats.put("pendingProducts", pendingProducts);
|
||||
stats.put("totalRevenue", totalRevenue != null ? totalRevenue : BigDecimal.ZERO);
|
||||
stats.put("todayRevenue", todayRevenue != null ? todayRevenue : BigDecimal.ZERO);
|
||||
stats.put("pendingComplaints", pendingComplaints);
|
||||
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
*/
|
||||
@GetMapping("/users")
|
||||
public Result<PageResult<User>> getUsers(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
|
||||
Page<User> pageParam = new Page<>(page, size);
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (keyword != null && !keyword.trim().isEmpty()) {
|
||||
queryWrapper.like("username", keyword)
|
||||
.or()
|
||||
.like("nickname", keyword)
|
||||
.or()
|
||||
.like("phone", keyword);
|
||||
}
|
||||
|
||||
if (status != null) {
|
||||
queryWrapper.eq("status", status);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<User> result = userService.page(pageParam, queryWrapper);
|
||||
|
||||
PageResult<User> pageResult = new PageResult<>();
|
||||
pageResult.setRecords(result.getRecords());
|
||||
pageResult.setTotal(result.getTotal());
|
||||
pageResult.setCurrent(result.getCurrent());
|
||||
pageResult.setSize(result.getSize());
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户状态
|
||||
*/
|
||||
@PutMapping("/users/{id}/status")
|
||||
public Result<String> updateUserStatus(@PathVariable Long id, @RequestParam Integer status) {
|
||||
User user = new User();
|
||||
user.setId(id);
|
||||
user.setStatus(status);
|
||||
|
||||
boolean success = userService.updateById(user);
|
||||
|
||||
if (success) {
|
||||
User targetUser = userService.getById(id);
|
||||
String statusText = status == 1 ? "启用" : "禁用";
|
||||
|
||||
systemLogService.recordSuccessLog(
|
||||
"user_status_update",
|
||||
statusText + "用户: " + (targetUser != null ? targetUser.getUsername() : "ID:" + id),
|
||||
UserContext.getCurrentUserId(),
|
||||
UserContext.getCurrentUsername(),
|
||||
SystemLog.OPERATOR_ADMIN
|
||||
);
|
||||
|
||||
return Result.success("用户状态更新成功");
|
||||
} else {
|
||||
return Result.error("用户状态更新失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商家列表
|
||||
*/
|
||||
@GetMapping("/merchants")
|
||||
public Result<PageResult<Merchant>> getMerchants(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
|
||||
Page<Merchant> pageParam = new Page<>(page, size);
|
||||
QueryWrapper<Merchant> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (keyword != null && !keyword.trim().isEmpty()) {
|
||||
queryWrapper.like("shop_name", keyword)
|
||||
.or()
|
||||
.like("legal_person", keyword)
|
||||
.or()
|
||||
.like("contact_phone", keyword);
|
||||
}
|
||||
|
||||
if (status != null) {
|
||||
queryWrapper.eq("status", status);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<Merchant> result = merchantService.page(pageParam, queryWrapper);
|
||||
|
||||
PageResult<Merchant> pageResult = new PageResult<>();
|
||||
pageResult.setRecords(result.getRecords());
|
||||
pageResult.setTotal(result.getTotal());
|
||||
pageResult.setCurrent(result.getCurrent());
|
||||
pageResult.setSize(result.getSize());
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核商家
|
||||
*/
|
||||
@PutMapping("/merchants/{id}/audit")
|
||||
public Result<String> auditMerchant(@PathVariable Long id, @RequestParam Integer status) {
|
||||
Merchant merchant = new Merchant();
|
||||
merchant.setId(id);
|
||||
merchant.setStatus(status);
|
||||
merchant.setVerifyTime(LocalDateTime.now());
|
||||
|
||||
boolean success = merchantService.updateById(merchant);
|
||||
|
||||
if (success) {
|
||||
Merchant targetMerchant = merchantService.getById(id);
|
||||
String statusText = status == 1 ? "通过" : "拒绝";
|
||||
|
||||
systemLogService.recordSuccessLog(
|
||||
SystemLog.MERCHANT_AUDIT,
|
||||
"商家审核" + statusText + ": " + (targetMerchant != null ? targetMerchant.getShopName() : "ID:" + id),
|
||||
UserContext.getCurrentUserId(),
|
||||
UserContext.getCurrentUsername(),
|
||||
SystemLog.OPERATOR_ADMIN
|
||||
);
|
||||
|
||||
String message = status == 1 ? "商家审核通过" : "商家审核拒绝";
|
||||
return Result.success(message);
|
||||
} else {
|
||||
return Result.error("商家审核失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品列表
|
||||
*/
|
||||
@GetMapping("/products")
|
||||
public Result<PageResult<Product>> getProducts(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
|
||||
Page<Product> pageParam = new Page<>(page, size);
|
||||
QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (keyword != null && !keyword.trim().isEmpty()) {
|
||||
queryWrapper.like("name", keyword)
|
||||
.or()
|
||||
.like("origin", keyword);
|
||||
}
|
||||
|
||||
if (status != null) {
|
||||
queryWrapper.eq("status", status);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<Product> result = productService.page(pageParam, queryWrapper);
|
||||
|
||||
PageResult<Product> pageResult = new PageResult<>();
|
||||
pageResult.setRecords(result.getRecords());
|
||||
pageResult.setTotal(result.getTotal());
|
||||
pageResult.setCurrent(result.getCurrent());
|
||||
pageResult.setSize(result.getSize());
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核商品
|
||||
*/
|
||||
@PutMapping("/products/{id}/audit")
|
||||
public Result<String> auditProduct(@PathVariable Long id, @RequestParam Integer status) {
|
||||
Product product = new Product();
|
||||
product.setId(id);
|
||||
product.setStatus(status);
|
||||
|
||||
boolean success = productService.updateById(product);
|
||||
|
||||
if (success) {
|
||||
Product targetProduct = productService.getById(id);
|
||||
String statusText = status == 1 ? "通过" : "拒绝";
|
||||
|
||||
systemLogService.recordSuccessLog(
|
||||
SystemLog.PRODUCT_AUDIT,
|
||||
"商品审核" + statusText + ": " + (targetProduct != null ? targetProduct.getName() : "ID:" + id),
|
||||
UserContext.getCurrentUserId(),
|
||||
UserContext.getCurrentUsername(),
|
||||
SystemLog.OPERATOR_ADMIN
|
||||
);
|
||||
|
||||
String message = status == 1 ? "商品审核通过" : "商品审核拒绝";
|
||||
return Result.success(message);
|
||||
} else {
|
||||
return Result.error("商品审核失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单列表
|
||||
*/
|
||||
@GetMapping("/orders")
|
||||
public Result<PageResult<Order>> getOrders(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
|
||||
Page<Order> pageParam = new Page<>(page, size);
|
||||
QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (keyword != null && !keyword.trim().isEmpty()) {
|
||||
queryWrapper.like("order_no", keyword)
|
||||
.or()
|
||||
.like("consignee", keyword)
|
||||
.or()
|
||||
.like("phone", keyword);
|
||||
}
|
||||
|
||||
if (status != null) {
|
||||
queryWrapper.eq("status", status);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<Order> result = orderService.page(pageParam, queryWrapper);
|
||||
|
||||
PageResult<Order> pageResult = new PageResult<>();
|
||||
pageResult.setRecords(result.getRecords());
|
||||
pageResult.setTotal(result.getTotal());
|
||||
pageResult.setCurrent(result.getCurrent());
|
||||
pageResult.setSize(result.getSize());
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新动态
|
||||
*/
|
||||
@GetMapping("/activities")
|
||||
public Result<List<Map<String, Object>>> getRecentActivities() {
|
||||
List<Map<String, Object>> activities = systemLogService.getRecentActivities(10);
|
||||
return Result.success(activities);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统配置
|
||||
*/
|
||||
@GetMapping("/config")
|
||||
public Result<Map<String, Object>> getSystemConfig() {
|
||||
Map<String, Object> config = systemConfigService.getSystemConfig();
|
||||
return Result.success(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新系统配置
|
||||
*/
|
||||
@PutMapping("/config")
|
||||
public Result<String> updateSystemConfig(@RequestBody Map<String, Object> config) {
|
||||
boolean success = systemConfigService.updateSystemConfig(
|
||||
config,
|
||||
UserContext.getCurrentUserId(),
|
||||
UserContext.getCurrentUsername()
|
||||
);
|
||||
|
||||
if (success) {
|
||||
return Result.success("系统配置更新成功");
|
||||
} else {
|
||||
return Result.error("系统配置更新失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公告列表
|
||||
*/
|
||||
@GetMapping("/announcements")
|
||||
public Result<PageResult<Announcement>> getAnnouncements(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
|
||||
Page<Announcement> pageParam = new Page<>(page, size);
|
||||
QueryWrapper<Announcement> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (keyword != null && !keyword.trim().isEmpty()) {
|
||||
queryWrapper.like("title", keyword)
|
||||
.or()
|
||||
.like("content", keyword);
|
||||
}
|
||||
|
||||
if (status != null) {
|
||||
queryWrapper.eq("status", status);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<Announcement> result = announcementService.page(pageParam, queryWrapper);
|
||||
|
||||
PageResult<Announcement> pageResult = new PageResult<>();
|
||||
pageResult.setRecords(result.getRecords());
|
||||
pageResult.setTotal(result.getTotal());
|
||||
pageResult.setCurrent(result.getCurrent());
|
||||
pageResult.setSize(result.getSize());
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建公告
|
||||
*/
|
||||
@PostMapping("/announcements")
|
||||
public Result<String> createAnnouncement(@RequestBody Announcement announcement) {
|
||||
boolean success = announcementService.createAnnouncement(
|
||||
announcement,
|
||||
UserContext.getCurrentUserId(),
|
||||
UserContext.getCurrentUsername()
|
||||
);
|
||||
|
||||
if (success) {
|
||||
return Result.success("公告创建成功");
|
||||
} else {
|
||||
return Result.error("公告创建失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新公告
|
||||
*/
|
||||
@PutMapping("/announcements/{id}")
|
||||
public Result<String> updateAnnouncement(@PathVariable Long id, @RequestBody Announcement announcement) {
|
||||
announcement.setId(id);
|
||||
boolean success = announcementService.updateAnnouncement(
|
||||
announcement,
|
||||
UserContext.getCurrentUserId(),
|
||||
UserContext.getCurrentUsername()
|
||||
);
|
||||
|
||||
if (success) {
|
||||
return Result.success("公告更新成功");
|
||||
} else {
|
||||
return Result.error("公告更新失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除公告
|
||||
*/
|
||||
@DeleteMapping("/announcements/{id}")
|
||||
public Result<String> deleteAnnouncement(@PathVariable Long id) {
|
||||
boolean success = announcementService.deleteAnnouncement(
|
||||
id,
|
||||
UserContext.getCurrentUserId(),
|
||||
UserContext.getCurrentUsername()
|
||||
);
|
||||
|
||||
if (success) {
|
||||
return Result.success("公告删除成功");
|
||||
} else {
|
||||
return Result.error("公告删除失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统日志
|
||||
*/
|
||||
@GetMapping("/logs")
|
||||
public Result<PageResult<SystemLog>> getSystemLogs(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) String operationType) {
|
||||
|
||||
Page<SystemLog> pageParam = new Page<>(page, size);
|
||||
QueryWrapper<SystemLog> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (keyword != null && !keyword.trim().isEmpty()) {
|
||||
queryWrapper.like("operation_desc", keyword)
|
||||
.or()
|
||||
.like("operator_name", keyword);
|
||||
}
|
||||
|
||||
if (operationType != null && !operationType.trim().isEmpty()) {
|
||||
queryWrapper.eq("operation_type", operationType);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<SystemLog> result = systemLogService.page(pageParam, queryWrapper);
|
||||
|
||||
PageResult<SystemLog> pageResult = new PageResult<>();
|
||||
pageResult.setRecords(result.getRecords());
|
||||
pageResult.setTotal(result.getTotal());
|
||||
pageResult.setCurrent(result.getCurrent());
|
||||
pageResult.setSize(result.getSize());
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新管理员个人信息
|
||||
*/
|
||||
@PutMapping("/profile/{id}")
|
||||
public Result<String> updateProfile(@PathVariable Long id, @Valid @RequestBody AdminUpdateRequest request) {
|
||||
try {
|
||||
boolean success = adminService.updateProfile(id, request);
|
||||
if (success) {
|
||||
systemLogService.recordSuccessLog(
|
||||
"admin_profile_update",
|
||||
"管理员更新个人信息",
|
||||
id,
|
||||
UserContext.getCurrentUsername(),
|
||||
SystemLog.OPERATOR_ADMIN
|
||||
);
|
||||
return Result.success("个人信息更新成功");
|
||||
} else {
|
||||
return Result.error("个人信息更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改管理员密码
|
||||
*/
|
||||
@PutMapping("/profile/{id}/password")
|
||||
public Result<String> changePassword(@PathVariable Long id, @Valid @RequestBody AdminPasswordRequest request) {
|
||||
try {
|
||||
boolean success = adminService.changePassword(id, request);
|
||||
if (success) {
|
||||
systemLogService.recordSuccessLog(
|
||||
"admin_password_change",
|
||||
"管理员修改密码",
|
||||
id,
|
||||
UserContext.getCurrentUsername(),
|
||||
SystemLog.OPERATOR_ADMIN
|
||||
);
|
||||
return Result.success("密码修改成功");
|
||||
} else {
|
||||
return Result.error("密码修改失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.common.PageResult;
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.entity.Announcement;
|
||||
import com.sunnyfarm.service.AnnouncementService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 公告控制器 - 公共接口
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/announcements")
|
||||
@CrossOrigin(originPatterns = "*")
|
||||
public class AnnouncementController {
|
||||
|
||||
@Autowired
|
||||
private AnnouncementService announcementService;
|
||||
|
||||
/**
|
||||
* 获取已发布的公告列表 - 公共接口
|
||||
*/
|
||||
@GetMapping("/published")
|
||||
public Result<PageResult<Announcement>> getPublishedAnnouncements(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) Integer type) {
|
||||
|
||||
Page<Announcement> pageParam = new Page<>(page, size);
|
||||
QueryWrapper<Announcement> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
// 只查询已发布的公告
|
||||
queryWrapper.eq("status", 1);
|
||||
|
||||
// 按类型筛选
|
||||
if (type != null) {
|
||||
queryWrapper.eq("type", type);
|
||||
}
|
||||
|
||||
// 确保publish_time不为NULL,然后按发布时间倒序
|
||||
queryWrapper.isNotNull("publish_time")
|
||||
.orderByDesc("publish_time");
|
||||
|
||||
Page<Announcement> result = announcementService.page(pageParam, queryWrapper);
|
||||
|
||||
PageResult<Announcement> pageResult = new PageResult<>();
|
||||
pageResult.setRecords(result.getRecords());
|
||||
pageResult.setTotal(result.getTotal());
|
||||
pageResult.setCurrent(result.getCurrent());
|
||||
pageResult.setSize(result.getSize());
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新公告 - 用于首页展示
|
||||
*/
|
||||
@GetMapping("/latest")
|
||||
public Result<List<Announcement>> getLatestAnnouncements(
|
||||
@RequestParam(defaultValue = "5") int limit) {
|
||||
|
||||
QueryWrapper<Announcement> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
// 只查询已发布的公告
|
||||
queryWrapper.eq("status", 1);
|
||||
|
||||
// 确保publish_time不为NULL,然后按发布时间倒序,限制数量
|
||||
queryWrapper.isNotNull("publish_time")
|
||||
.orderByDesc("publish_time")
|
||||
.last("LIMIT " + limit);
|
||||
|
||||
List<Announcement> announcements = announcementService.list(queryWrapper);
|
||||
|
||||
return Result.success(announcements);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公告详情
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<Announcement> getAnnouncementDetail(@PathVariable Long id) {
|
||||
QueryWrapper<Announcement> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("id", id)
|
||||
.eq("status", 1); // 只能查看已发布的公告
|
||||
|
||||
Announcement announcement = announcementService.getOne(queryWrapper);
|
||||
|
||||
if (announcement == null) {
|
||||
return Result.error("公告不存在或未发布");
|
||||
}
|
||||
|
||||
return Result.success(announcement);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,188 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.dto.CartItemDTO;
|
||||
import com.sunnyfarm.entity.Cart;
|
||||
import com.sunnyfarm.service.CartService;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/cart")
|
||||
@CrossOrigin
|
||||
public class CartController {
|
||||
|
||||
@Autowired
|
||||
private CartService cartService;
|
||||
|
||||
/**
|
||||
* 获取购物车列表
|
||||
*/
|
||||
@GetMapping
|
||||
public Result<List<Cart>> getCartList() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
List<Cart> cartList = cartService.getUserCart(userId);
|
||||
return Result.success(cartList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取购物车详细信息列表
|
||||
*/
|
||||
@GetMapping("/items")
|
||||
public Result<List<CartItemDTO>> getCartItems() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
List<CartItemDTO> cartItems = cartService.getUserCartItems(userId);
|
||||
return Result.success(cartItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加到购物车
|
||||
*/
|
||||
@PostMapping("/add")
|
||||
public Result<String> addToCart(@RequestBody Map<String, Object> cartData) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Long productId = Long.valueOf(cartData.get("productId").toString());
|
||||
Integer quantity = Integer.valueOf(cartData.get("quantity").toString());
|
||||
|
||||
boolean success = cartService.addToCart(userId, productId, quantity);
|
||||
if (success) {
|
||||
return Result.success("添加到购物车成功");
|
||||
} else {
|
||||
return Result.error("添加到购物车失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新购物车数量
|
||||
*/
|
||||
@PutMapping("/{productId}/quantity")
|
||||
public Result<String> updateQuantity(@PathVariable Long productId, @RequestBody Map<String, Integer> quantityData) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Integer quantity = quantityData.get("quantity");
|
||||
|
||||
boolean success = cartService.updateCartQuantity(userId, productId, quantity);
|
||||
if (success) {
|
||||
return Result.success("购物车更新成功");
|
||||
} else {
|
||||
return Result.error("购物车更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从购物车删除商品
|
||||
*/
|
||||
@DeleteMapping("/{productId}")
|
||||
public Result<String> removeFromCart(@PathVariable Long productId) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
boolean success = cartService.removeFromCart(userId, productId);
|
||||
if (success) {
|
||||
return Result.success("商品已从购物车删除");
|
||||
} else {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空购物车
|
||||
*/
|
||||
@DeleteMapping("/clear")
|
||||
public Result<String> clearCart() {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
boolean success = cartService.clearCart(userId);
|
||||
if (success) {
|
||||
return Result.success("购物车已清空");
|
||||
} else {
|
||||
return Result.error("清空失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取购物车商品数量
|
||||
*/
|
||||
@GetMapping("/count")
|
||||
public Result<Long> getCartCount() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Long count = cartService.getCartCount(userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除购物车商品
|
||||
*/
|
||||
@DeleteMapping("/batch")
|
||||
public Result<String> batchRemoveFromCart(@RequestBody Map<String, List<Long>> data) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
List<Long> productIds = data.get("productIds");
|
||||
|
||||
boolean success = cartService.batchRemoveFromCart(userId, productIds);
|
||||
if (success) {
|
||||
return Result.success("批量删除成功");
|
||||
} else {
|
||||
return Result.error("批量删除失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择/取消选择购物车商品
|
||||
*/
|
||||
@PutMapping("/{productId}/select")
|
||||
public Result<String> toggleSelect(@PathVariable Long productId, @RequestBody Map<String, Boolean> selectData) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Boolean selected = selectData.get("selected");
|
||||
|
||||
boolean success = cartService.toggleCartSelect(userId, productId, selected);
|
||||
if (success) {
|
||||
return Result.success("选择状态更新成功");
|
||||
} else {
|
||||
return Result.error("选择状态更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 全选/取消全选
|
||||
*/
|
||||
@PutMapping("/select-all")
|
||||
public Result<String> toggleSelectAll(@RequestBody Map<String, Boolean> selectData) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Boolean selected = selectData.get("selected");
|
||||
|
||||
boolean success = cartService.toggleAllCartSelection(userId, selected);
|
||||
if (success) {
|
||||
return Result.success("全选状态更新成功");
|
||||
} else {
|
||||
return Result.error("全选状态更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.entity.Category;
|
||||
import com.sunnyfarm.service.CategoryService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/category")
|
||||
@CrossOrigin
|
||||
public class CategoryController {
|
||||
|
||||
@Autowired
|
||||
private CategoryService categoryService;
|
||||
|
||||
/**
|
||||
* 获取所有分类列表(用户端公共接口)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<List<Category>> getCategoryList() {
|
||||
try {
|
||||
List<Category> categories = categoryService.getAllCategories();
|
||||
return Result.success(categories);
|
||||
} catch (Exception e) {
|
||||
return Result.error("获取分类列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主分类列表(用户端公共接口)
|
||||
*/
|
||||
@GetMapping("/main")
|
||||
public Result<List<Category>> getMainCategories() {
|
||||
try {
|
||||
List<Category> categories = categoryService.getMainCategories();
|
||||
return Result.success(categories);
|
||||
} catch (Exception e) {
|
||||
return Result.error("获取主分类列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类树形结构(用户端公共接口)
|
||||
*/
|
||||
@GetMapping("/tree")
|
||||
public Result<List<Category>> getCategoryTree() {
|
||||
try {
|
||||
List<Category> categories = categoryService.getCategoryTree();
|
||||
return Result.success(categories);
|
||||
} catch (Exception e) {
|
||||
return Result.error("获取分类树失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据父ID获取子分类(用户端公共接口)
|
||||
*/
|
||||
@GetMapping("/children/{parentId}")
|
||||
public Result<List<Category>> getChildCategories(@PathVariable Long parentId) {
|
||||
try {
|
||||
List<Category> categories = categoryService.getChildCategories(parentId);
|
||||
return Result.success(categories);
|
||||
} catch (Exception e) {
|
||||
return Result.error("获取子分类失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,143 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.dto.SendMessageRequest;
|
||||
import com.sunnyfarm.entity.ChatMessage;
|
||||
import com.sunnyfarm.entity.ChatSession;
|
||||
import com.sunnyfarm.service.ChatService;
|
||||
import com.sunnyfarm.service.MerchantService;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/chat")
|
||||
@CrossOrigin
|
||||
public class ChatController {
|
||||
|
||||
@Autowired
|
||||
private ChatService chatService;
|
||||
|
||||
@Autowired
|
||||
private MerchantService merchantService;
|
||||
|
||||
/**
|
||||
* 用户获取聊天会话列表
|
||||
*/
|
||||
@GetMapping("/user/sessions")
|
||||
public Result<List<ChatSession>> getUserSessions() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
List<ChatSession> sessions = chatService.getUserSessions(userId);
|
||||
return Result.success(sessions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商家获取聊天会话列表
|
||||
*/
|
||||
@GetMapping("/merchant/sessions")
|
||||
public Result<List<ChatSession>> getMerchantSessions() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Long merchantId = merchantService.getMerchantIdByUserId(userId);
|
||||
if (merchantId == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
List<ChatSession> sessions = chatService.getMerchantSessions(merchantId);
|
||||
return Result.success(sessions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取或创建聊天会话
|
||||
*/
|
||||
@PostMapping("/session")
|
||||
public Result<ChatSession> createSession(@RequestParam Long merchantId) {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
ChatSession session = chatService.getOrCreateSession(userId, merchantId);
|
||||
return Result.success(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话消息列表
|
||||
*/
|
||||
@GetMapping("/session/{sessionId}/messages")
|
||||
public Result<List<ChatMessage>> getSessionMessages(
|
||||
@PathVariable Long sessionId,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "20") Integer size) {
|
||||
|
||||
List<ChatMessage> messages = chatService.getSessionMessages(sessionId, page, size);
|
||||
return Result.success(messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户发送消息
|
||||
*/
|
||||
@PostMapping("/user/send")
|
||||
public Result<ChatMessage> sendUserMessage(@Valid @RequestBody SendMessageRequest request) {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
ChatMessage message = chatService.sendUserMessage(userId, request);
|
||||
return Result.success(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商家发送消息
|
||||
*/
|
||||
@PostMapping("/merchant/send")
|
||||
public Result<ChatMessage> sendMerchantMessage(@RequestBody SendMessageRequest request) {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Long merchantId = merchantService.getMerchantIdByUserId(userId);
|
||||
if (merchantId == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
ChatMessage message = chatService.sendMerchantMessage(
|
||||
merchantId,
|
||||
request.getMerchantId(), // 这里复用字段作为sessionId
|
||||
request.getContent(),
|
||||
request.getMessageType()
|
||||
);
|
||||
return Result.success(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记消息为已读
|
||||
*/
|
||||
@PutMapping("/session/{sessionId}/read")
|
||||
public Result<String> markAsRead(@PathVariable Long sessionId) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
|
||||
// 检查是否是商家
|
||||
Long merchantId = merchantService.getMerchantIdByUserId(userId);
|
||||
|
||||
boolean success;
|
||||
if (merchantId != null) {
|
||||
// 商家标记消息为已读 - 标记用户发送的消息(senderType=1)
|
||||
success = chatService.markMerchantMessagesAsRead(sessionId, merchantId);
|
||||
} else {
|
||||
// 普通用户标记消息为已读 - 标记商家发送的消息(senderType=2)
|
||||
success = chatService.markUserMessagesAsRead(sessionId, userId);
|
||||
}
|
||||
|
||||
return Result.success("标记成功");
|
||||
} catch (Exception e) {
|
||||
log.error("标记消息已读失败", e);
|
||||
return Result.success("标记成功"); // 返回成功避免前端错误提示
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未读消息数量
|
||||
*/
|
||||
@GetMapping("/session/{sessionId}/unread")
|
||||
public Result<Integer> getUnreadCount(@PathVariable Long sessionId) {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Integer count = chatService.getUnreadCount(sessionId, userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,204 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.common.PageResult;
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.entity.Comment;
|
||||
import com.sunnyfarm.service.CommentService;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/comment")
|
||||
@CrossOrigin
|
||||
public class CommentController {
|
||||
|
||||
@Autowired
|
||||
private CommentService commentService;
|
||||
|
||||
/**
|
||||
* 创建评论
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
public Result<Comment> createComment(@RequestBody Comment comment) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
comment.setUserId(userId);
|
||||
|
||||
// 验证必填字段
|
||||
if (comment.getMerchantId() == null) {
|
||||
return Result.error("商家ID不能为空");
|
||||
}
|
||||
if (comment.getProductId() == null) {
|
||||
return Result.error("商品ID不能为空");
|
||||
}
|
||||
if (comment.getOrderId() == null) {
|
||||
return Result.error("订单ID不能为空");
|
||||
}
|
||||
|
||||
Comment createdComment = commentService.createComment(comment);
|
||||
return Result.success("评论提交成功", createdComment);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品评论列表
|
||||
*/
|
||||
@GetMapping("/product/{productId}")
|
||||
public Result<PageResult<Comment>> getProductComments(
|
||||
@PathVariable Long productId,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
|
||||
Page<Comment> pageObj = new Page<>(page, size);
|
||||
IPage<Comment> result = commentService.getProductComments(pageObj, productId);
|
||||
|
||||
PageResult<Comment> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户评论列表
|
||||
*/
|
||||
@GetMapping("/user")
|
||||
public Result<PageResult<Comment>> getUserComments(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Page<Comment> pageObj = new Page<>(page, size);
|
||||
IPage<Comment> result = commentService.getUserComments(pageObj, userId);
|
||||
|
||||
PageResult<Comment> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品平均评分
|
||||
*/
|
||||
@GetMapping("/product/{productId}/rating")
|
||||
public Result<BigDecimal> getProductRating(@PathVariable Long productId) {
|
||||
BigDecimal rating = commentService.getProductAverageRating(productId);
|
||||
return Result.success(rating);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品评论数量
|
||||
*/
|
||||
@GetMapping("/product/{productId}/count")
|
||||
public Result<Long> getProductCommentCount(@PathVariable Long productId) {
|
||||
Long count = commentService.getProductCommentCount(productId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否已评论
|
||||
*/
|
||||
@GetMapping("/check")
|
||||
public Result<Boolean> checkUserComment(
|
||||
@RequestParam Long productId,
|
||||
@RequestParam Long orderId) {
|
||||
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
boolean hasCommented = commentService.hasUserCommented(userId, productId, orderId);
|
||||
return Result.success(hasCommented);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复评论(管理员)
|
||||
*/
|
||||
@PutMapping("/{id}/reply")
|
||||
public Result<String> replyComment(@PathVariable Long id, @RequestBody Map<String, String> replyData) {
|
||||
try {
|
||||
String reply = replyData.get("reply");
|
||||
boolean success = commentService.replyComment(id, reply);
|
||||
if (success) {
|
||||
return Result.success("回复成功");
|
||||
} else {
|
||||
return Result.error("回复失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核评论(管理员)
|
||||
*/
|
||||
@PutMapping("/{id}/audit")
|
||||
public Result<String> auditComment(@PathVariable Long id, @RequestBody Map<String, Integer> auditData) {
|
||||
try {
|
||||
Integer status = auditData.get("status");
|
||||
boolean success = commentService.auditComment(id, status);
|
||||
if (success) {
|
||||
return Result.success("审核成功");
|
||||
} else {
|
||||
return Result.error("审核失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除评论(管理员)
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<String> deleteComment(@PathVariable Long id) {
|
||||
try {
|
||||
boolean success = commentService.deleteComment(id);
|
||||
if (success) {
|
||||
return Result.success("评论删除成功");
|
||||
} else {
|
||||
return Result.error("评论删除失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有评论(管理员)
|
||||
*/
|
||||
@GetMapping("/admin/list")
|
||||
public Result<PageResult<Comment>> getAllComments(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
|
||||
Page<Comment> pageObj = new Page<>(page, size);
|
||||
IPage<Comment> result = commentService.getAllComments(pageObj, keyword, status);
|
||||
|
||||
PageResult<Comment> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.sunnyfarm.config.properties.AlipayProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/test")
|
||||
public class ConfigTestController {
|
||||
|
||||
@Autowired
|
||||
private AlipayProperties alipayProperties;
|
||||
|
||||
@GetMapping("/alipay-config")
|
||||
public Map<String, Object> testAlipayConfig() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
result.put("appId", alipayProperties.getAppId());
|
||||
result.put("hasAppPrivateKey", StringUtils.hasText(alipayProperties.getAppPrivateKey()));
|
||||
result.put("appPrivateKeyLength", alipayProperties.getAppPrivateKey() != null ? alipayProperties.getAppPrivateKey().length() : 0);
|
||||
result.put("hasAlipayPublicKey", StringUtils.hasText(alipayProperties.getAlipayPublicKey()));
|
||||
result.put("serverUrl", alipayProperties.getServerUrl());
|
||||
result.put("notifyUrl", alipayProperties.getNotifyUrl());
|
||||
result.put("returnUrl", alipayProperties.getReturnUrl());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.common.PageResult;
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.entity.Favorite;
|
||||
import com.sunnyfarm.service.FavoriteService;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/favorite")
|
||||
@CrossOrigin
|
||||
public class FavoriteController {
|
||||
|
||||
@Autowired
|
||||
private FavoriteService favoriteService;
|
||||
|
||||
/**
|
||||
* 添加收藏
|
||||
*/
|
||||
@PostMapping("/add")
|
||||
public Result<String> addFavorite(@RequestBody Map<String, Long> favoriteData) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Long productId = favoriteData.get("productId");
|
||||
|
||||
boolean success = favoriteService.addFavorite(userId, productId);
|
||||
if (success) {
|
||||
return Result.success("收藏成功");
|
||||
} else {
|
||||
return Result.error("商品已收藏");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消收藏
|
||||
*/
|
||||
@DeleteMapping("/remove")
|
||||
public Result<String> removeFavorite(@RequestBody Map<String, Long> favoriteData) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Long productId = favoriteData.get("productId");
|
||||
|
||||
boolean success = favoriteService.removeFavorite(userId, productId);
|
||||
if (success) {
|
||||
return Result.success("取消收藏成功");
|
||||
} else {
|
||||
return Result.error("取消收藏失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否已收藏
|
||||
*/
|
||||
@GetMapping("/check/{productId}")
|
||||
public Result<Boolean> checkFavorite(@PathVariable Long productId) {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
boolean isFavorited = favoriteService.isFavorited(userId, productId);
|
||||
return Result.success(isFavorited);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户收藏列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<Favorite>> getFavoriteList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Page<Favorite> pageObj = new Page<>(page, size);
|
||||
IPage<Favorite> result = favoriteService.getUserFavorites(pageObj, userId);
|
||||
|
||||
PageResult<Favorite> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户收藏数量
|
||||
*/
|
||||
@GetMapping("/count")
|
||||
public Result<Long> getFavoriteCount() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Long count = favoriteService.getUserFavoriteCount(userId);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量取消收藏
|
||||
*/
|
||||
@DeleteMapping("/batch")
|
||||
public Result<String> batchRemoveFavorite(@RequestBody Map<String, List<Long>> data) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
List<Long> productIds = data.get("productIds");
|
||||
|
||||
boolean success = favoriteService.batchRemoveFavorite(userId, productIds);
|
||||
if (success) {
|
||||
return Result.success("批量取消收藏成功");
|
||||
} else {
|
||||
return Result.error("批量取消收藏失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.util.FileUploadUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/file")
|
||||
@CrossOrigin
|
||||
public class FileController {
|
||||
|
||||
@Autowired
|
||||
private FileUploadUtil fileUploadUtil;
|
||||
|
||||
/**
|
||||
* 单文件上传
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
public Result<String> uploadFile(@RequestParam("file") MultipartFile file) {
|
||||
try {
|
||||
String fileUrl = fileUploadUtil.uploadFile(file);
|
||||
return Result.success("文件上传成功", fileUrl);
|
||||
} catch (Exception e) {
|
||||
return Result.error("文件上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多文件上传
|
||||
*/
|
||||
@PostMapping("/upload/batch")
|
||||
public Result<List<String>> uploadFiles(@RequestParam("files") MultipartFile[] files) {
|
||||
try {
|
||||
List<String> fileUrls = new ArrayList<>();
|
||||
for (MultipartFile file : files) {
|
||||
String fileUrl = fileUploadUtil.uploadFile(file);
|
||||
fileUrls.add(fileUrl);
|
||||
}
|
||||
return Result.success("文件上传成功", fileUrls);
|
||||
} catch (Exception e) {
|
||||
return Result.error("文件上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*/
|
||||
@DeleteMapping("/delete")
|
||||
public Result<String> deleteFile(@RequestParam String fileUrl) {
|
||||
try {
|
||||
boolean success = fileUploadUtil.deleteFile(fileUrl);
|
||||
if (success) {
|
||||
return Result.success("文件删除成功");
|
||||
} else {
|
||||
return Result.error("文件删除失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error("文件删除失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.entity.Admin;
|
||||
import com.sunnyfarm.entity.Role;
|
||||
import com.sunnyfarm.service.AdminService;
|
||||
import com.sunnyfarm.service.RoleService;
|
||||
import com.sunnyfarm.util.PasswordUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/init")
|
||||
@CrossOrigin
|
||||
public class InitController {
|
||||
|
||||
@Autowired
|
||||
private AdminService adminService;
|
||||
|
||||
@Autowired
|
||||
private RoleService roleService;
|
||||
|
||||
/**
|
||||
* 初始化管理员账号
|
||||
*/
|
||||
@PostMapping("/admin")
|
||||
public Result<String> initAdmin() {
|
||||
try {
|
||||
// 检查是否已经存在管理员
|
||||
Admin existingAdmin = adminService.getByUsername("admin");
|
||||
if (existingAdmin != null) {
|
||||
return Result.error("管理员账号已存在");
|
||||
}
|
||||
|
||||
// 创建管理员账号
|
||||
Admin admin = new Admin();
|
||||
admin.setUsername("admin");
|
||||
admin.setPassword(PasswordUtil.encode("admin123")); // 使用BCrypt加密
|
||||
admin.setEmail("admin@sunnyfarm.com");
|
||||
admin.setPhone("18888888888");
|
||||
admin.setRealName("系统管理员");
|
||||
admin.setRoleId(1L);
|
||||
admin.setStatus(1);
|
||||
|
||||
adminService.save(admin);
|
||||
|
||||
return Result.success("管理员账号初始化成功!用户名: admin, 密码: admin123");
|
||||
} catch (Exception e) {
|
||||
return Result.error("初始化失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置管理员密码
|
||||
*/
|
||||
@PostMapping("/reset-admin-password")
|
||||
public Result<String> resetAdminPassword() {
|
||||
try {
|
||||
Admin admin = adminService.getByUsername("admin");
|
||||
if (admin == null) {
|
||||
return Result.error("管理员账号不存在");
|
||||
}
|
||||
|
||||
// 重置密码为admin123
|
||||
admin.setPassword(PasswordUtil.encode("admin123"));
|
||||
adminService.updateById(admin);
|
||||
|
||||
return Result.success("管理员密码重置成功!新密码: admin123");
|
||||
} catch (Exception e) {
|
||||
return Result.error("重置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成密码hash(用于测试)
|
||||
*/
|
||||
@GetMapping("/generate-password/{password}")
|
||||
public Result<String> generatePassword(@PathVariable String password) {
|
||||
String encodedPassword = PasswordUtil.encode(password);
|
||||
return Result.success("加密后的密码: " + encodedPassword);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.common.PageResult;
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.dto.InventoryWithProduct;
|
||||
import com.sunnyfarm.entity.Inventory;
|
||||
import com.sunnyfarm.service.InventoryService;
|
||||
import com.sunnyfarm.service.MerchantService;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/inventory")
|
||||
@CrossOrigin
|
||||
public class InventoryController {
|
||||
|
||||
@Autowired
|
||||
private InventoryService inventoryService;
|
||||
|
||||
@Autowired
|
||||
private MerchantService merchantService;
|
||||
|
||||
/**
|
||||
* 获取商家库存统计
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
public Result<InventoryService.InventoryStatistics> getInventoryStats() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Long merchantId = merchantService.getMerchantIdByUserId(userId);
|
||||
if (merchantId == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
InventoryService.InventoryStatistics stats = inventoryService.getMerchantInventoryStats(merchantId);
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取商家库存列表
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<InventoryWithProduct>> getInventoryPage(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Long merchantId = merchantService.getMerchantIdByUserId(userId);
|
||||
if (merchantId == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
Page<InventoryWithProduct> pageObj = new Page<>(page, size);
|
||||
IPage<InventoryWithProduct> result = inventoryService.getMerchantInventoryPage(pageObj, merchantId, keyword);
|
||||
|
||||
PageResult<InventoryWithProduct> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新库存数量
|
||||
*/
|
||||
@PutMapping("/{productId}/stock")
|
||||
public Result<String> updateStock(@PathVariable Long productId, @RequestBody Map<String, Integer> data) {
|
||||
try {
|
||||
Integer quantity = data.get("quantity");
|
||||
boolean success = inventoryService.updateStockQuantity(productId, quantity);
|
||||
|
||||
if (success) {
|
||||
return Result.success("库存更新成功");
|
||||
} else {
|
||||
return Result.error("库存更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置预警数量
|
||||
*/
|
||||
@PutMapping("/{productId}/warning")
|
||||
public Result<String> setWarningQuantity(@PathVariable Long productId, @RequestBody Map<String, Integer> data) {
|
||||
try {
|
||||
Integer warningQuantity = data.get("warningQuantity");
|
||||
boolean success = inventoryService.setWarningQuantity(productId, warningQuantity);
|
||||
|
||||
if (success) {
|
||||
return Result.success("预警数量设置成功");
|
||||
} else {
|
||||
return Result.error("预警数量设置失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新库存
|
||||
*/
|
||||
@PutMapping("/batch")
|
||||
public Result<String> batchUpdateStock(@RequestBody Map<String, Object> data) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Long> productIds = (List<Long>) data.get("productIds");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Integer> quantities = (List<Integer>) data.get("quantities");
|
||||
|
||||
boolean success = inventoryService.batchUpdateStock(productIds, quantities);
|
||||
|
||||
if (success) {
|
||||
return Result.success("批量更新成功");
|
||||
} else {
|
||||
return Result.error("批量更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,431 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.common.PageResult;
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.dto.MerchantRegisterRequest;
|
||||
import com.sunnyfarm.dto.MerchantDashboardDTO;
|
||||
import com.sunnyfarm.entity.Merchant;
|
||||
import com.sunnyfarm.entity.Product;
|
||||
import com.sunnyfarm.service.MerchantService;
|
||||
import com.sunnyfarm.service.ProductService;
|
||||
import com.sunnyfarm.util.JwtUtil;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import com.sunnyfarm.util.FileUploadUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/merchant")
|
||||
@CrossOrigin
|
||||
public class MerchantController {
|
||||
|
||||
@Autowired
|
||||
private MerchantService merchantService;
|
||||
|
||||
@Autowired
|
||||
private ProductService productService;
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@Autowired
|
||||
private FileUploadUtil fileUploadUtil;
|
||||
|
||||
/**
|
||||
* 商家注册
|
||||
*/
|
||||
@PostMapping("/register")
|
||||
public Result<String> register(@Valid @RequestBody MerchantRegisterRequest request) {
|
||||
try {
|
||||
boolean success = merchantService.register(request);
|
||||
if (success) {
|
||||
return Result.success("商家注册成功,请等待审核");
|
||||
} else {
|
||||
return Result.error("商家注册失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 商家登录
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public Result<Map<String, Object>> login(@RequestBody Map<String, String> loginData) {
|
||||
try {
|
||||
String username = loginData.get("username");
|
||||
String password = loginData.get("password");
|
||||
|
||||
Merchant merchant = merchantService.login(username, password);
|
||||
if (merchant != null) {
|
||||
String token = jwtUtil.generateMerchantToken(
|
||||
merchant.getUser().getId(),
|
||||
merchant.getUser().getUsername(),
|
||||
merchant.getId()
|
||||
);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("token", token);
|
||||
result.put("user", merchant.getUser());
|
||||
result.put("merchant", merchant);
|
||||
|
||||
return Result.success("登录成功", result);
|
||||
} else {
|
||||
return Result.error("登录失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前商家信息
|
||||
*/
|
||||
@GetMapping("/profile")
|
||||
public Result<Merchant> getProfile() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Merchant merchant = merchantService.getByUserId(userId);
|
||||
return Result.success(merchant);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新商家信息
|
||||
*/
|
||||
@PutMapping("/profile")
|
||||
public Result<Merchant> updateProfile(@RequestBody Merchant merchant) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Merchant currentMerchant = merchantService.getByUserId(userId);
|
||||
if (currentMerchant == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
merchant.setId(currentMerchant.getId());
|
||||
merchant.setUserId(userId);
|
||||
|
||||
boolean success = merchantService.updateMerchantInfo(merchant);
|
||||
if (success) {
|
||||
// 重新查询更新后的商家信息
|
||||
Merchant updatedMerchant = merchantService.getByUserId(userId);
|
||||
return Result.success("更新成功", updatedMerchant);
|
||||
} else {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商家列表(管理员)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<Merchant>> getMerchantList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
|
||||
Page<Merchant> pageObj = new Page<>(page, size);
|
||||
IPage<Merchant> result = merchantService.getMerchantPage(pageObj, keyword, status);
|
||||
|
||||
PageResult<Merchant> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核商家(管理员)
|
||||
*/
|
||||
@PutMapping("/{id}/verify")
|
||||
public Result<String> verifyMerchant(@PathVariable Long id, @RequestBody Map<String, Object> verifyData) {
|
||||
try {
|
||||
Integer status = (Integer) verifyData.get("status");
|
||||
String remark = (String) verifyData.get("remark");
|
||||
|
||||
boolean success = merchantService.verifyMerchant(id, status, remark);
|
||||
if (success) {
|
||||
return Result.success("审核完成");
|
||||
} else {
|
||||
return Result.error("审核失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商家Dashboard数据
|
||||
*/
|
||||
@GetMapping("/dashboard")
|
||||
public Result<MerchantDashboardDTO> getDashboard() {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Merchant merchant = merchantService.getByUserId(userId);
|
||||
if (merchant == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
MerchantDashboardDTO dashboard = merchantService.getDashboardData(merchant.getId());
|
||||
return Result.success(dashboard);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 商品管理相关接口 ====================
|
||||
|
||||
/**
|
||||
* 获取商家商品列表
|
||||
*/
|
||||
@GetMapping("/products")
|
||||
public Result<PageResult<Product>> getMerchantProducts(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "20") Integer size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Merchant merchant = merchantService.getByUserId(userId);
|
||||
if (merchant == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
Page<Product> pageObj = new Page<>(page, size);
|
||||
IPage<Product> result = productService.getMerchantProductPage(pageObj, merchant.getId(), keyword, status);
|
||||
|
||||
PageResult<Product> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建商品
|
||||
*/
|
||||
@PostMapping("/products")
|
||||
public Result<Product> createMerchantProduct(@RequestBody Product product) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Merchant merchant = merchantService.getByUserId(userId);
|
||||
if (merchant == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
product.setMerchantId(merchant.getId());
|
||||
Product createdProduct = productService.createMerchantProduct(product);
|
||||
return Result.success("商品创建成功", createdProduct);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新商品
|
||||
*/
|
||||
@PutMapping("/products/{id}")
|
||||
public Result<Product> updateMerchantProduct(@PathVariable Long id, @RequestBody Product product) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Merchant merchant = merchantService.getByUserId(userId);
|
||||
if (merchant == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
// 检查商品是否属于当前商家
|
||||
Product existingProduct = productService.getById(id);
|
||||
if (existingProduct == null || !existingProduct.getMerchantId().equals(merchant.getId())) {
|
||||
return Result.error("商品不存在或无权限操作");
|
||||
}
|
||||
|
||||
product.setId(id);
|
||||
product.setMerchantId(merchant.getId());
|
||||
Product updatedProduct = productService.updateMerchantProduct(product);
|
||||
return Result.success("商品更新成功", updatedProduct);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除商品
|
||||
*/
|
||||
@DeleteMapping("/products/{id}")
|
||||
public Result<String> deleteMerchantProduct(@PathVariable Long id) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Merchant merchant = merchantService.getByUserId(userId);
|
||||
if (merchant == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
// 检查商品是否属于当前商家
|
||||
Product existingProduct = productService.getById(id);
|
||||
if (existingProduct == null || !existingProduct.getMerchantId().equals(merchant.getId())) {
|
||||
return Result.error("商品不存在或无权限操作");
|
||||
}
|
||||
|
||||
boolean success = productService.deleteMerchantProduct(id);
|
||||
if (success) {
|
||||
return Result.success("商品删除成功");
|
||||
} else {
|
||||
return Result.error("商品删除失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上架/下架商品
|
||||
*/
|
||||
@PutMapping("/products/{id}/status")
|
||||
public Result<String> updateMerchantProductStatus(@PathVariable Long id, @RequestBody Map<String, Integer> statusData) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Merchant merchant = merchantService.getByUserId(userId);
|
||||
if (merchant == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
// 检查商品是否属于当前商家
|
||||
Product existingProduct = productService.getById(id);
|
||||
if (existingProduct == null || !existingProduct.getMerchantId().equals(merchant.getId())) {
|
||||
return Result.error("商品不存在或无权限操作");
|
||||
}
|
||||
|
||||
Integer status = statusData.get("status");
|
||||
boolean success = productService.updateMerchantProductStatus(id, status);
|
||||
if (success) {
|
||||
String action = status == 1 ? "上架" : "下架";
|
||||
return Result.success("商品" + action + "成功");
|
||||
} else {
|
||||
return Result.error("操作失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品详情
|
||||
*/
|
||||
@GetMapping("/products/{id}")
|
||||
public Result<Product> getMerchantProductDetail(@PathVariable Long id) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Merchant merchant = merchantService.getByUserId(userId);
|
||||
if (merchant == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
Product product = productService.getMerchantProductDetail(id, merchant.getId());
|
||||
if (product != null) {
|
||||
return Result.success(product);
|
||||
} else {
|
||||
return Result.error("商品不存在或无权限查看");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商家信息(用于设置页面)
|
||||
*/
|
||||
@GetMapping("/info")
|
||||
public Result<Merchant> getMerchantInfo() {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Merchant merchant = merchantService.getByUserId(userId);
|
||||
if (merchant == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
return Result.success(merchant);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新商家信息(用于设置页面)
|
||||
*/
|
||||
@PutMapping("/info")
|
||||
public Result<String> updateMerchantInfo(@RequestBody Merchant merchant) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Merchant currentMerchant = merchantService.getByUserId(userId);
|
||||
if (currentMerchant == null) {
|
||||
return Result.error("商家信息不存在");
|
||||
}
|
||||
|
||||
merchant.setId(currentMerchant.getId());
|
||||
merchant.setUserId(userId);
|
||||
|
||||
boolean success = merchantService.updateMerchantInfo(merchant);
|
||||
if (success) {
|
||||
return Result.success("更新成功");
|
||||
} else {
|
||||
return Result.error("更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
@PutMapping("/password")
|
||||
public Result<String> changePassword(@RequestBody Map<String, String> passwordData) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
String oldPassword = passwordData.get("oldPassword");
|
||||
String newPassword = passwordData.get("newPassword");
|
||||
|
||||
boolean success = merchantService.changePassword(userId, oldPassword, newPassword);
|
||||
if (success) {
|
||||
return Result.success("密码修改成功");
|
||||
} else {
|
||||
return Result.error("原密码错误");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传商品图片
|
||||
*/
|
||||
@PostMapping("/products/upload/image")
|
||||
public Result<String> uploadProductImage(@RequestParam("file") MultipartFile file) {
|
||||
try {
|
||||
String imageUrl = fileUploadUtil.uploadFile(file);
|
||||
return Result.success("图片上传成功", imageUrl);
|
||||
} catch (Exception e) {
|
||||
return Result.error("图片上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,212 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.common.PageResult;
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.dto.OrderCreateRequest;
|
||||
import com.sunnyfarm.entity.Order;
|
||||
import com.sunnyfarm.service.OrderService;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/order")
|
||||
@CrossOrigin
|
||||
public class OrderController {
|
||||
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
|
||||
/**
|
||||
* 创建订单(支持购物车结算和立即购买)
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
public Result<Order> createOrder(@Valid @RequestBody OrderCreateRequest request) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Order order = orderService.createOrder(userId, request);
|
||||
return Result.success("订单创建成功", order);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接购买创建订单(保持向后兼容)
|
||||
*/
|
||||
@PostMapping("/buy-now")
|
||||
public Result<Order> buyNow(@RequestBody Map<String, Object> orderData) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Long addressId = Long.valueOf(orderData.get("addressId").toString());
|
||||
String remark = (String) orderData.get("remark");
|
||||
|
||||
Order order = orderService.createOrder(userId, addressId, remark);
|
||||
return Result.success("订单创建成功", order);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户订单列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<Order>> getOrderList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Page<Order> pageObj = new Page<>(page, size);
|
||||
IPage<Order> result = orderService.getUserOrders(pageObj, userId, status);
|
||||
|
||||
PageResult<Order> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单详情
|
||||
*/
|
||||
@GetMapping("/{orderId}")
|
||||
public Result<Order> getOrderDetail(@PathVariable Long orderId) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
Order order = orderService.getOrderDetail(orderId, userId);
|
||||
if (order == null) {
|
||||
return Result.error("订单不存在");
|
||||
}
|
||||
return Result.success(order);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付订单
|
||||
*/
|
||||
@PostMapping("/{orderId}/pay")
|
||||
public Result<String> payOrder(@PathVariable Long orderId, @RequestBody Map<String, String> payData) {
|
||||
try {
|
||||
String payTransactionId = payData.get("payTransactionId");
|
||||
boolean success = orderService.payOrder(orderId, payTransactionId);
|
||||
if (success) {
|
||||
return Result.success("支付成功");
|
||||
} else {
|
||||
return Result.error("支付失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订单
|
||||
*/
|
||||
@PostMapping("/{orderId}/cancel")
|
||||
public Result<String> cancelOrder(@PathVariable Long orderId, @RequestBody Map<String, String> cancelData) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
String reason = cancelData.get("reason");
|
||||
boolean success = orderService.cancelOrder(orderId, userId);
|
||||
if (success) {
|
||||
return Result.success("订单已取消");
|
||||
} else {
|
||||
return Result.error("取消失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认收货
|
||||
*/
|
||||
@PostMapping("/{orderId}/confirm")
|
||||
public Result<String> confirmOrder(@PathVariable Long orderId) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
boolean success = orderService.confirmOrder(orderId, userId);
|
||||
if (success) {
|
||||
return Result.success("确认收货成功");
|
||||
} else {
|
||||
return Result.error("确认收货失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除订单
|
||||
*/
|
||||
@DeleteMapping("/{orderId}")
|
||||
public Result<String> deleteOrder(@PathVariable Long orderId) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
boolean success = orderService.deleteOrder(orderId, userId);
|
||||
if (success) {
|
||||
return Result.success("订单已删除");
|
||||
} else {
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 商家获取订单列表
|
||||
*/
|
||||
@GetMapping("/merchant/list")
|
||||
public Result<PageResult<Order>> getMerchantOrderList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
|
||||
Long merchantId = UserContext.getMerchantId();
|
||||
Page<Order> pageObj = new Page<>(page, size);
|
||||
IPage<Order> result = orderService.getMerchantOrders(pageObj, merchantId, status);
|
||||
|
||||
PageResult<Order> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商家发货
|
||||
*/
|
||||
@PostMapping("/{orderId}/ship")
|
||||
public Result<String> shipOrder(@PathVariable Long orderId, @RequestBody Map<String, String> shipData) {
|
||||
try {
|
||||
String shipCompany = shipData.get("shipCompany");
|
||||
String shipNo = shipData.get("shipNo");
|
||||
boolean success = orderService.shipOrder(orderId, shipCompany, shipNo);
|
||||
if (success) {
|
||||
return Result.success("发货成功");
|
||||
} else {
|
||||
return Result.error("发货失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,192 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.entity.Order;
|
||||
import com.sunnyfarm.entity.Payment;
|
||||
import com.sunnyfarm.service.AlipayService;
|
||||
import com.sunnyfarm.service.OrderService;
|
||||
import com.sunnyfarm.service.PaymentService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/payment")
|
||||
@CrossOrigin
|
||||
public class PaymentController {
|
||||
|
||||
@Autowired
|
||||
private AlipayService alipayService;
|
||||
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
|
||||
@Autowired
|
||||
private PaymentService paymentService;
|
||||
|
||||
/**
|
||||
* 创建支付宝支付表单
|
||||
*/
|
||||
@PostMapping("/alipay/create")
|
||||
public Result<String> createAlipay(@RequestBody Map<String, Object> payData) {
|
||||
try {
|
||||
Long orderId = Long.valueOf(payData.get("orderId").toString());
|
||||
|
||||
// 获取订单信息
|
||||
Order order = orderService.getById(orderId);
|
||||
if (order == null) {
|
||||
return Result.error("订单不存在");
|
||||
}
|
||||
|
||||
if (order.getStatus() != 1) {
|
||||
return Result.error("订单状态不正确,无法支付");
|
||||
}
|
||||
|
||||
String subject = "农产品订单-" + order.getOrderNo();
|
||||
BigDecimal amount = order.getActualAmount();
|
||||
|
||||
String payForm = alipayService.createPayForm(orderId, subject, amount);
|
||||
|
||||
return Result.success("创建支付成功", payForm);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("创建支付失败", e);
|
||||
return Result.error("创建支付失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付宝异步回调
|
||||
*/
|
||||
@PostMapping("/alipay/notify")
|
||||
public String alipayNotify(HttpServletRequest request) {
|
||||
try {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
Map<String, String[]> requestParams = request.getParameterMap();
|
||||
|
||||
for (String name : requestParams.keySet()) {
|
||||
String[] values = requestParams.get(name);
|
||||
String valueStr = "";
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
|
||||
}
|
||||
params.put(name, valueStr);
|
||||
}
|
||||
|
||||
log.info("收到支付宝回调: {}", params);
|
||||
|
||||
boolean success = alipayService.handlePayNotify(params);
|
||||
|
||||
if (success) {
|
||||
return "success";
|
||||
} else {
|
||||
return "failure";
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理支付回调异常", e);
|
||||
return "failure";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付宝同步返回
|
||||
*/
|
||||
@GetMapping("/alipay/return")
|
||||
public void alipayReturn(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
try {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
Map<String, String[]> requestParams = request.getParameterMap();
|
||||
|
||||
for (String name : requestParams.keySet()) {
|
||||
String[] values = requestParams.get(name);
|
||||
String valueStr = "";
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
|
||||
}
|
||||
params.put(name, valueStr);
|
||||
}
|
||||
|
||||
boolean verifyResult = alipayService.verifySign(params);
|
||||
|
||||
if (verifyResult) {
|
||||
String outTradeNo = params.get("out_trade_no");
|
||||
String tradeStatus = params.get("trade_status");
|
||||
|
||||
if ("TRADE_SUCCESS".equals(tradeStatus)) {
|
||||
// 支付成功,跳转到成功页面
|
||||
response.sendRedirect("http://localhost:3000/orders?status=2&payment=success");
|
||||
} else {
|
||||
// 支付失败,跳转到失败页面
|
||||
response.sendRedirect("http://localhost:3000/orders?payment=failed");
|
||||
}
|
||||
} else {
|
||||
response.sendRedirect("http://localhost:3000/orders?payment=error");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理支付返回异常", e);
|
||||
response.sendRedirect("http://localhost:3000/orders?payment=error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询支付结果
|
||||
*/
|
||||
@GetMapping("/query/{paymentNo}")
|
||||
public Result<Map<String, Object>> queryPayResult(@PathVariable String paymentNo) {
|
||||
try {
|
||||
Map<String, Object> result = alipayService.queryPayResult(paymentNo);
|
||||
return Result.success("查询成功", result);
|
||||
} catch (Exception e) {
|
||||
log.error("查询支付结果异常", e);
|
||||
return Result.error("查询失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请退款
|
||||
*/
|
||||
@PostMapping("/refund")
|
||||
public Result<Map<String, Object>> refund(@RequestBody Map<String, Object> refundData) {
|
||||
try {
|
||||
String paymentNo = (String) refundData.get("paymentNo");
|
||||
BigDecimal refundAmount = new BigDecimal(refundData.get("refundAmount").toString());
|
||||
String refundReason = (String) refundData.get("refundReason");
|
||||
|
||||
Map<String, Object> result = alipayService.refund(paymentNo, refundAmount, refundReason);
|
||||
|
||||
if ((Boolean) result.get("success")) {
|
||||
return Result.success("退款成功", result);
|
||||
} else {
|
||||
return Result.error("退款失败: " + result.get("msg"));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("退款异常", e);
|
||||
return Result.error("退款失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单支付记录
|
||||
*/
|
||||
@GetMapping("/order/{orderId}")
|
||||
public Result<Payment> getOrderPayment(@PathVariable Long orderId) {
|
||||
try {
|
||||
Payment payment = paymentService.getByOrderId(orderId);
|
||||
return Result.success("获取成功", payment);
|
||||
} catch (Exception e) {
|
||||
log.error("获取支付记录异常", e);
|
||||
return Result.error("获取失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.entity.Order;
|
||||
import com.sunnyfarm.entity.Payment;
|
||||
import com.sunnyfarm.service.AlipayService;
|
||||
import com.sunnyfarm.service.OrderService;
|
||||
import com.sunnyfarm.service.PaymentService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/payment/status")
|
||||
@CrossOrigin
|
||||
public class PaymentStatusController {
|
||||
|
||||
@Autowired
|
||||
private PaymentService paymentService;
|
||||
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
|
||||
@Autowired
|
||||
private AlipayService alipayService;
|
||||
|
||||
/**
|
||||
* 检查订单支付状态
|
||||
*/
|
||||
@GetMapping("/check/{orderId}")
|
||||
public Result<Map<String, Object>> checkPaymentStatus(@PathVariable Long orderId) {
|
||||
try {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
// 获取订单信息
|
||||
Order order = orderService.getById(orderId);
|
||||
if (order == null) {
|
||||
return Result.error("订单不存在");
|
||||
}
|
||||
|
||||
result.put("orderStatus", order.getStatus());
|
||||
result.put("orderStatusText", getOrderStatusText(order.getStatus()));
|
||||
|
||||
// 获取支付记录
|
||||
Payment payment = paymentService.getByOrderId(orderId);
|
||||
if (payment != null) {
|
||||
result.put("paymentStatus", payment.getStatus());
|
||||
result.put("paymentStatusText", getPaymentStatusText(payment.getStatus()));
|
||||
result.put("paymentNo", payment.getPaymentNo());
|
||||
|
||||
// 如果支付记录状态为待支付,查询支付宝最新状态
|
||||
if (payment.getStatus() == 1 && order.getStatus() == 1) {
|
||||
log.info("🔍 订单{}支付状态待确认,查询支付宝最新状态", orderId);
|
||||
Map<String, Object> alipayResult = alipayService.queryPayResult(payment.getPaymentNo());
|
||||
|
||||
if ((Boolean) alipayResult.get("success")) {
|
||||
String tradeStatus = (String) alipayResult.get("tradeStatus");
|
||||
if ("TRADE_SUCCESS".equals(tradeStatus)) {
|
||||
log.info("🔄 发现支付成功但状态未更新,手动同步状态");
|
||||
// 手动同步支付状态
|
||||
Map<String, String> syncParams = new HashMap<>();
|
||||
syncParams.put("out_trade_no", payment.getPaymentNo());
|
||||
syncParams.put("trade_status", "TRADE_SUCCESS");
|
||||
syncParams.put("trade_no", (String) alipayResult.get("tradeNo"));
|
||||
syncParams.put("buyer_pay_amount", (String) alipayResult.get("buyerPayAmount"));
|
||||
syncParams.put("buyer_logon_id", (String) alipayResult.get("buyerLogonId"));
|
||||
|
||||
alipayService.handlePayNotify(syncParams);
|
||||
|
||||
// 重新获取最新状态
|
||||
order = orderService.getById(orderId);
|
||||
payment = paymentService.getByOrderId(orderId);
|
||||
|
||||
result.put("orderStatus", order.getStatus());
|
||||
result.put("orderStatusText", getOrderStatusText(order.getStatus()));
|
||||
result.put("paymentStatus", payment.getStatus());
|
||||
result.put("paymentStatusText", getPaymentStatusText(payment.getStatus()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result.success("查询成功", result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("查询支付状态异常", e);
|
||||
return Result.error("查询失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String getOrderStatusText(Integer status) {
|
||||
switch (status) {
|
||||
case 1: return "待支付";
|
||||
case 2: return "已支付";
|
||||
case 3: return "已发货";
|
||||
case 4: return "已完成";
|
||||
case 5: return "已取消";
|
||||
case 6: return "已退款";
|
||||
default: return "未知状态";
|
||||
}
|
||||
}
|
||||
|
||||
private String getPaymentStatusText(Integer status) {
|
||||
switch (status) {
|
||||
case 1: return "待支付";
|
||||
case 2: return "已支付";
|
||||
case 3: return "支付失败";
|
||||
case 4: return "已退款";
|
||||
default: return "未知状态";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,227 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.common.PageResult;
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.entity.Product;
|
||||
import com.sunnyfarm.service.ProductService;
|
||||
import com.sunnyfarm.util.FileUploadUtil;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/product")
|
||||
@CrossOrigin
|
||||
public class ProductController {
|
||||
|
||||
@Autowired
|
||||
private ProductService productService;
|
||||
|
||||
@Autowired
|
||||
private FileUploadUtil fileUploadUtil;
|
||||
|
||||
/**
|
||||
* 获取产品列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<Product>> getProductList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "12") Integer size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Long categoryId,
|
||||
@RequestParam(required = false) BigDecimal minPrice,
|
||||
@RequestParam(required = false) BigDecimal maxPrice,
|
||||
@RequestParam(required = false) String sort) {
|
||||
|
||||
Page<Product> pageObj = new Page<>(page, size);
|
||||
IPage<Product> result = productService.getProductList(pageObj, keyword, categoryId, minPrice, maxPrice, sort);
|
||||
|
||||
PageResult<Product> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品详情
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<Product> getProductDetail(@PathVariable Long id) {
|
||||
Product product = productService.getProductDetail(id);
|
||||
if (product != null) {
|
||||
return Result.success(product);
|
||||
} else {
|
||||
return Result.error("产品不存在");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取热门产品
|
||||
*/
|
||||
@GetMapping("/hot")
|
||||
public Result<List<Product>> getHotProducts(@RequestParam(defaultValue = "8") Integer limit) {
|
||||
List<Product> products = productService.getHotProducts(limit);
|
||||
return Result.success(products);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取新品推荐
|
||||
*/
|
||||
@GetMapping("/new")
|
||||
public Result<List<Product>> getNewProducts(@RequestParam(defaultValue = "8") Integer limit) {
|
||||
List<Product> products = productService.getNewProducts(limit);
|
||||
return Result.success(products);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类产品
|
||||
*/
|
||||
@GetMapping("/category/{categoryId}")
|
||||
public Result<PageResult<Product>> getCategoryProducts(
|
||||
@PathVariable Long categoryId,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "12") Integer size,
|
||||
@RequestParam(required = false) String sort) {
|
||||
|
||||
Page<Product> pageObj = new Page<>(page, size);
|
||||
IPage<Product> result = productService.getCategoryProducts(pageObj, categoryId, sort);
|
||||
|
||||
PageResult<Product> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索产品
|
||||
*/
|
||||
@GetMapping("/search")
|
||||
public Result<PageResult<Product>> searchProducts(
|
||||
@RequestParam String keyword,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "12") Integer size,
|
||||
@RequestParam(required = false) String sort) {
|
||||
|
||||
Page<Product> pageObj = new Page<>(page, size);
|
||||
IPage<Product> result = productService.searchProducts(pageObj, keyword, sort);
|
||||
|
||||
PageResult<Product> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建产品(管理员)
|
||||
*/
|
||||
@PostMapping
|
||||
public Result<Product> createProduct(@RequestBody Product product) {
|
||||
try {
|
||||
Product createdProduct = productService.createProduct(product);
|
||||
return Result.success("产品创建成功", createdProduct);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新产品(管理员)
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public Result<Product> updateProduct(@PathVariable Long id, @RequestBody Product product) {
|
||||
try {
|
||||
Product updatedProduct = productService.updateProduct(id, product);
|
||||
return Result.success("产品更新成功", updatedProduct);
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除产品(管理员)
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<String> deleteProduct(@PathVariable Long id) {
|
||||
try {
|
||||
boolean success = productService.deleteProduct(id);
|
||||
if (success) {
|
||||
return Result.success("产品删除成功");
|
||||
} else {
|
||||
return Result.error("产品删除失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传产品图片
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
public Result<String> uploadImage(@RequestParam("file") MultipartFile file) {
|
||||
try {
|
||||
String imageUrl = fileUploadUtil.uploadFile(file);
|
||||
return Result.success("图片上传成功", imageUrl);
|
||||
} catch (Exception e) {
|
||||
return Result.error("图片上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新产品库存(管理员)
|
||||
*/
|
||||
@PutMapping("/{id}/stock")
|
||||
public Result<String> updateStock(@PathVariable Long id, @RequestBody Map<String, Integer> stockData) {
|
||||
try {
|
||||
Integer stock = stockData.get("stock");
|
||||
boolean success = productService.updateProductStock(id, stock);
|
||||
if (success) {
|
||||
return Result.success("库存更新成功");
|
||||
} else {
|
||||
return Result.error("库存更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新产品状态(管理员)
|
||||
*/
|
||||
@PutMapping("/{id}/status")
|
||||
public Result<String> updateStatus(@PathVariable Long id, @RequestBody Map<String, Integer> statusData) {
|
||||
try {
|
||||
Integer status = statusData.get("status");
|
||||
boolean success = productService.updateProductStatus(id, status);
|
||||
if (success) {
|
||||
return Result.success("状态更新成功");
|
||||
} else {
|
||||
return Result.error("状态更新失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.service.StatisticsService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/statistics")
|
||||
@CrossOrigin
|
||||
public class StatisticsController {
|
||||
|
||||
@Autowired
|
||||
private StatisticsService statisticsService;
|
||||
|
||||
/**
|
||||
* 获取系统统计数据(管理员)
|
||||
*/
|
||||
@GetMapping("/overview")
|
||||
public Result<Map<String, Object>> getOverviewStatistics() {
|
||||
Map<String, Object> statistics = statisticsService.getOverviewStatistics();
|
||||
return Result.success(statistics);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取销售统计数据(管理员)
|
||||
*/
|
||||
@GetMapping("/sales")
|
||||
public Result<Map<String, Object>> getSalesStatistics(
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate) {
|
||||
Map<String, Object> statistics = statisticsService.getSalesStatistics(startDate, endDate);
|
||||
return Result.success(statistics);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品统计数据(管理员)
|
||||
*/
|
||||
@GetMapping("/product")
|
||||
public Result<Map<String, Object>> getProductStatistics() {
|
||||
Map<String, Object> statistics = statisticsService.getProductStatistics();
|
||||
return Result.success(statistics);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户统计数据(管理员)
|
||||
*/
|
||||
@GetMapping("/user")
|
||||
public Result<Map<String, Object>> getUserStatistics() {
|
||||
Map<String, Object> statistics = statisticsService.getUserStatistics();
|
||||
return Result.success(statistics);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,206 @@
|
||||
package com.sunnyfarm.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.common.PageResult;
|
||||
import com.sunnyfarm.common.Result;
|
||||
import com.sunnyfarm.entity.User;
|
||||
import com.sunnyfarm.service.UserService;
|
||||
import com.sunnyfarm.util.JwtUtil;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import com.sunnyfarm.dto.EmailCodeRequest;
|
||||
import com.sunnyfarm.dto.VerifyCodeRequest;
|
||||
import com.sunnyfarm.dto.RegisterRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/user")
|
||||
@CrossOrigin
|
||||
public class UserController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*/
|
||||
@PostMapping("/register")
|
||||
public Result<String> register(@Valid @RequestBody RegisterRequest request) {
|
||||
try {
|
||||
// 验证邮箱验证码
|
||||
if (!userService.verifyEmailCode(request.getEmail(), request.getEmailCode())) {
|
||||
return Result.error("邮箱验证码错误或已过期");
|
||||
}
|
||||
|
||||
// 创建用户对象
|
||||
User user = new User();
|
||||
user.setUsername(request.getUsername());
|
||||
user.setPhone(request.getPhone());
|
||||
user.setEmail(request.getEmail());
|
||||
user.setPassword(request.getPassword());
|
||||
|
||||
User registeredUser = userService.register(user);
|
||||
return Result.success("注册成功");
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public Result<Map<String, Object>> login(@RequestBody Map<String, String> loginData) {
|
||||
try {
|
||||
String username = loginData.get("username");
|
||||
String password = loginData.get("password");
|
||||
|
||||
User user = userService.login(username, password);
|
||||
if (user != null) {
|
||||
String token = jwtUtil.generateToken(user.getId(), user.getUsername());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("token", token);
|
||||
result.put("user", user);
|
||||
|
||||
return Result.success("登录成功", result);
|
||||
} else {
|
||||
return Result.error("用户名或密码错误");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
@GetMapping("/profile")
|
||||
public Result<User> getProfile() {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
User user = userService.getById(userId);
|
||||
return Result.success(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*/
|
||||
@PutMapping("/profile")
|
||||
public Result<User> updateProfile(@RequestBody User user) {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
user.setId(userId);
|
||||
User updatedUser = userService.updateProfile(user);
|
||||
return Result.success("更新成功", updatedUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
@PutMapping("/password")
|
||||
public Result<String> changePassword(@RequestBody Map<String, String> passwordData) {
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
String oldPassword = passwordData.get("oldPassword");
|
||||
String newPassword = passwordData.get("newPassword");
|
||||
|
||||
boolean success = userService.changePassword(userId, oldPassword, newPassword);
|
||||
if (success) {
|
||||
return Result.success("密码修改成功");
|
||||
} else {
|
||||
return Result.error("原密码错误");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注销
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public Result<String> logout() {
|
||||
// JWT是无状态的,客户端删除token即可
|
||||
return Result.success("注销成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮箱验证码
|
||||
*/
|
||||
@PostMapping("/send-email-code")
|
||||
public Result<String> sendEmailCode(@Valid @RequestBody EmailCodeRequest request) {
|
||||
try {
|
||||
boolean success = userService.sendEmailCode(request.getEmail());
|
||||
if (success) {
|
||||
return Result.success("验证码发送成功");
|
||||
} else {
|
||||
return Result.error("验证码发送失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证邮箱验证码
|
||||
*/
|
||||
@PostMapping("/verify-email-code")
|
||||
public Result<String> verifyEmailCode(@Valid @RequestBody VerifyCodeRequest request) {
|
||||
try {
|
||||
boolean valid = userService.verifyEmailCode(request.getEmail(), request.getCode());
|
||||
if (valid) {
|
||||
return Result.success("验证码验证成功");
|
||||
} else {
|
||||
return Result.error("验证码错误或已过期");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户列表(管理员)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<User>> getUserList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
|
||||
Page<User> pageObj = new Page<>(page, size);
|
||||
IPage<User> result = userService.getUserList(pageObj, keyword, status);
|
||||
|
||||
PageResult<User> pageResult = PageResult.of(
|
||||
result.getTotal(),
|
||||
result.getPages(),
|
||||
result.getCurrent(),
|
||||
result.getSize(),
|
||||
result.getRecords()
|
||||
);
|
||||
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户状态(管理员)
|
||||
*/
|
||||
@PutMapping("/{id}/status")
|
||||
public Result<String> updateUserStatus(@PathVariable Long id, @RequestBody Map<String, Integer> statusData) {
|
||||
Integer status = statusData.get("status");
|
||||
boolean success = userService.updateUserStatus(id, status);
|
||||
if (success) {
|
||||
return Result.success("状态更新成功");
|
||||
} else {
|
||||
return Result.error("状态更新失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class AdminDashboardDTO {
|
||||
|
||||
// 用户统计
|
||||
private Long totalUsers;
|
||||
private Long newUsersToday;
|
||||
|
||||
// 商家统计
|
||||
private Long totalMerchants;
|
||||
private Long pendingMerchants;
|
||||
|
||||
// 订单统计
|
||||
private Long totalOrders;
|
||||
private Long newOrdersToday;
|
||||
|
||||
// 商品统计
|
||||
private Long totalProducts;
|
||||
private Long pendingProducts;
|
||||
|
||||
// 收入统计
|
||||
private Double totalRevenue;
|
||||
private Double todayRevenue;
|
||||
|
||||
// 待处理事项
|
||||
private Long pendingComplaints;
|
||||
|
||||
// 系统信息
|
||||
private String systemVersion;
|
||||
private LocalDateTime lastUpdateTime;
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class AdminLoginRequest {
|
||||
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AdminMerchantPageDTO {
|
||||
private Integer pageNum = 1;
|
||||
private Integer pageSize = 10;
|
||||
private String keyword;
|
||||
private Integer status;
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class AdminOrderPageDTO {
|
||||
private Integer pageNum = 1;
|
||||
private Integer pageSize = 10;
|
||||
private String keyword;
|
||||
private Integer status;
|
||||
private LocalDateTime startTime;
|
||||
private LocalDateTime endTime;
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Data
|
||||
public class AdminPasswordRequest {
|
||||
|
||||
@NotBlank(message = "原密码不能为空")
|
||||
private String oldPassword;
|
||||
|
||||
@NotBlank(message = "新密码不能为空")
|
||||
@Size(min = 6, max = 20, message = "新密码长度必须在6-20个字符之间")
|
||||
private String newPassword;
|
||||
|
||||
@NotBlank(message = "确认密码不能为空")
|
||||
private String confirmPassword;
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AdminProductPageDTO {
|
||||
private Integer pageNum = 1;
|
||||
private Integer pageSize = 10;
|
||||
private String keyword;
|
||||
private Integer status;
|
||||
private Long categoryId;
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Data
|
||||
public class AdminUpdateRequest {
|
||||
|
||||
@Size(max = 100, message = "邮箱长度不能超过100个字符")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
@Size(max = 20, message = "手机号长度不能超过20个字符")
|
||||
private String phone;
|
||||
|
||||
@Size(max = 50, message = "真实姓名不能超过50个字符")
|
||||
private String realName;
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AdminUserPageDTO {
|
||||
private Integer pageNum = 1;
|
||||
private Integer pageSize = 10;
|
||||
private String keyword;
|
||||
private Integer status;
|
||||
private Integer isVerified;
|
||||
}
|
||||
32
backend/src/main/java/com/sunnyfarm/dto/CartItemDTO.java
Normal file
32
backend/src/main/java/com/sunnyfarm/dto/CartItemDTO.java
Normal file
@ -0,0 +1,32 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class CartItemDTO {
|
||||
|
||||
private Long id;
|
||||
private Long userId;
|
||||
private Long productId;
|
||||
private Integer quantity;
|
||||
private Boolean selected;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
// 商品信息
|
||||
private String productName;
|
||||
private String productImage;
|
||||
private BigDecimal price;
|
||||
private BigDecimal originPrice;
|
||||
private String origin;
|
||||
private String unit;
|
||||
private Integer status;
|
||||
private Long merchantId;
|
||||
private String merchantName;
|
||||
|
||||
// 库存信息
|
||||
private Integer stockQuantity;
|
||||
}
|
||||
20
backend/src/main/java/com/sunnyfarm/dto/ChatSessionDTO.java
Normal file
20
backend/src/main/java/com/sunnyfarm/dto/ChatSessionDTO.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class ChatSessionDTO {
|
||||
private Long id;
|
||||
private Long userId;
|
||||
private Long merchantId;
|
||||
private String lastMessage;
|
||||
private LocalDateTime lastMessageTime;
|
||||
private Integer status;
|
||||
private String userName;
|
||||
private String merchantName;
|
||||
private Integer unreadCount;
|
||||
private String userAvatar;
|
||||
private String merchantAvatar;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
public class EmailCodeRequest {
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
public EmailCodeRequest() {}
|
||||
|
||||
public EmailCodeRequest(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class InventoryWithProduct {
|
||||
|
||||
// 库存信息
|
||||
private Long id;
|
||||
private Long productId;
|
||||
private Integer stockQuantity;
|
||||
private Integer warningQuantity;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
// 商品基本信息
|
||||
private String productName;
|
||||
private BigDecimal price;
|
||||
private Integer productStatus;
|
||||
private String unit;
|
||||
private String description;
|
||||
private String origin;
|
||||
private Integer salesCount;
|
||||
private Integer viewCount;
|
||||
|
||||
// 商品图片信息
|
||||
private String mainImage;
|
||||
|
||||
// 分类信息
|
||||
private Long categoryId;
|
||||
private String categoryName;
|
||||
|
||||
// 商家信息
|
||||
private Long merchantId;
|
||||
private String merchantName;
|
||||
|
||||
// 创建时间
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class MerchantDashboardDTO {
|
||||
|
||||
/**
|
||||
* 总订单数
|
||||
*/
|
||||
private Integer totalOrders;
|
||||
|
||||
/**
|
||||
* 今日订单数
|
||||
*/
|
||||
private Integer todayOrders;
|
||||
|
||||
/**
|
||||
* 总收入
|
||||
*/
|
||||
private BigDecimal totalRevenue;
|
||||
|
||||
/**
|
||||
* 今日收入
|
||||
*/
|
||||
private BigDecimal todayRevenue;
|
||||
|
||||
/**
|
||||
* 商品总数
|
||||
*/
|
||||
private Long totalProducts;
|
||||
|
||||
/**
|
||||
* 在售商品数
|
||||
*/
|
||||
private Integer activeProducts;
|
||||
|
||||
/**
|
||||
* 低库存商品数
|
||||
*/
|
||||
private Integer lowStockProducts;
|
||||
|
||||
/**
|
||||
* 客户总数
|
||||
*/
|
||||
private Long totalCustomers;
|
||||
|
||||
/**
|
||||
* 最近订单列表
|
||||
*/
|
||||
private List<RecentOrderDTO> recentOrders;
|
||||
|
||||
/**
|
||||
* 销售趋势数据
|
||||
*/
|
||||
private List<SalesTrendDTO> salesTrend;
|
||||
|
||||
/**
|
||||
* 商品销量排行
|
||||
*/
|
||||
private List<ProductSalesDTO> topProducts;
|
||||
|
||||
@Data
|
||||
public static class RecentOrderDTO {
|
||||
private String orderNo;
|
||||
private String customerName;
|
||||
private BigDecimal totalAmount;
|
||||
private Integer status;
|
||||
private String createTime;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class SalesTrendDTO {
|
||||
private String date;
|
||||
private BigDecimal sales;
|
||||
private Long orders;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ProductSalesDTO {
|
||||
private String productName;
|
||||
private Long salesCount;
|
||||
private BigDecimal salesAmount;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Data
|
||||
public class MerchantRegisterRequest {
|
||||
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 3, max = 20, message = "用户名长度在3到20个字符")
|
||||
private String username;
|
||||
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "请输入正确的手机号")
|
||||
private String phone;
|
||||
|
||||
@Email(message = "请输入正确的邮箱格式")
|
||||
private String email;
|
||||
|
||||
@NotBlank(message = "邮箱验证码不能为空")
|
||||
@Size(min = 6, max = 6, message = "验证码为6位数字")
|
||||
private String emailCode;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 20, message = "密码长度在6到20个字符")
|
||||
private String password;
|
||||
|
||||
@NotBlank(message = "店铺名称不能为空")
|
||||
@Size(max = 100, message = "店铺名称不能超过100个字符")
|
||||
private String shopName;
|
||||
|
||||
@NotBlank(message = "法人姓名不能为空")
|
||||
@Size(max = 50, message = "法人姓名不能超过50个字符")
|
||||
private String legalPerson;
|
||||
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "请输入正确的联系电话")
|
||||
private String contactPhone;
|
||||
|
||||
@Size(max = 500, message = "经营范围不能超过500个字符")
|
||||
private String businessScope;
|
||||
|
||||
@Size(max = 200, message = "经营地址不能超过200个字符")
|
||||
private String address;
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class OrderCreateRequest {
|
||||
|
||||
/**
|
||||
* 订单类型:1-立即购买,2-购物车结算
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 收货地址ID
|
||||
*/
|
||||
private Long addressId;
|
||||
|
||||
/**
|
||||
* 订单备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 购物车ID列表(购物车结算时使用)
|
||||
*/
|
||||
private List<Long> cartIds;
|
||||
|
||||
/**
|
||||
* 商品信息列表(立即购买时使用)
|
||||
*/
|
||||
private List<OrderItemRequest> items;
|
||||
|
||||
@Data
|
||||
public static class OrderItemRequest {
|
||||
/**
|
||||
* 商品ID
|
||||
*/
|
||||
private Long productId;
|
||||
|
||||
/**
|
||||
* 购买数量
|
||||
*/
|
||||
private Integer quantity;
|
||||
|
||||
/**
|
||||
* 商品价格
|
||||
*/
|
||||
private BigDecimal price;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证订单创建请求是否有效
|
||||
* 至少要有购物车ID列表或商品列表中的一种
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return (cartIds != null && !cartIds.isEmpty()) ||
|
||||
(items != null && !items.isEmpty());
|
||||
}
|
||||
}
|
||||
37
backend/src/main/java/com/sunnyfarm/dto/OrderDetailDTO.java
Normal file
37
backend/src/main/java/com/sunnyfarm/dto/OrderDetailDTO.java
Normal file
@ -0,0 +1,37 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class OrderDetailDTO {
|
||||
|
||||
private Long id;
|
||||
private String orderNo;
|
||||
private Long userId;
|
||||
private Long merchantId;
|
||||
private String merchantName;
|
||||
private BigDecimal totalAmount;
|
||||
private BigDecimal discountAmount;
|
||||
private BigDecimal actualAmount;
|
||||
private Integer status;
|
||||
private String statusName;
|
||||
private String consignee;
|
||||
private String phone;
|
||||
private String address;
|
||||
private String remark;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
// 订单项列表
|
||||
private List<OrderItemDTO> orderItems;
|
||||
|
||||
// 物流信息
|
||||
private String logisticsCompany;
|
||||
private String trackingNumber;
|
||||
private Integer logisticsStatus;
|
||||
private String logisticsStatusName;
|
||||
}
|
||||
18
backend/src/main/java/com/sunnyfarm/dto/OrderItemDTO.java
Normal file
18
backend/src/main/java/com/sunnyfarm/dto/OrderItemDTO.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
public class OrderItemDTO {
|
||||
|
||||
private Long id;
|
||||
private Long orderId;
|
||||
private Long productId;
|
||||
private String productName;
|
||||
private String productImage;
|
||||
private BigDecimal price;
|
||||
private Integer quantity;
|
||||
private BigDecimal totalAmount;
|
||||
}
|
||||
73
backend/src/main/java/com/sunnyfarm/dto/RegisterRequest.java
Normal file
73
backend/src/main/java/com/sunnyfarm/dto/RegisterRequest.java
Normal file
@ -0,0 +1,73 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class RegisterRequest {
|
||||
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 3, max = 20, message = "用户名长度在3到20个字符")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
private String phone;
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
@NotBlank(message = "验证码不能为空")
|
||||
@Pattern(regexp = "^\\d{6}$", message = "验证码必须为6位数字")
|
||||
private String emailCode;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 20, message = "密码长度在6到20个字符")
|
||||
private String password;
|
||||
|
||||
// 构造函数
|
||||
public RegisterRequest() {}
|
||||
|
||||
// Getters and Setters
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getEmailCode() {
|
||||
return emailCode;
|
||||
}
|
||||
|
||||
public void setEmailCode(String emailCode) {
|
||||
this.emailCode = emailCode;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
public class SendMessageRequest {
|
||||
|
||||
@NotNull(message = "商家ID不能为空")
|
||||
private Long merchantId;
|
||||
|
||||
@NotNull(message = "消息类型不能为空")
|
||||
private Integer messageType; // 1文本,2图片,3文件
|
||||
|
||||
@NotBlank(message = "消息内容不能为空")
|
||||
private String content;
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.sunnyfarm.dto;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
public class VerifyCodeRequest {
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
@NotBlank(message = "验证码不能为空")
|
||||
@Pattern(regexp = "^\\d{6}$", message = "验证码必须为6位数字")
|
||||
private String code;
|
||||
|
||||
public VerifyCodeRequest() {}
|
||||
|
||||
public VerifyCodeRequest(String email, String code) {
|
||||
this.email = email;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
45
backend/src/main/java/com/sunnyfarm/entity/Address.java
Normal file
45
backend/src/main/java/com/sunnyfarm/entity/Address.java
Normal file
@ -0,0 +1,45 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("user_addresses")
|
||||
public class Address extends BaseEntity {
|
||||
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
private Long userId;
|
||||
|
||||
@NotBlank(message = "收货人姓名不能为空")
|
||||
@Size(max = 50, message = "收货人姓名长度不能超过50个字符")
|
||||
private String consignee;
|
||||
|
||||
@NotBlank(message = "收货人电话不能为空")
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
private String phone;
|
||||
|
||||
@NotBlank(message = "省份不能为空")
|
||||
@Size(max = 50, message = "省份长度不能超过50个字符")
|
||||
private String province;
|
||||
|
||||
@NotBlank(message = "城市不能为空")
|
||||
@Size(max = 50, message = "城市长度不能超过50个字符")
|
||||
private String city;
|
||||
|
||||
@NotBlank(message = "区县不能为空")
|
||||
@Size(max = 50, message = "区县长度不能超过50个字符")
|
||||
private String district;
|
||||
|
||||
@NotBlank(message = "详细地址不能为空")
|
||||
@Size(max = 200, message = "详细地址长度不能超过200个字符")
|
||||
private String address;
|
||||
|
||||
private Boolean isDefault = false;
|
||||
}
|
||||
49
backend/src/main/java/com/sunnyfarm/entity/Admin.java
Normal file
49
backend/src/main/java/com/sunnyfarm/entity/Admin.java
Normal file
@ -0,0 +1,49 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("admins")
|
||||
public class Admin extends BaseEntity {
|
||||
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 2, max = 50, message = "用户名长度必须在2-50个字符之间")
|
||||
private String username;
|
||||
|
||||
@JsonIgnore
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 128, message = "密码长度必须在6-128个字符之间")
|
||||
private String password;
|
||||
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Size(max = 100, message = "邮箱长度不能超过100个字符")
|
||||
private String email;
|
||||
|
||||
private String phone;
|
||||
|
||||
@Size(max = 50, message = "真实姓名不能超过50个字符")
|
||||
private String realName;
|
||||
|
||||
private Long roleId;
|
||||
|
||||
private Integer status; // 状态:0禁用,1正常
|
||||
|
||||
private LocalDateTime lastLoginTime;
|
||||
|
||||
// 非数据库字段
|
||||
@TableField(exist = false)
|
||||
private String token;
|
||||
|
||||
@TableField(exist = false)
|
||||
private Role role;
|
||||
}
|
||||
57
backend/src/main/java/com/sunnyfarm/entity/Announcement.java
Normal file
57
backend/src/main/java/com/sunnyfarm/entity/Announcement.java
Normal file
@ -0,0 +1,57 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("announcements")
|
||||
public class Announcement extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 类型:1系统公告,2活动公告,3维护公告
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 状态:0草稿,1发布
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 发布时间
|
||||
*/
|
||||
private LocalDateTime publishTime;
|
||||
|
||||
/**
|
||||
* 创建者ID
|
||||
*/
|
||||
private Long creatorId;
|
||||
|
||||
/**
|
||||
* 创建者名称
|
||||
*/
|
||||
private String creatorName;
|
||||
|
||||
// 类型常量
|
||||
public static final int TYPE_SYSTEM = 1;
|
||||
public static final int TYPE_ACTIVITY = 2;
|
||||
public static final int TYPE_MAINTENANCE = 3;
|
||||
|
||||
// 状态常量
|
||||
public static final int STATUS_DRAFT = 0;
|
||||
public static final int STATUS_PUBLISHED = 1;
|
||||
}
|
||||
23
backend/src/main/java/com/sunnyfarm/entity/BaseEntity.java
Normal file
23
backend/src/main/java/com/sunnyfarm/entity/BaseEntity.java
Normal file
@ -0,0 +1,23 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class BaseEntity {
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@TableField("created_at")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@TableField("updated_at")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
31
backend/src/main/java/com/sunnyfarm/entity/Cart.java
Normal file
31
backend/src/main/java/com/sunnyfarm/entity/Cart.java
Normal file
@ -0,0 +1,31 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("shopping_cart")
|
||||
public class Cart extends BaseEntity {
|
||||
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
private Long userId;
|
||||
|
||||
@NotNull(message = "产品ID不能为空")
|
||||
private Long productId;
|
||||
|
||||
@NotNull(message = "数量不能为空")
|
||||
@Min(value = 1, message = "数量必须大于0")
|
||||
private Integer quantity;
|
||||
|
||||
private Boolean selected = false;
|
||||
|
||||
// 非数据库字段
|
||||
@TableField(exist = false)
|
||||
private Product product;
|
||||
}
|
||||
76
backend/src/main/java/com/sunnyfarm/entity/Category.java
Normal file
76
backend/src/main/java/com/sunnyfarm/entity/Category.java
Normal file
@ -0,0 +1,76 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("categories")
|
||||
public class Category {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private String icon;
|
||||
|
||||
private Long parentId;
|
||||
|
||||
private Integer sortOrder;
|
||||
|
||||
private Integer status;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
// 子分类列表,用于构建树形结构
|
||||
@TableField(exist = false)
|
||||
private List<Category> children;
|
||||
|
||||
// 父分类名称
|
||||
@TableField(exist = false)
|
||||
private String parentName;
|
||||
|
||||
// 产品数量
|
||||
@TableField(exist = false)
|
||||
private Long productCount;
|
||||
|
||||
// 兼容旧方法名
|
||||
public LocalDateTime getCreateTime() {
|
||||
return this.createdAt;
|
||||
}
|
||||
|
||||
public void setCreateTime(LocalDateTime createTime) {
|
||||
this.createdAt = createTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdateTime() {
|
||||
return this.updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdateTime(LocalDateTime updateTime) {
|
||||
this.updatedAt = updateTime;
|
||||
}
|
||||
|
||||
public Integer getSort() {
|
||||
return this.sortOrder;
|
||||
}
|
||||
|
||||
public void setSort(Integer sort) {
|
||||
this.sortOrder = sort;
|
||||
}
|
||||
}
|
||||
47
backend/src/main/java/com/sunnyfarm/entity/ChatMessage.java
Normal file
47
backend/src/main/java/com/sunnyfarm/entity/ChatMessage.java
Normal file
@ -0,0 +1,47 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("chat_messages")
|
||||
public class ChatMessage {
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@TableField("session_id")
|
||||
private Long sessionId;
|
||||
|
||||
@TableField("sender_id")
|
||||
private Long senderId;
|
||||
|
||||
@TableField("sender_type")
|
||||
private Integer senderType; // 1用户,2商家
|
||||
|
||||
@TableField("message_type")
|
||||
private Integer messageType; // 1文本,2图片,3文件
|
||||
|
||||
@TableField("content")
|
||||
private String content;
|
||||
|
||||
@TableField("is_read")
|
||||
private Integer isRead; // 0未读,1已读
|
||||
|
||||
@TableField("created_at")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
// 非数据库字段
|
||||
@TableField(exist = false)
|
||||
private String senderName;
|
||||
|
||||
@TableField(exist = false)
|
||||
private String senderAvatar;
|
||||
}
|
||||
46
backend/src/main/java/com/sunnyfarm/entity/ChatSession.java
Normal file
46
backend/src/main/java/com/sunnyfarm/entity/ChatSession.java
Normal file
@ -0,0 +1,46 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("chat_sessions")
|
||||
public class ChatSession extends BaseEntity {
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@TableField("user_id")
|
||||
private Long userId;
|
||||
|
||||
@TableField("merchant_id")
|
||||
private Long merchantId;
|
||||
|
||||
@TableField("last_message")
|
||||
private String lastMessage;
|
||||
|
||||
@TableField("last_message_time")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime lastMessageTime;
|
||||
|
||||
@TableField("status")
|
||||
private Integer status; // 0关闭,1活跃
|
||||
|
||||
// 非数据库字段
|
||||
@TableField(exist = false)
|
||||
private String userName;
|
||||
|
||||
@TableField(exist = false)
|
||||
private String merchantName;
|
||||
|
||||
@TableField(exist = false)
|
||||
private Integer unreadCount;
|
||||
}
|
||||
50
backend/src/main/java/com/sunnyfarm/entity/Comment.java
Normal file
50
backend/src/main/java/com/sunnyfarm/entity/Comment.java
Normal file
@ -0,0 +1,50 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("reviews")
|
||||
public class Comment extends BaseEntity {
|
||||
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
private Long userId;
|
||||
@NotNull(message = "商家ID不能为空")
|
||||
private Long merchantId;
|
||||
|
||||
@NotNull(message = "订单ID不能为空")
|
||||
private Long orderId;
|
||||
|
||||
@NotNull(message = "产品ID不能为空")
|
||||
private Long productId;
|
||||
|
||||
@Size(max = 1000, message = "评论内容不能超过1000个字符")
|
||||
private String content;
|
||||
|
||||
private BigDecimal rating;
|
||||
|
||||
@TableField(exist = false)
|
||||
private String images;
|
||||
|
||||
private String reply;
|
||||
|
||||
private LocalDateTime replyTime;
|
||||
|
||||
private Integer status;
|
||||
|
||||
// 关联用户信息
|
||||
@TableField(exist = false)
|
||||
private User user;
|
||||
|
||||
// 关联产品信息
|
||||
@TableField(exist = false)
|
||||
private Product product;
|
||||
}
|
||||
22
backend/src/main/java/com/sunnyfarm/entity/Favorite.java
Normal file
22
backend/src/main/java/com/sunnyfarm/entity/Favorite.java
Normal file
@ -0,0 +1,22 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("favorites")
|
||||
public class Favorite extends BaseEntity {
|
||||
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
private Long userId;
|
||||
@NotNull(message = "产品ID不能为空")
|
||||
private Long productId;
|
||||
// 非数据库字段
|
||||
@TableField(exist = false)
|
||||
private Product product;
|
||||
}
|
||||
25
backend/src/main/java/com/sunnyfarm/entity/Inventory.java
Normal file
25
backend/src/main/java/com/sunnyfarm/entity/Inventory.java
Normal file
@ -0,0 +1,25 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("inventory")
|
||||
public class Inventory {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Long productId;
|
||||
|
||||
private Integer stockQuantity;
|
||||
|
||||
private Integer warningQuantity;
|
||||
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
43
backend/src/main/java/com/sunnyfarm/entity/Merchant.java
Normal file
43
backend/src/main/java/com/sunnyfarm/entity/Merchant.java
Normal file
@ -0,0 +1,43 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("merchants")
|
||||
public class Merchant extends BaseEntity {
|
||||
|
||||
private Long userId; // 关联用户ID
|
||||
|
||||
@NotBlank(message = "店铺名称不能为空")
|
||||
@Size(max = 100, message = "店铺名称不能超过100个字符")
|
||||
private String shopName;
|
||||
|
||||
private String businessLicense; // 营业执照
|
||||
|
||||
@Size(max = 50, message = "法人姓名不能超过50个字符")
|
||||
private String legalPerson;
|
||||
|
||||
private String contactPhone; // 联系电话
|
||||
|
||||
@Size(max = 500, message = "经营范围不能超过500个字符")
|
||||
private String businessScope;
|
||||
|
||||
@Size(max = 200, message = "经营地址不能超过200个字符")
|
||||
private String address;
|
||||
|
||||
private Integer status; // 状态:0待审核,1通过,2拒绝
|
||||
|
||||
private LocalDateTime verifyTime; // 审核时间
|
||||
|
||||
// 关联用户信息(非数据库字段)
|
||||
@TableField(exist = false)
|
||||
private User user;
|
||||
}
|
||||
91
backend/src/main/java/com/sunnyfarm/entity/Order.java
Normal file
91
backend/src/main/java/com/sunnyfarm/entity/Order.java
Normal file
@ -0,0 +1,91 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("orders")
|
||||
public class Order extends BaseEntity {
|
||||
|
||||
@NotBlank(message = "订单号不能为空")
|
||||
@Size(max = 50, message = "订单号长度不能超过50个字符")
|
||||
private String orderNo;
|
||||
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
private Long userId;
|
||||
|
||||
@NotNull(message = "商家ID不能为空")
|
||||
private Long merchantId;
|
||||
|
||||
@NotNull(message = "总金额不能为空")
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
private BigDecimal discountAmount;
|
||||
|
||||
@NotNull(message = "实付金额不能为空")
|
||||
private BigDecimal actualAmount;
|
||||
|
||||
@NotNull(message = "订单状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
@NotBlank(message = "收货人不能为空")
|
||||
@Size(max = 50, message = "收货人姓名长度不能超过50个字符")
|
||||
private String consignee;
|
||||
|
||||
@NotBlank(message = "收货人电话不能为空")
|
||||
@Size(max = 20, message = "收货人电话长度不能超过20个字符")
|
||||
private String phone;
|
||||
|
||||
@NotBlank(message = "收货地址不能为空")
|
||||
@Size(max = 200, message = "收货地址长度不能超过200个字符")
|
||||
private String address;
|
||||
|
||||
private String remark;
|
||||
|
||||
private LocalDateTime payTime;
|
||||
|
||||
private String payTransactionId;
|
||||
|
||||
private LocalDateTime shipTime;
|
||||
|
||||
private String shipCompany;
|
||||
|
||||
private String shipNo;
|
||||
|
||||
private LocalDateTime receiveTime;
|
||||
|
||||
private LocalDateTime finishTime;
|
||||
|
||||
private LocalDateTime cancelTime;
|
||||
|
||||
private String cancelReason;
|
||||
|
||||
private String refundReason;
|
||||
|
||||
private BigDecimal refundAmount;
|
||||
|
||||
private LocalDateTime refundTime;
|
||||
|
||||
// 非数据库字段
|
||||
@TableField(exist = false)
|
||||
private String statusName;
|
||||
|
||||
@TableField(exist = false)
|
||||
private List<OrderDetail> orderDetails;
|
||||
|
||||
@TableField(exist = false)
|
||||
private String userName;
|
||||
|
||||
@TableField(exist = false)
|
||||
private String merchantName;
|
||||
}
|
||||
58
backend/src/main/java/com/sunnyfarm/entity/OrderDetail.java
Normal file
58
backend/src/main/java/com/sunnyfarm/entity/OrderDetail.java
Normal file
@ -0,0 +1,58 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("order_items")
|
||||
public class OrderDetail {
|
||||
|
||||
private Long id;
|
||||
|
||||
@NotNull(message = "订单ID不能为空")
|
||||
private Long orderId;
|
||||
|
||||
@NotNull(message = "产品ID不能为空")
|
||||
private Long productId;
|
||||
// 商家ID(从products表关联获取)
|
||||
@TableField(exist = false)
|
||||
private Long merchantId;
|
||||
|
||||
|
||||
@NotNull(message = "数量不能为空")
|
||||
@Min(value = 1, message = "数量必须大于0")
|
||||
private Integer quantity;
|
||||
|
||||
@NotNull(message = "单价不能为空")
|
||||
private BigDecimal price;
|
||||
|
||||
@NotNull(message = "小计不能为空")
|
||||
@TableField("total_amount") // 映射到数据库的 total_amount 字段
|
||||
private BigDecimal subtotal;
|
||||
|
||||
// 冗余字段,避免产品信息变更影响历史订单
|
||||
private String productName;
|
||||
|
||||
private String productImage;
|
||||
|
||||
// 数据库中没有这些字段,标记为非数据库字段
|
||||
@TableField(exist = false)
|
||||
private String productSpecs;
|
||||
|
||||
@TableField(exist = false)
|
||||
private Product product;
|
||||
|
||||
// 时间字段
|
||||
@TableField("created_at")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// order_items表没有updated_at字段,标记为非数据库字段
|
||||
@TableField(exist = false)
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
45
backend/src/main/java/com/sunnyfarm/entity/Payment.java
Normal file
45
backend/src/main/java/com/sunnyfarm/entity/Payment.java
Normal file
@ -0,0 +1,45 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("payments")
|
||||
public class Payment extends BaseEntity {
|
||||
|
||||
@NotNull(message = "订单ID不能为空")
|
||||
private Long orderId;
|
||||
|
||||
@NotBlank(message = "支付单号不能为空")
|
||||
private String paymentNo;
|
||||
|
||||
@NotBlank(message = "支付方式不能为空")
|
||||
private String paymentMethod;
|
||||
|
||||
@NotNull(message = "支付金额不能为空")
|
||||
private BigDecimal amount;
|
||||
|
||||
@NotNull(message = "支付状态不能为空")
|
||||
private Integer status; // 1:待支付 2:已支付 3:支付失败 4:已退款
|
||||
|
||||
private String tradeNo; // 第三方交易号
|
||||
|
||||
private LocalDateTime paidAt;
|
||||
|
||||
// 支付宝相关字段
|
||||
private String alipayTradeNo; // 支付宝交易号
|
||||
private String buyerPayAmount; // 买家实付金额
|
||||
private String buyerLogonId; // 买家支付宝账号
|
||||
|
||||
// 退款相关
|
||||
private BigDecimal refundAmount;
|
||||
private LocalDateTime refundTime;
|
||||
private String refundReason;
|
||||
}
|
||||
32
backend/src/main/java/com/sunnyfarm/entity/Permission.java
Normal file
32
backend/src/main/java/com/sunnyfarm/entity/Permission.java
Normal file
@ -0,0 +1,32 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("permissions")
|
||||
public class Permission extends BaseEntity {
|
||||
|
||||
@NotBlank(message = "权限名称不能为空")
|
||||
@Size(max = 50, message = "权限名称不能超过50个字符")
|
||||
private String name;
|
||||
|
||||
@NotBlank(message = "权限代码不能为空")
|
||||
@Size(max = 50, message = "权限代码不能超过50个字符")
|
||||
private String code;
|
||||
|
||||
private Integer type; // 权限类型:1菜单,2按钮,3接口
|
||||
|
||||
private Long parentId; // 父权限ID
|
||||
|
||||
@Size(max = 100, message = "路径不能超过100个字符")
|
||||
private String path;
|
||||
|
||||
@Size(max = 200, message = "描述不能超过200个字符")
|
||||
private String description;
|
||||
}
|
||||
90
backend/src/main/java/com/sunnyfarm/entity/Product.java
Normal file
90
backend/src/main/java/com/sunnyfarm/entity/Product.java
Normal file
@ -0,0 +1,90 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.DecimalMin;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("products")
|
||||
public class Product extends BaseEntity {
|
||||
|
||||
@NotNull(message = "商家不能为空")
|
||||
private Long merchantId;
|
||||
|
||||
@NotNull(message = "分类不能为空")
|
||||
private Long categoryId;
|
||||
|
||||
@NotBlank(message = "产品名称不能为空")
|
||||
@Size(max = 200, message = "产品名称长度不能超过200个字符")
|
||||
private String name;
|
||||
|
||||
@Size(max = 1000, message = "产品描述长度不能超过1000个字符")
|
||||
private String description;
|
||||
|
||||
@NotNull(message = "价格不能为空")
|
||||
@DecimalMin(value = "0.01", message = "价格必须大于0")
|
||||
private BigDecimal price;
|
||||
|
||||
private BigDecimal originPrice;
|
||||
|
||||
private String origin;
|
||||
|
||||
private String unit;
|
||||
|
||||
private String weight;
|
||||
|
||||
private String shelfLife;
|
||||
|
||||
private String storageMethod;
|
||||
|
||||
private Integer status; // 0审核中,1上架,2下架
|
||||
|
||||
private Integer sortOrder;
|
||||
|
||||
private Integer salesCount;
|
||||
|
||||
private Integer viewCount;
|
||||
|
||||
// 非数据库字段 - 来自inventory表
|
||||
@TableField(exist = false)
|
||||
private Integer stock;
|
||||
|
||||
// 非数据库字段
|
||||
@TableField(exist = false)
|
||||
private String categoryName;
|
||||
|
||||
@TableField(exist = false)
|
||||
private List<String> imageList;
|
||||
|
||||
@TableField(exist = false)
|
||||
private Boolean collected;
|
||||
|
||||
@TableField(exist = false)
|
||||
private Integer cartCount;
|
||||
|
||||
// 兼容性方法
|
||||
public Long getSellerId() {
|
||||
return this.merchantId;
|
||||
}
|
||||
|
||||
public void setSellerId(Long sellerId) {
|
||||
this.merchantId = sellerId;
|
||||
}
|
||||
|
||||
public Integer getSales() {
|
||||
return this.salesCount;
|
||||
}
|
||||
|
||||
public void setSales(Integer sales) {
|
||||
this.salesCount = sales;
|
||||
}
|
||||
}
|
||||
24
backend/src/main/java/com/sunnyfarm/entity/ProductImage.java
Normal file
24
backend/src/main/java/com/sunnyfarm/entity/ProductImage.java
Normal file
@ -0,0 +1,24 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("product_images")
|
||||
public class ProductImage extends BaseEntity {
|
||||
|
||||
@NotNull(message = "商品ID不能为空")
|
||||
private Long productId;
|
||||
|
||||
@NotBlank(message = "图片URL不能为空")
|
||||
private String imageUrl;
|
||||
|
||||
private Boolean isMain = false; // 是否主图
|
||||
|
||||
private Integer sortOrder = 0; // 排序
|
||||
}
|
||||
33
backend/src/main/java/com/sunnyfarm/entity/Role.java
Normal file
33
backend/src/main/java/com/sunnyfarm/entity/Role.java
Normal file
@ -0,0 +1,33 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("roles")
|
||||
public class Role extends BaseEntity {
|
||||
|
||||
@NotBlank(message = "角色名称不能为空")
|
||||
@Size(max = 50, message = "角色名称不能超过50个字符")
|
||||
private String name;
|
||||
|
||||
@NotBlank(message = "角色代码不能为空")
|
||||
@Size(max = 50, message = "角色代码不能超过50个字符")
|
||||
private String code;
|
||||
|
||||
@Size(max = 200, message = "描述不能超过200个字符")
|
||||
private String description;
|
||||
|
||||
private Integer status; // 状态:0禁用,1启用
|
||||
|
||||
// 非数据库字段
|
||||
@TableField(exist = false)
|
||||
private List<Permission> permissions;
|
||||
}
|
||||
40
backend/src/main/java/com/sunnyfarm/entity/SystemConfig.java
Normal file
40
backend/src/main/java/com/sunnyfarm/entity/SystemConfig.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("system_configs")
|
||||
public class SystemConfig extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 配置键
|
||||
*/
|
||||
private String configKey;
|
||||
|
||||
/**
|
||||
* 配置值
|
||||
*/
|
||||
private String configValue;
|
||||
|
||||
/**
|
||||
* 配置类型
|
||||
*/
|
||||
private String configType;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
// 配置键常量
|
||||
public static final String SITE_NAME = "site_name";
|
||||
public static final String SITE_LOGO = "site_logo";
|
||||
public static final String SITE_DESCRIPTION = "site_description";
|
||||
public static final String CONTACT_PHONE = "contact_phone";
|
||||
public static final String CONTACT_EMAIL = "contact_email";
|
||||
public static final String AUTO_CONFIRM_DAYS = "auto_confirm_days";
|
||||
public static final String MAX_CART_ITEMS = "max_cart_items";
|
||||
}
|
||||
91
backend/src/main/java/com/sunnyfarm/entity/SystemLog.java
Normal file
91
backend/src/main/java/com/sunnyfarm/entity/SystemLog.java
Normal file
@ -0,0 +1,91 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("system_logs")
|
||||
public class SystemLog extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 操作类型
|
||||
*/
|
||||
private String operationType;
|
||||
|
||||
/**
|
||||
* 操作描述
|
||||
*/
|
||||
private String operationDesc;
|
||||
|
||||
/**
|
||||
* 操作者ID
|
||||
*/
|
||||
private Long operatorId;
|
||||
|
||||
/**
|
||||
* 操作者名称
|
||||
*/
|
||||
private String operatorName;
|
||||
|
||||
/**
|
||||
* 操作者类型:1普通用户,2商家,3管理员,4系统
|
||||
*/
|
||||
private Integer operatorType;
|
||||
|
||||
/**
|
||||
* 操作目标类型
|
||||
*/
|
||||
private String targetType;
|
||||
|
||||
/**
|
||||
* 操作目标ID
|
||||
*/
|
||||
private Long targetId;
|
||||
|
||||
/**
|
||||
* IP地址
|
||||
*/
|
||||
private String ipAddress;
|
||||
|
||||
/**
|
||||
* 用户代理
|
||||
*/
|
||||
private String userAgent;
|
||||
|
||||
/**
|
||||
* 操作结果:0失败,1成功
|
||||
*/
|
||||
private Integer result;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMsg;
|
||||
|
||||
// 操作类型常量
|
||||
public static final String USER_REGISTER = "user_register";
|
||||
public static final String USER_LOGIN = "user_login";
|
||||
public static final String MERCHANT_REGISTER = "merchant_register";
|
||||
public static final String MERCHANT_AUDIT = "merchant_audit";
|
||||
public static final String PRODUCT_CREATE = "product_create";
|
||||
public static final String PRODUCT_AUDIT = "product_audit";
|
||||
public static final String ORDER_CREATE = "order_create";
|
||||
public static final String ORDER_AUDIT = "order_audit";
|
||||
public static final String ADMIN_LOGIN = "admin_login";
|
||||
public static final String SYSTEM_CONFIG = "system_config";
|
||||
public static final String ANNOUNCEMENT_CREATE = "announcement_create";
|
||||
public static final String ANNOUNCEMENT_UPDATE = "announcement_update";
|
||||
public static final String ANNOUNCEMENT_DELETE = "announcement_delete";
|
||||
|
||||
// 操作者类型常量
|
||||
public static final int OPERATOR_USER = 1;
|
||||
public static final int OPERATOR_MERCHANT = 2;
|
||||
public static final int OPERATOR_ADMIN = 3;
|
||||
public static final int OPERATOR_SYSTEM = 4;
|
||||
|
||||
// 操作结果常量
|
||||
public static final int RESULT_FAIL = 0;
|
||||
public static final int RESULT_SUCCESS = 1;
|
||||
}
|
||||
62
backend/src/main/java/com/sunnyfarm/entity/User.java
Normal file
62
backend/src/main/java/com/sunnyfarm/entity/User.java
Normal file
@ -0,0 +1,62 @@
|
||||
package com.sunnyfarm.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("users")
|
||||
public class User extends BaseEntity {
|
||||
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 2, max = 50, message = "用户名长度必须在2-50个字符之间")
|
||||
private String username;
|
||||
|
||||
@JsonIgnore
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 100, message = "密码长度必须在6-100个字符之间")
|
||||
private String password;
|
||||
|
||||
@Size(max = 50, message = "昵称长度不能超过50个字符")
|
||||
private String nickname;
|
||||
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Size(max = 100, message = "邮箱长度不能超过100个字符")
|
||||
private String email;
|
||||
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
private String phone;
|
||||
|
||||
private String avatar;
|
||||
|
||||
private Integer gender; // 0未知,1男,2女
|
||||
|
||||
private LocalDate birthday;
|
||||
|
||||
private String realName; // 真实姓名
|
||||
|
||||
private String idCard; // 身份证号
|
||||
|
||||
private Boolean isVerified; // 是否实名认证
|
||||
|
||||
private Integer status; // 0禁用,1正常
|
||||
|
||||
private LocalDateTime lastLoginTime;
|
||||
|
||||
// 非数据库字段
|
||||
@TableField(exist = false)
|
||||
private String token;
|
||||
|
||||
@TableField(exist = false)
|
||||
private String confirmPassword;
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.sunnyfarm.exception;
|
||||
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
private Integer code;
|
||||
|
||||
public BusinessException(String message) {
|
||||
super(message);
|
||||
this.code = 500;
|
||||
}
|
||||
|
||||
public BusinessException(Integer code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
package com.sunnyfarm.exception;
|
||||
|
||||
import com.sunnyfarm.common.Result;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
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.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 处理业务异常
|
||||
*/
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Result<String> handleBusinessException(BusinessException e) {
|
||||
log.error("业务异常: {}", e.getMessage());
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数校验异常
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Result<String> handleValidException(MethodArgumentNotValidException e) {
|
||||
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (FieldError error : fieldErrors) {
|
||||
sb.append(error.getField()).append(": ").append(error.getDefaultMessage()).append("; ");
|
||||
}
|
||||
log.error("参数校验异常: {}", sb.toString());
|
||||
return Result.error("参数校验失败: " + sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理绑定异常
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Result<String> handleBindException(BindException e) {
|
||||
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (FieldError error : fieldErrors) {
|
||||
sb.append(error.getField()).append(": ").append(error.getDefaultMessage()).append("; ");
|
||||
}
|
||||
log.error("绑定异常: {}", sb.toString());
|
||||
return Result.error("参数绑定失败: " + sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理约束违反异常
|
||||
*/
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Result<String> handleConstraintViolationException(ConstraintViolationException e) {
|
||||
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (ConstraintViolation<?> violation : violations) {
|
||||
sb.append(violation.getMessage()).append("; ");
|
||||
}
|
||||
log.error("约束违反异常: {}", sb.toString());
|
||||
return Result.error("参数约束违反: " + sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理运行时异常
|
||||
*/
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result<String> handleRuntimeException(RuntimeException e) {
|
||||
log.error("运行时异常", e);
|
||||
return Result.error("系统异常,请稍后重试");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理其他异常
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result<String> handleException(Exception e) {
|
||||
log.error("系统异常", e);
|
||||
return Result.error("系统异常,请稍后重试");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package com.sunnyfarm.interceptor;
|
||||
|
||||
import com.sunnyfarm.util.JwtUtil;
|
||||
import com.sunnyfarm.util.UserContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class JwtAuthInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
String requestURI = request.getRequestURI();
|
||||
String method = request.getMethod();
|
||||
|
||||
log.debug("拦截器处理请求: {} {}", method, requestURI);
|
||||
|
||||
// 处理OPTIONS预检请求
|
||||
if ("OPTIONS".equals(method)) {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取token
|
||||
String token = getTokenFromRequest(request);
|
||||
|
||||
if (!StringUtils.hasText(token)) {
|
||||
log.warn("请求 {} 缺少Authorization token", requestURI);
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().write("{\"code\": 401, \"message\": \"未登录或登录已过期\"}");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 验证token
|
||||
String username = jwtUtil.getUsernameFromToken(token);
|
||||
if (username == null || jwtUtil.isTokenExpired(token)) {
|
||||
log.warn("Token验证失败或已过期: {}", requestURI);
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().write("{\"code\": 401, \"message\": \"登录已过期,请重新登录\"}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取用户信息并存储到上下文
|
||||
String userType = jwtUtil.getUserTypeFromToken(token);
|
||||
|
||||
if ("admin".equals(userType)) {
|
||||
Long adminId = jwtUtil.getAdminIdFromToken(token);
|
||||
UserContext.setCurrentUserId(adminId);
|
||||
UserContext.setCurrentUsername(username);
|
||||
UserContext.setUserType("admin");
|
||||
} else if ("merchant".equals(userType)) {
|
||||
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||
Long merchantId = jwtUtil.getMerchantIdFromToken(token);
|
||||
UserContext.setCurrentUserId(userId);
|
||||
UserContext.setCurrentUsername(username);
|
||||
UserContext.setUserType("merchant");
|
||||
UserContext.setMerchantId(merchantId);
|
||||
} else {
|
||||
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||
UserContext.setCurrentUserId(userId);
|
||||
UserContext.setCurrentUsername(username);
|
||||
UserContext.setUserType("user");
|
||||
}
|
||||
|
||||
log.debug("用户认证成功: {} ({})", username, userType);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("JWT验证失败: " + requestURI, e);
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().write("{\"code\": 401, \"message\": \"Token验证失败\"}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
// 清除用户上下文
|
||||
UserContext.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中获取token
|
||||
*/
|
||||
private String getTokenFromRequest(HttpServletRequest request) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {
|
||||
return authHeader.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.entity.Address;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface AddressMapper extends BaseMapper<Address> {
|
||||
|
||||
@Select("SELECT * FROM addresses WHERE user_id = #{userId} AND deleted = 0 ORDER BY is_default DESC, create_time DESC")
|
||||
List<Address> findByUserId(@Param("userId") Long userId);
|
||||
|
||||
@Select("SELECT * FROM addresses WHERE user_id = #{userId} AND is_default = 1 AND deleted = 0")
|
||||
Address findDefaultByUserId(@Param("userId") Long userId);
|
||||
|
||||
@Update("UPDATE addresses SET is_default = 0 WHERE user_id = #{userId}")
|
||||
int clearDefaultByUserId(@Param("userId") Long userId);
|
||||
|
||||
@Select("SELECT COUNT(*) FROM addresses WHERE user_id = #{userId} AND deleted = 0")
|
||||
Long countByUser(@Param("userId") Long userId);
|
||||
}
|
||||
22
backend/src/main/java/com/sunnyfarm/mapper/AdminMapper.java
Normal file
22
backend/src/main/java/com/sunnyfarm/mapper/AdminMapper.java
Normal file
@ -0,0 +1,22 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.entity.Admin;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
@Mapper
|
||||
public interface AdminMapper extends BaseMapper<Admin> {
|
||||
|
||||
/**
|
||||
* 根据用户名查询管理员
|
||||
*/
|
||||
@Select("SELECT * FROM admins WHERE username = #{username}")
|
||||
Admin selectByUsername(@Param("username") String username);
|
||||
|
||||
/**
|
||||
* 查询管理员详情(包含角色信息)
|
||||
*/
|
||||
Admin selectAdminWithRole(@Param("id") Long id);
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.entity.Announcement;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface AnnouncementMapper extends BaseMapper<Announcement> {
|
||||
}
|
||||
18
backend/src/main/java/com/sunnyfarm/mapper/CartMapper.java
Normal file
18
backend/src/main/java/com/sunnyfarm/mapper/CartMapper.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.dto.CartItemDTO;
|
||||
import com.sunnyfarm.entity.Cart;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface CartMapper extends BaseMapper<Cart> {
|
||||
|
||||
/**
|
||||
* 获取用户购物车详情(包含商品和商家信息)
|
||||
*/
|
||||
List<CartItemDTO> getUserCartItems(@Param("userId") Long userId);
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.entity.Category;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface CategoryMapper extends BaseMapper<Category> {
|
||||
|
||||
@Select("SELECT * FROM categories WHERE parent_id = #{parentId} AND deleted = 0 ORDER BY sort ASC")
|
||||
List<Category> findByParentId(@Param("parentId") Long parentId);
|
||||
|
||||
@Select("SELECT * FROM categories WHERE status = 1 AND deleted = 0 ORDER BY sort ASC")
|
||||
List<Category> findActiveCategories();
|
||||
|
||||
@Select("SELECT COUNT(*) FROM categories WHERE deleted = 0")
|
||||
Long countCategories();
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.entity.ChatMessage;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ChatMessageMapper extends BaseMapper<ChatMessage> {
|
||||
|
||||
/**
|
||||
* 获取会话消息列表
|
||||
*/
|
||||
List<ChatMessage> getSessionMessages(@Param("sessionId") Long sessionId,
|
||||
@Param("offset") Integer offset,
|
||||
@Param("limit") Integer limit);
|
||||
|
||||
/**
|
||||
* 标记消息为已读
|
||||
*/
|
||||
int markMessagesAsRead(@Param("sessionId") Long sessionId, @Param("readerId") Long readerId);
|
||||
|
||||
/**
|
||||
* 根据发送者类型标记消息为已读
|
||||
*/
|
||||
int markMessagesByTypeAsRead(@Param("sessionId") Long sessionId, @Param("senderType") Integer senderType);
|
||||
|
||||
/**
|
||||
* 获取未读消息数量
|
||||
*/
|
||||
Integer getUnreadCount(@Param("sessionId") Long sessionId, @Param("receiverId") Long receiverId);
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.entity.ChatSession;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ChatSessionMapper extends BaseMapper<ChatSession> {
|
||||
|
||||
/**
|
||||
* 获取用户的聊天会话列表
|
||||
*/
|
||||
List<ChatSession> getUserSessions(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 获取商家的聊天会话列表
|
||||
*/
|
||||
List<ChatSession> getMerchantSessions(@Param("merchantId") Long merchantId);
|
||||
|
||||
/**
|
||||
* 获取或创建聊天会话
|
||||
*/
|
||||
ChatSession getOrCreateSession(@Param("userId") Long userId, @Param("merchantId") Long merchantId);
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.entity.Comment;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Mapper
|
||||
public interface CommentMapper extends BaseMapper<Comment> {
|
||||
|
||||
@Select("SELECT c.*, u.nickname as user_name, u.avatar as user_avatar, " +
|
||||
"p.name as product_name " +
|
||||
"FROM reviews c " +
|
||||
"LEFT JOIN users u ON c.user_id = u.id " +
|
||||
"LEFT JOIN products p ON c.product_id = p.id " +
|
||||
"WHERE c.product_id = #{productId} AND c.status = 1 " +
|
||||
"ORDER BY c.created_at DESC")
|
||||
IPage<Comment> findCommentsByProduct(Page<?> page, @Param("productId") Long productId);
|
||||
|
||||
@Select("SELECT c.*, u.nickname as user_name, u.avatar as user_avatar, " +
|
||||
"p.name as product_name " +
|
||||
"FROM reviews c " +
|
||||
"LEFT JOIN users u ON c.user_id = u.id " +
|
||||
"LEFT JOIN products p ON c.product_id = p.id " +
|
||||
"WHERE c.user_id = #{userId} " +
|
||||
"ORDER BY c.created_at DESC")
|
||||
IPage<Comment> findCommentsByUser(Page<?> page, @Param("userId") Long userId);
|
||||
|
||||
@Select("SELECT AVG(rating) FROM reviews WHERE product_id = #{productId} AND status = 1 AND deleted = 0")
|
||||
BigDecimal getAverageRating(@Param("productId") Long productId);
|
||||
|
||||
@Select("SELECT COUNT(*) FROM reviews WHERE product_id = #{productId} AND status = 1 AND deleted = 0")
|
||||
Long countByProduct(@Param("productId") Long productId);
|
||||
|
||||
@Select("SELECT COUNT(*) FROM reviews WHERE user_id = #{userId} AND deleted = 0")
|
||||
Long countByUser(@Param("userId") Long userId);
|
||||
|
||||
@Select("SELECT * FROM reviews WHERE user_id = #{userId} AND product_id = #{productId} AND order_id = #{orderId} AND deleted = 0")
|
||||
Comment findByUserAndProductAndOrder(@Param("userId") Long userId, @Param("productId") Long productId, @Param("orderId") Long orderId);
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.entity.Favorite;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
@Mapper
|
||||
public interface FavoriteMapper extends BaseMapper<Favorite> {
|
||||
|
||||
/**
|
||||
* 查询用户收藏列表(包含商品信息)
|
||||
*/
|
||||
IPage<Favorite> getUserFavoritesWithProduct(Page<Favorite> page, @Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 检查用户是否收藏了某商品
|
||||
*/
|
||||
Integer checkUserFavorite(@Param("userId") Long userId, @Param("productId") Long productId);
|
||||
|
||||
@Select("SELECT f.*, p.name as product_name, p.price, p.main_image " +
|
||||
"FROM favorites f " +
|
||||
"LEFT JOIN products p ON f.product_id = p.id " +
|
||||
"WHERE f.user_id = #{userId} AND f.deleted = 0 " +
|
||||
"ORDER BY f.create_time DESC")
|
||||
IPage<Favorite> findFavoritesByUser(Page<?> page, @Param("userId") Long userId);
|
||||
|
||||
@Select("SELECT * FROM favorites WHERE user_id = #{userId} AND product_id = #{productId} AND deleted = 0")
|
||||
Favorite findByUserAndProduct(@Param("userId") Long userId, @Param("productId") Long productId);
|
||||
|
||||
@Select("SELECT COUNT(*) FROM favorites WHERE user_id = #{userId} AND deleted = 0")
|
||||
Long countByUser(@Param("userId") Long userId);
|
||||
|
||||
@Select("SELECT COUNT(*) FROM favorites WHERE product_id = #{productId} AND deleted = 0")
|
||||
Long countByProduct(@Param("productId") Long productId);
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.dto.InventoryWithProduct;
|
||||
import com.sunnyfarm.entity.Inventory;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface InventoryMapper extends BaseMapper<Inventory> {
|
||||
|
||||
/**
|
||||
* 获取商家库存分页数据(包含商品信息)
|
||||
*/
|
||||
IPage<InventoryWithProduct> getMerchantInventoryPage(Page<InventoryWithProduct> page,
|
||||
@Param("merchantId") Long merchantId,
|
||||
@Param("keyword") String keyword);
|
||||
|
||||
/**
|
||||
* 获取商家库存列表(包含商品信息)
|
||||
*/
|
||||
List<InventoryWithProduct> getMerchantInventoryList(@Param("merchantId") Long merchantId);
|
||||
|
||||
/**
|
||||
* 获取商家库存列表(不分页)
|
||||
*/
|
||||
/**
|
||||
* 获取库存不足商品
|
||||
*/
|
||||
List<InventoryWithProduct> getLowStockProducts(@Param("merchantId") Long merchantId);
|
||||
|
||||
/**
|
||||
* 获取缺货商品
|
||||
*/
|
||||
List<InventoryWithProduct> getOutOfStockProducts(@Param("merchantId") Long merchantId);
|
||||
|
||||
/**
|
||||
* 按商品ID删除库存
|
||||
*/
|
||||
int deleteByProductId(@Param("productId") Long productId);
|
||||
|
||||
/**
|
||||
* 批量插入库存记录
|
||||
*/
|
||||
int batchInsert(@Param("list") List<Inventory> inventoryList);
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.dto.MerchantDashboardDTO;
|
||||
import com.sunnyfarm.entity.Merchant;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface MerchantMapper extends BaseMapper<Merchant> {
|
||||
|
||||
/**
|
||||
* 分页查询商家列表(包含用户信息)
|
||||
*/
|
||||
IPage<Merchant> selectMerchantPage(Page<Merchant> page,
|
||||
@Param("keyword") String keyword,
|
||||
@Param("status") Integer status);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询商家信息(包含用户信息)
|
||||
*/
|
||||
Merchant selectByUserId(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 查询待审核商家数量
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM merchants WHERE status = 0")
|
||||
Long countPendingMerchants();
|
||||
|
||||
// Dashboard统计查询方法
|
||||
Long getTotalOrdersByMerchant(@Param("merchantId") Long merchantId);
|
||||
Long getTodayOrdersByMerchant(@Param("merchantId") Long merchantId);
|
||||
BigDecimal getTotalRevenueByMerchant(@Param("merchantId") Long merchantId);
|
||||
BigDecimal getTodayRevenueByMerchant(@Param("merchantId") Long merchantId);
|
||||
Long getTotalProductsByMerchant(@Param("merchantId") Long merchantId);
|
||||
Long getActiveProductsByMerchant(@Param("merchantId") Long merchantId);
|
||||
Long getTotalCustomersByMerchant(@Param("merchantId") Long merchantId);
|
||||
|
||||
List<MerchantDashboardDTO.RecentOrderDTO> getRecentOrdersByMerchant(@Param("merchantId") Long merchantId);
|
||||
List<MerchantDashboardDTO.SalesTrendDTO> getSalesTrendByMerchant(@Param("merchantId") Long merchantId);
|
||||
List<MerchantDashboardDTO.ProductSalesDTO> getTopProductsByMerchant(@Param("merchantId") Long merchantId);
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.entity.OrderDetail;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface OrderDetailMapper extends BaseMapper<OrderDetail> {
|
||||
/**
|
||||
* 获取订单详情(包含商家ID)
|
||||
*/
|
||||
List<OrderDetail> getOrderDetailsByOrderId(@Param("orderId") Long orderId);
|
||||
|
||||
|
||||
@Select("SELECT od.*, p.name as product_name, p.main_image as product_image " +
|
||||
"FROM order_details od " +
|
||||
"LEFT JOIN products p ON od.product_id = p.id " +
|
||||
"WHERE od.order_id = #{orderId} AND od.deleted = 0")
|
||||
List<OrderDetail> findByOrderId(@Param("orderId") Long orderId);
|
||||
|
||||
@Select("SELECT SUM(quantity) FROM order_details WHERE product_id = #{productId} AND deleted = 0")
|
||||
Long getTotalSalesByProduct(@Param("productId") Long productId);
|
||||
}
|
||||
55
backend/src/main/java/com/sunnyfarm/mapper/OrderMapper.java
Normal file
55
backend/src/main/java/com/sunnyfarm/mapper/OrderMapper.java
Normal file
@ -0,0 +1,55 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.entity.Order;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface OrderMapper extends BaseMapper<Order> {
|
||||
|
||||
@Select("SELECT o.*, u.nickname as user_name " +
|
||||
"FROM orders o " +
|
||||
"LEFT JOIN users u ON o.user_id = u.id " +
|
||||
"WHERE o.id = #{id} AND o.deleted = 0")
|
||||
Order findOrderWithUser(@Param("id") Long id);
|
||||
|
||||
@Select("SELECT o.*, u.nickname as user_name " +
|
||||
"FROM orders o " +
|
||||
"LEFT JOIN users u ON o.user_id = u.id " +
|
||||
"WHERE o.user_id = #{userId} AND o.deleted = 0 " +
|
||||
"ORDER BY o.create_time DESC")
|
||||
IPage<Order> findOrdersByUser(Page<?> page, @Param("userId") Long userId);
|
||||
|
||||
@Select("SELECT o.*, u.nickname as user_name " +
|
||||
"FROM orders o " +
|
||||
"LEFT JOIN users u ON o.user_id = u.id " +
|
||||
"WHERE o.user_id = #{userId} AND o.status = #{status} AND o.deleted = 0 " +
|
||||
"ORDER BY o.create_time DESC")
|
||||
IPage<Order> findOrdersByUserAndStatus(Page<?> page, @Param("userId") Long userId, @Param("status") Integer status);
|
||||
|
||||
@Select("SELECT * FROM orders WHERE order_no = #{orderNo} AND deleted = 0")
|
||||
Order findByOrderNo(@Param("orderNo") String orderNo);
|
||||
|
||||
@Select("SELECT SUM(actual_amount) FROM orders WHERE status = 2 AND deleted = 0")
|
||||
BigDecimal getTotalRevenue();
|
||||
|
||||
@Select("SELECT COUNT(*) FROM orders WHERE deleted = 0")
|
||||
Long countOrders();
|
||||
|
||||
@Select("SELECT COUNT(*) FROM orders WHERE status = #{status} AND deleted = 0")
|
||||
Long countOrdersByStatus(@Param("status") Integer status);
|
||||
|
||||
@Select("SELECT COUNT(*) FROM orders WHERE user_id = #{userId} AND deleted = 0")
|
||||
Long countOrdersByUser(@Param("userId") Long userId);
|
||||
|
||||
@Select("SELECT COUNT(*) FROM orders WHERE pay_time >= #{startTime} AND pay_time <= #{endTime} AND deleted = 0")
|
||||
Long countOrdersByTimeRange(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime);
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.entity.Payment;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface PaymentMapper extends BaseMapper<Payment> {
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.entity.Permission;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface PermissionMapper extends BaseMapper<Permission> {
|
||||
|
||||
/**
|
||||
* 根据角色ID查询权限列表
|
||||
*/
|
||||
List<Permission> selectPermissionsByRoleId(@Param("roleId") Long roleId);
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.entity.ProductImage;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Delete;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ProductImageMapper extends BaseMapper<ProductImage> {
|
||||
|
||||
/**
|
||||
* 根据商品ID获取图片列表
|
||||
*/
|
||||
@Select("SELECT * FROM product_images WHERE product_id = #{productId} ORDER BY sort_order ASC, id ASC")
|
||||
List<ProductImage> getImagesByProductId(@Param("productId") Long productId);
|
||||
|
||||
/**
|
||||
* 根据商品ID删除所有图片
|
||||
*/
|
||||
@Delete("DELETE FROM product_images WHERE product_id = #{productId}")
|
||||
int deleteByProductId(@Param("productId") Long productId);
|
||||
|
||||
/**
|
||||
* 获取商品主图
|
||||
*/
|
||||
@Select("SELECT * FROM product_images WHERE product_id = #{productId} AND is_main = 1 LIMIT 1")
|
||||
ProductImage getMainImageByProductId(@Param("productId") Long productId);
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunnyfarm.entity.Product;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ProductMapper extends BaseMapper<Product> {
|
||||
|
||||
@Select("SELECT p.*, c.name as category_name, u.nickname as seller_name " +
|
||||
"FROM products p " +
|
||||
"LEFT JOIN categories c ON p.category_id = c.id " +
|
||||
"LEFT JOIN users u ON p.seller_id = u.id " +
|
||||
"WHERE p.id = #{id} AND p.deleted = 0")
|
||||
Product findProductWithDetails(@Param("id") Long id);
|
||||
|
||||
@Select("SELECT p.*, c.name as category_name, u.nickname as seller_name " +
|
||||
"FROM products p " +
|
||||
"LEFT JOIN categories c ON p.category_id = c.id " +
|
||||
"LEFT JOIN users u ON p.seller_id = u.id " +
|
||||
"WHERE p.status = 1 AND p.deleted = 0 " +
|
||||
"ORDER BY p.sort ASC, p.create_time DESC")
|
||||
IPage<Product> findActiveProducts(Page<?> page);
|
||||
|
||||
@Select("SELECT p.*, c.name as category_name, u.nickname as seller_name " +
|
||||
"FROM products p " +
|
||||
"LEFT JOIN categories c ON p.category_id = c.id " +
|
||||
"LEFT JOIN users u ON p.seller_id = u.id " +
|
||||
"WHERE p.category_id = #{categoryId} AND p.status = 1 AND p.deleted = 0 " +
|
||||
"ORDER BY p.sort ASC, p.create_time DESC")
|
||||
IPage<Product> findProductsByCategory(Page<?> page, @Param("categoryId") Long categoryId);
|
||||
|
||||
@Select("SELECT * FROM products WHERE recommended = 1 AND status = 1 AND deleted = 0 " +
|
||||
"ORDER BY sort ASC, create_time DESC LIMIT #{limit}")
|
||||
List<Product> findRecommendedProducts(@Param("limit") Integer limit);
|
||||
|
||||
@Update("UPDATE products SET sales = sales + #{quantity} WHERE id = #{productId}")
|
||||
int updateSales(@Param("productId") Long productId, @Param("quantity") Integer quantity);
|
||||
|
||||
@Update("UPDATE products SET stock = stock - #{quantity} WHERE id = #{productId}")
|
||||
int updateStock(@Param("productId") Long productId, @Param("quantity") Integer quantity);
|
||||
|
||||
@Select("SELECT COUNT(*) FROM products WHERE deleted = 0")
|
||||
Long countProducts();
|
||||
}
|
||||
15
backend/src/main/java/com/sunnyfarm/mapper/RoleMapper.java
Normal file
15
backend/src/main/java/com/sunnyfarm/mapper/RoleMapper.java
Normal file
@ -0,0 +1,15 @@
|
||||
package com.sunnyfarm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunnyfarm.entity.Role;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@Mapper
|
||||
public interface RoleMapper extends BaseMapper<Role> {
|
||||
|
||||
/**
|
||||
* 根据角色ID查询角色及权限
|
||||
*/
|
||||
Role selectRoleWithPermissions(@Param("roleId") Long roleId);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user