# 萌芽小店 · 后端

基于 **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` 比对。
