feat: major update - MySQL, chat, wishlist, PWA, admin overhaul

This commit is contained in:
2026-03-21 20:22:00 +08:00
committed by 树萌芽
parent 48fb818b8c
commit 84874707f5
71 changed files with 13457 additions and 2031 deletions

View File

@@ -0,0 +1,256 @@
# 萌芽小店 · 后端
基于 **Go + Gin + GORM** 构建的 RESTful API 服务,负责商品管理、订单处理、用户认证、聊天消息等核心业务。
## 技术依赖
| 包 | 版本 | 用途 |
|----|------|------|
| gin | v1.9 | HTTP 路由框架 |
| gorm | v1.31 | ORM |
| gorm/driver/mysql | v1.6 | MySQL 驱动 |
| go-sql-driver/mysql | v1.9 | 底层 MySQL 连接 |
| gin-contrib/cors | latest | CORS 中间件 |
| google/uuid | latest | UUID 生成 |
## 目录结构
```
mengyastore-backend/
├── main.go # 程序入口,路由注册
├── cmd/
│ └── migrate/
│ └── main.go # 一次性 JSON→MySQL 数据迁移脚本
├── data/
│ └── json/
│ └── settings.json # 服务配置adminToken、DSN 等)
├── internal/
│ ├── config/
│ │ └── config.go # 配置加载
│ ├── database/
│ │ ├── db.go # GORM 初始化 + AutoMigrate
│ │ └── models.go # 数据库行结构体GORM 模型)
│ ├── models/
│ │ ├── product.go # 业务模型 Product
│ │ ├── order.go # 业务模型 Order
│ │ └── chat.go # 业务模型 ChatMessage
│ ├── storage/
│ │ ├── jsonstore.go # 商品存储GORM 实现)
│ │ ├── orderstore.go # 订单存储
│ │ ├── sitestore.go # 站点设置存储
│ │ ├── wishliststore.go # 收藏夹存储
│ │ └── chatstore.go # 聊天消息存储(含内存级频率限制)
│ ├── handlers/
│ │ ├── admin.go # AdminHandler 结构体 + requireAdmin
│ │ ├── admin_product.go # 商品 CRUD 接口
│ │ ├── admin_site.go # 维护模式接口
│ │ ├── admin_orders.go # 订单管理接口
│ │ ├── admin_chat.go # 管理员聊天接口
│ │ ├── public.go # 公开接口(商品列表、浏览量)
│ │ ├── order.go # 下单、确认订单接口
│ │ ├── stats.go # 统计信息接口
│ │ ├── wishlist.go # 收藏夹接口(用户)
│ │ └── chat.go # 聊天接口(用户)
│ └── auth/
│ └── sproutgate.go # SproutGate OAuth 客户端
```
## API 路由一览
### 公开接口
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/health` | 健康检查 |
| GET | `/api/products` | 获取商品列表(仅 active |
| POST | `/api/products/:id/view` | 记录商品浏览量 |
| GET | `/api/stats` | 获取总订单数和总访问量 |
| POST | `/api/site/visit` | 记录站点访问 |
| GET | `/api/site/maintenance` | 获取维护状态 |
| POST | `/api/checkout` | 创建订单(生成支付二维码) |
| GET | `/api/orders` | 获取当前用户订单(需 Bearer token |
| POST | `/api/orders/:id/confirm` | 确认付款(触发发货) |
### 收藏夹(需登录)
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/wishlist` | 获取收藏商品 ID 列表 |
| POST | `/api/wishlist` | 添加收藏 |
| DELETE | `/api/wishlist/:id` | 取消收藏 |
### 聊天(需登录)
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/chat/messages` | 获取自己的聊天记录 |
| POST | `/api/chat/messages` | 发送消息1 秒频率限制) |
### 管理员接口(需 `?token=xxx` 或 `Authorization: <token>`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/admin/token` | 获取令牌(用于验证) |
| GET | `/api/admin/products` | 获取全部商品(含卡密) |
| POST | `/api/admin/products` | 创建商品 |
| PUT | `/api/admin/products/:id` | 编辑商品 |
| PATCH | `/api/admin/products/:id/status` | 切换上下架 |
| DELETE | `/api/admin/products/:id` | 删除商品 |
| POST | `/api/admin/site/maintenance` | 设置维护模式 |
| GET | `/api/admin/orders` | 获取全部订单 |
| DELETE | `/api/admin/orders/:id` | 删除订单 |
| GET | `/api/admin/chat` | 获取全部用户对话 |
| GET | `/api/admin/chat/:account` | 获取指定用户对话 |
| POST | `/api/admin/chat/:account` | 管理员回复 |
| DELETE | `/api/admin/chat/:account` | 清除对话 |
## 数据库表结构
### products
| 字段 | 类型 | 说明 |
|------|------|------|
| id | varchar(36) | UUID 主键 |
| name | varchar(255) | 商品名称 |
| price | double | 原价 |
| discount_price | double | 折扣价0 = 无折扣)|
| tags | json | 标签数组 |
| cover_url | varchar(500) | 封面图 URL |
| screenshot_urls | json | 截图 URL 数组(最多 5 张)|
| verification_url | varchar(500) | 验证链接 |
| description | text | Markdown 描述 |
| active | tinyint(1) | 是否上架 |
| require_login | tinyint(1) | 是否必须登录购买 |
| max_per_account | bigint | 每账户最大购买数0=不限)|
| total_sold | bigint | 累计销量 |
| view_count | bigint | 累计浏览量 |
| delivery_mode | varchar(20) | 发货模式:`auto` / `manual` |
| show_note | tinyint(1) | 下单时显示备注输入框 |
| show_contact | tinyint(1) | 下单时显示联系方式输入框 |
| created_at | datetime(3) | 创建时间 |
### product_codes
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint unsigned | 自增主键 |
| product_id | varchar(36) | 关联商品 ID索引|
| code | text | 卡密内容 |
### orders
| 字段 | 类型 | 说明 |
|------|------|------|
| id | varchar(36) | UUID 主键 |
| product_id | varchar(36) | 商品 ID索引|
| product_name | varchar(255) | 商品名称快照 |
| user_account | varchar(255) | 用户账号(可空,匿名)|
| user_name | varchar(255) | 用户昵称 |
| quantity | bigint | 购买数量 |
| delivered_codes | json | 已发放卡密 |
| status | varchar(20) | `pending` / `completed` |
| delivery_mode | varchar(20) | `auto` / `manual` |
| note | text | 用户备注 |
| contact_phone | varchar(50) | 联系手机号 |
| contact_email | varchar(255) | 联系邮箱 |
| created_at | datetime(3) | 下单时间 |
### site_settings
键值对存储,当前使用的键:
| Key | 说明 |
|-----|------|
| `totalVisits` | 总访问量 |
| `maintenance` | 维护模式(`true` / `false`|
| `maintenanceReason` | 维护原因文本 |
### wishlists
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint unsigned | 自增主键 |
| account_id | varchar(255) | 用户账号(唯一索引)|
| product_id | varchar(36) | 商品 ID联合唯一|
### chat_messages
| 字段 | 类型 | 说明 |
|------|------|------|
| id | varchar(36) | UUID 主键 |
| account_id | varchar(255) | 用户账号(索引)|
| account_name | varchar(255) | 用户昵称 |
| content | text | 消息内容 |
| sent_at | datetime(3) | 发送时间 |
| from_admin | tinyint(1) | 是否来自管理员 |
## 配置文件
`data/json/settings.json`
```json
{
"adminToken": "你的管理员令牌",
"authApiUrl": "https://auth.api.shumengya.top",
"databaseDsn": ""
}
```
`databaseDsn` 为空时自动使用测试数据库。也可以通过环境变量 `DATABASE_DSN` 覆盖。
## 发货逻辑
### 自动发货(`deliveryMode = "auto"`
1. `POST /api/checkout` → 从 `product_codes` 提取指定数量的卡密
2. 商品 `quantity` 减少,卡密从数据库删除
3. 卡密保存到订单 `delivered_codes`
4. 用户 `POST /api/orders/:id/confirm` 确认付款后,订单状态变为 `completed`,响应中返回卡密内容
5. 同时调用 `IncrementSold` 增加销量统计
### 手动发货(`deliveryMode = "manual"`
1. `POST /api/checkout` → 创建订单,不提取卡密
2. 用户 `POST /api/orders/:id/confirm` 后,订单变为 `completed`,但 `delivered_codes` 为空
3. 管理员在后台查看订单的备注、手机号、邮箱后手动发货
## 本地开发
```bash
go run . # 启动服务(默认 :8080
go build -o mengyastore-backend.exe . # 构建可执行文件
go run ./cmd/migrate/main.go # 迁移旧 JSON 数据到数据库
```
### 切换数据库
```bash
# 测试库(默认)
# host: 10.1.1.100:3306 / db: mengyastore-test
# 生产库
set DATABASE_DSN=mengyastore:mengyastore@tcp(192.168.1.100:3306)/mengyastore?charset=utf8mb4&parseTime=True&loc=Local
./mengyastore-backend.exe
```
## 认证说明
### 用户认证
通过 SproutGate OAuth 服务验证 Bearer Token
```go
result, err := authClient.VerifyToken(token)
// result.Valid, result.User.Account, result.User.Username
```
### 管理员认证
管理员令牌通过查询参数或 Authorization 头传入:
```
GET /api/admin/products?token=xxx
Authorization: xxx
```
令牌与 `settings.json` 中的 `adminToken` 比对。