Files
affiliate_dash/README.md
T
yml2213 d821fe13df fix(security): 分离 JWT 与数据加密密钥并新增启动安全体检
.env 中 JWT_SECRET 仍逐字节等于 .env.example 的示例值,且 DATA_ENCRYPTION_KEY
未设置、按 config.go 的回退逻辑实际等于 JWT_SECRET。任何见过仓库的人都能伪造
管理员 JWT,并解密库中全部 API Client / 回调密钥密文。

- JWT_SECRET 与 DATA_ENCRYPTION_KEY 改为各自独立的强随机值
- 新增 Config.SecurityWarnings():示例密钥 / 未分离 / release 下开调试日志均告警
- 新增 cmd/reencrypt_secrets:默认只预演,-apply 才写库;幂等,坏数据不中断整轮
- 新增 docs/密钥轮换说明.md;.gitignore 忽略 .env.bak*
- release 模式下关闭 OPEN_API_DEBUG
2026-10-06 19:42:42 +08:00

215 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 游戏皮肤分销管理系统
基于 Go + React 的游戏皮肤分销管理平台,支持分销商管理、订单追踪、上游皮肤源头对接。
## 技术栈
| 层级 | 技术 |
|------|------|
| 后端 | Go 1.26 + Gin + GORM |
| 数据库 | PostgreSQL 16 |
| 前端 | React 19 + TypeScript + Ant Design |
| 构建 | Vite + Docker Compose |
| 反向代理 | Caddy |
## 本地开发
### 环境要求
- Go 1.26+
- Node.js 22+
- Docker / Docker Compose(用于自动启动 PostgreSQL 开发服务)
### 快速启动
```bash
# 1. 复制配置文件
cp .env.example .env
# 按需修改 .env 中的密钥
# 2. 一键启动(后端 + 前端 + PostgreSQL)
make dev
# 或直接
./start.sh
```
`make dev` 会通过 Docker Compose 启动 `affiliate_dash/postgres`,后端和前端仍在宿主机运行,方便热更新。首次从旧版 `affiliate_dash_postgres_dev` 开发容器迁移时,脚本会停止旧容器并优先复用旧数据卷。
启动后:
- 前端:`http://localhost:15173`
- 后端:`http://localhost:18080`
按 `Ctrl+C` 停止所有服务。
### 手动启动
```bash
# 安装依赖
make install
# 分别启动
make backend # 后端 :18080
make frontend # 前端 :15173
```
## Docker 生产部署
### 配置
```bash
cp .env.example .env
# 编辑 .env,确保以下变量已配置:
# JWT_SECRET — 生产环境务必修改
# OPEN_API_KEY — 皮肤源头对接 API Key
# OPEN_API_SECRET — 签名密钥
# POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB — PostgreSQL 初始化配置
```
> 国内服务器可在 `.env` 中取消 `GOPROXY`、`ALPINE_MIRROR`、`NPM_REGISTRY` 等镜像加速源的注释。
> Docker 后端容器默认使用 `postgres` 作为数据库主机;只有连接外部数据库时才需要设置 `DOCKER_DATABASE_URL`。
### 启动服务
```bash
# 构建并启动全部服务(PostgreSQL + 后端 + Caddy)
make docker-up
# 或
docker compose up -d --build
```
服务端口(默认值已避开常见端口,避免与其他项目冲突;均可在 `.env` 中覆盖):
- `:80` — 前端页面 + API(Caddy 统一入口,可通过 `HTTP_PORT`/`HTTPS_PORT` 调整)
- `:18080` — 后端 API(直接访问,调试用,可通过 `PORT` 调整)
- `:15432` — PostgreSQL(本地开发/调试用,可通过 `POSTGRES_PORT` 调整)
### 常用命令
```bash
make docker-logs # 查看日志
make docker-ps # 查看容器状态
make docker-down # 停止并移除
```
### 自定义域名
`Caddyfile` 默认同时支持本地 `:80` 和示例域名 `skin.khhao.com`。生产部署时把 `skin.khhao.com` 改为你的域名,重新构建即可。
## 环境变量
| 变量 | 说明 | 默认值 |
|------|------|--------|
| `PORT` | 后端端口 | `18080` |
| `FRONTEND_PORT` | 前端开发端口 | `15173` |
| `HTTP_PORT` / `HTTPS_PORT` | Caddy 对外 HTTP/HTTPS 端口 | `80` / `443` |
| `JWT_SECRET` | JWT 签名密钥 | 开发默认值 |
| `DATA_ENCRYPTION_KEY` | 加密落库的 API/回调密钥;留空会回退到 `JWT_SECRET` 并在启动日志告警 | 回退到 `JWT_SECRET` |
| `DATABASE_URL` | PostgreSQL 连接串 | 本地开发默认值 |
| `DOCKER_DATABASE_URL` | Docker 后端容器内 PostgreSQL 连接串,通常无需设置 | Compose 内置默认值 |
| `POSTGRES_PORT` | PostgreSQL 映射到宿主机的端口 | `15432` |
| `COMPOSE_PROJECT_NAME` | Docker Compose 项目名 | `affiliate_dash` |
| `POSTGRES_DATA_VOLUME` | PostgreSQL 数据卷名 | `affiliate_dash_postgres_data` |
| `GIN_MODE` | Gin 运行模式 | `debug` |
| `OPEN_API_KEY` | 开放接口 ApiKey | 需修改 |
| `OPEN_API_SECRET` | 开放接口签名密钥 | 需修改 |
| `OPEN_SIGN_SKEW` | 签名时间戳偏差(秒) | `300` |
| `OPEN_API_DEBUG` | 开放接口调试日志 | debug 模式默认开启 |
| `LOG_FILE` | 日志文件路径(按天分割为 `app-YYYYMMDD.log`) | `logs/app.log` |
| `LOG_RETAIN_DAYS` | 日志文件保留天数,到期自动清理 | `30` |
| `CORS_ALLOWED_ORIGINS` | 跨域来源白名单(逗号分隔);生产同域部署无需配置,为空时仅放行本地开发端口,不支持 `*` | 空 |
| `FULFILLMENT_PROCESSING_TIMEOUT_MINUTES` | 发货中订单自动标记失败的超时分钟数,<=0 关闭 | `30` |
| `FULFILLMENT_TIMEOUT_SCAN_INTERVAL_SECONDS` | 发货超时巡检间隔秒数 | `60` |
> **安全提示**:`JWT_SECRET` 与 `DATA_ENCRYPTION_KEY` 必须各自生成随机值(`openssl rand -hex 48` / `openssl rand -hex 32`),
> 不能沿用 `.env.example` 中的示例值,也不能两者相同。服务启动时会自动体检并打印
> `[CONFIG] 安全告警: ...`。轮换方法见 [`docs/密钥轮换说明.md`](docs/密钥轮换说明.md)。
## 开放接口(皮肤源头对接)
项目有两套外部 API,请先区分调用方:
- 商户侧 `/api/client/v1`:商户系统创建订单、查询订单和钱包,使用商户专属 `X-App-Key`。
- 源头侧 `/api/open/v1`:上游发货平台查询订单并回传发货结果,使用平台配置的 `X-Api-Key`。
源头侧 `ship_notify` 当前只使用 `success` / `failed`,速查见 [`docs/发货通知约定.md`](docs/发货通知约定.md),完整关系图见 [`docs/API对接关系.md`](docs/API对接关系.md)。两套 API 的签名算法不同,不能混用鉴权头或签名串。
### 状态速查
| 订单状态 | 含义 | 是否可发货 |
|----------|------|------------|
| `paid` | 已支付/已扣款,待发货 | 是 |
| `delivering` | 发货中 | 否 |
| `delivered` | 已交付 | 否 |
| `ship_failed` | 发货失败,可重试 | 是 |
| `cancelled` | 已取消并退款 | 否 |
上游皮肤源头系统调用源头侧接口时,需携带签名头 `X-Api-Key`、`X-Timestamp`、`X-Nonce`、`X-Sign`。
### 1. 查询订单(发货前置)
```
GET /api/open/v1/orders/:order_no
```
返回订单状态、商品信息、是否可发货。
### 2. 发货结果通知
```
POST /api/open/v1/orders/ship-notify
```
上游发货完成后推送结果,请求体:
```json
{
"order_no": "O202607241200000012",
"ship_status": "success",
"provider_order_no": "上游单号",
"shipped_at": "2026-07-24T16:00:00+08:00",
"fail_reason": "",
"game_channel": "IOS-微信",
"game_uid": "550e8400-e29b-41d4-a716-446655440000",
"role_name": "玩家名",
"pay_score": 100
}
```
| 字段 | 必填 | 说明 |
|------|------|------|
| `order_no` | 是 | 系统订单号 |
| `ship_status` | 是 | 仅 `success` / `failed` |
| `provider_order_no` | 否 | 上游单号 |
| `shipped_at` | 否 | 发货时间,RFC3339 格式 |
| `fail_reason` | 失败时是 | `failed` 时必填,填写详细失败原因 |
| `game_channel` | 否 | 账号区服(安卓/IOS-微信/QQ) |
| `game_uid` | 否 | 游戏角色 UUID |
| `role_name` | 否 | 角色名 |
| `pay_score` | 否 | 消耗积分 |
## 管理端
管理员登录后可访问商品管理、订单管理、用户管理、发货日志等功能。后台默认地址 `/`。
## 目录结构
```
├── backend/
│ ├── cmd/server/main.go # 入口
│ └── internal/
│ ├── config/ # 配置加载
│ ├── handler/ # HTTP 处理器
│ ├── middleware/ # JWT 认证、签名验证
│ ├── model/ # 数据模型
│ ├── pkg/ # 工具包
│ ├── router/ # 路由
│ └── service/ # 业务逻辑
├── frontend/src/
│ ├── api/ # API 调用
│ ├── pages/ # 页面组件
│ ├── layouts/ # 布局
│ └── types/ # 类型定义
├── docker-compose.yml # 容器编排
├── Makefile # 常用命令
└── start.sh # 开发一键启动
```