666 lines
13 KiB
Markdown
666 lines
13 KiB
Markdown
# Planting Service API 文档
|
||
|
||
## 目录
|
||
|
||
- [概述](#概述)
|
||
- [认证](#认证)
|
||
- [通用响应格式](#通用响应格式)
|
||
- [API 端点](#api-端点)
|
||
- [健康检查](#健康检查)
|
||
- [订单管理](#订单管理)
|
||
- [持仓查询](#持仓查询)
|
||
- [错误码](#错误码)
|
||
- [数据模型](#数据模型)
|
||
|
||
---
|
||
|
||
## 概述
|
||
|
||
- **Base URL**: `http://localhost:3003/api/v1`
|
||
- **协议**: HTTP/HTTPS
|
||
- **数据格式**: JSON
|
||
- **字符编码**: UTF-8
|
||
|
||
### Swagger 文档
|
||
|
||
启动服务后访问: `http://localhost:3003/api/docs`
|
||
|
||
---
|
||
|
||
## 认证
|
||
|
||
所有业务 API(除健康检查外)需要 JWT Bearer Token 认证。
|
||
|
||
### 请求头
|
||
|
||
```http
|
||
Authorization: Bearer <jwt_token>
|
||
```
|
||
|
||
### Token 结构
|
||
|
||
```json
|
||
{
|
||
"id": "1",
|
||
"username": "user@example.com",
|
||
"iat": 1699999999,
|
||
"exp": 1700003599
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 通用响应格式
|
||
|
||
### 成功响应
|
||
|
||
```json
|
||
{
|
||
"data": { ... },
|
||
"timestamp": "2024-11-30T10:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
### 错误响应
|
||
|
||
```json
|
||
{
|
||
"statusCode": 400,
|
||
"message": "错误信息",
|
||
"error": "Bad Request",
|
||
"timestamp": "2024-11-30T10:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## API 端点
|
||
|
||
### 健康检查
|
||
|
||
#### GET /health
|
||
|
||
检查服务健康状态。
|
||
|
||
**请求**
|
||
```http
|
||
GET /api/v1/health
|
||
```
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"timestamp": "2024-11-30T10:00:00.000Z",
|
||
"service": "planting-service"
|
||
}
|
||
```
|
||
|
||
#### GET /health/ready
|
||
|
||
检查服务就绪状态。
|
||
|
||
**请求**
|
||
```http
|
||
GET /api/v1/health/ready
|
||
```
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"status": "ready",
|
||
"timestamp": "2024-11-30T10:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 订单管理
|
||
|
||
#### POST /planting/orders
|
||
|
||
创建认种订单。
|
||
|
||
**请求**
|
||
```http
|
||
POST /api/v1/planting/orders
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"treeCount": 5
|
||
}
|
||
```
|
||
|
||
**参数说明**
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|-----|------|-----|------|
|
||
| treeCount | number | 是 | 认种数量,1-1000 |
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"orderNo": "PO202411300001",
|
||
"userId": "1",
|
||
"treeCount": 5,
|
||
"totalAmount": 10995,
|
||
"status": "CREATED",
|
||
"createdAt": "2024-11-30T10:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
**错误码**
|
||
|
||
| 状态码 | 说明 |
|
||
|-------|------|
|
||
| 400 | 参数错误(数量超限) |
|
||
| 400 | 超过个人最大认种数量限制(1000棵)|
|
||
| 400 | 余额不足 |
|
||
| 401 | 未授权 |
|
||
|
||
---
|
||
|
||
#### GET /planting/orders
|
||
|
||
查询用户订单列表。
|
||
|
||
**请求**
|
||
```http
|
||
GET /api/v1/planting/orders?page=1&pageSize=10
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**参数说明**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|-----|------|-----|-------|------|
|
||
| page | number | 否 | 1 | 页码 |
|
||
| pageSize | number | 否 | 10 | 每页数量,最大100 |
|
||
|
||
**响应**
|
||
```json
|
||
[
|
||
{
|
||
"orderNo": "PO202411300001",
|
||
"treeCount": 5,
|
||
"totalAmount": 10995,
|
||
"status": "CREATED",
|
||
"selectedProvince": null,
|
||
"selectedCity": null,
|
||
"createdAt": "2024-11-30T10:00:00.000Z"
|
||
},
|
||
{
|
||
"orderNo": "PO202411300002",
|
||
"treeCount": 3,
|
||
"totalAmount": 6597,
|
||
"status": "MINING_ENABLED",
|
||
"selectedProvince": "广东省",
|
||
"selectedCity": "广州市",
|
||
"createdAt": "2024-11-25T08:00:00.000Z"
|
||
}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
#### GET /planting/orders/:orderNo
|
||
|
||
查询订单详情。
|
||
|
||
**请求**
|
||
```http
|
||
GET /api/v1/planting/orders/PO202411300001
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"orderNo": "PO202411300001",
|
||
"userId": "1",
|
||
"treeCount": 5,
|
||
"totalAmount": 10995,
|
||
"status": "FUND_ALLOCATED",
|
||
"selectedProvince": "广东省",
|
||
"selectedCity": "广州市",
|
||
"provinceCitySelectedAt": "2024-11-30T10:01:00.000Z",
|
||
"provinceCityConfirmedAt": "2024-11-30T10:01:05.000Z",
|
||
"paidAt": "2024-11-30T10:02:00.000Z",
|
||
"fundAllocatedAt": "2024-11-30T10:02:01.000Z",
|
||
"allocations": [
|
||
{
|
||
"targetType": "POOL",
|
||
"amount": 9895.5
|
||
},
|
||
{
|
||
"targetType": "OPERATION",
|
||
"amount": 549.75
|
||
}
|
||
],
|
||
"createdAt": "2024-11-30T10:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### POST /planting/orders/:orderNo/select-province-city
|
||
|
||
选择省市(开始5秒倒计时)。
|
||
|
||
**请求**
|
||
```http
|
||
POST /api/v1/planting/orders/PO202411300001/select-province-city
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"provinceCode": "440000",
|
||
"provinceName": "广东省",
|
||
"cityCode": "440100",
|
||
"cityName": "广州市"
|
||
}
|
||
```
|
||
|
||
**参数说明**
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|-----|------|-----|------|
|
||
| provinceCode | string | 是 | 省份代码 |
|
||
| provinceName | string | 是 | 省份名称 |
|
||
| cityCode | string | 是 | 城市代码 |
|
||
| cityName | string | 是 | 城市名称 |
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "省市选择成功,请在5秒后确认",
|
||
"expiresAt": "2024-11-30T10:01:05.000Z"
|
||
}
|
||
```
|
||
|
||
**错误码**
|
||
|
||
| 状态码 | 说明 |
|
||
|-------|------|
|
||
| 400 | 订单状态不允许选择省市 |
|
||
| 404 | 订单不存在 |
|
||
| 403 | 无权操作此订单 |
|
||
|
||
---
|
||
|
||
#### POST /planting/orders/:orderNo/confirm-province-city
|
||
|
||
确认省市选择(需在选择5秒后调用)。
|
||
|
||
**请求**
|
||
```http
|
||
POST /api/v1/planting/orders/PO202411300001/confirm-province-city
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "省市确认成功"
|
||
}
|
||
```
|
||
|
||
**错误码**
|
||
|
||
| 状态码 | 说明 |
|
||
|-------|------|
|
||
| 400 | 还需等待5秒才能确认 |
|
||
| 400 | 订单状态不允许确认 |
|
||
| 404 | 订单不存在 |
|
||
|
||
---
|
||
|
||
#### POST /planting/orders/:orderNo/pay
|
||
|
||
支付订单。
|
||
|
||
**请求**
|
||
```http
|
||
POST /api/v1/planting/orders/PO202411300001/pay
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"orderNo": "PO202411300001",
|
||
"status": "POOL_SCHEDULED",
|
||
"paidAt": "2024-11-30T10:02:00.000Z",
|
||
"totalAmount": 10995,
|
||
"allocations": [
|
||
{
|
||
"targetType": "POOL",
|
||
"amount": 9895.5,
|
||
"description": "资金池"
|
||
},
|
||
{
|
||
"targetType": "OPERATION",
|
||
"amount": 549.75,
|
||
"description": "运营费用"
|
||
},
|
||
{
|
||
"targetType": "PROVINCE_AUTH",
|
||
"amount": 65.97,
|
||
"description": "省代奖励"
|
||
},
|
||
{
|
||
"targetType": "CITY_AUTH",
|
||
"amount": 32.985,
|
||
"description": "市代奖励"
|
||
},
|
||
{
|
||
"targetType": "COMMUNITY",
|
||
"amount": 54.975,
|
||
"description": "社区长奖励"
|
||
},
|
||
{
|
||
"targetType": "REFERRAL_L1",
|
||
"amount": 164.925,
|
||
"description": "一级推荐奖励"
|
||
},
|
||
{
|
||
"targetType": "REFERRAL_L2",
|
||
"amount": 109.95,
|
||
"description": "二级推荐奖励"
|
||
},
|
||
{
|
||
"targetType": "REFERRAL_L3",
|
||
"amount": 54.975,
|
||
"description": "三级推荐奖励"
|
||
},
|
||
{
|
||
"targetType": "PLATFORM",
|
||
"amount": 32.985,
|
||
"description": "平台费用"
|
||
},
|
||
{
|
||
"targetType": "RESERVE",
|
||
"amount": 32.985,
|
||
"description": "储备金"
|
||
}
|
||
],
|
||
"batchInfo": {
|
||
"batchNo": "BATCH202411300001",
|
||
"scheduledInjectionTime": "2024-12-05T00:00:00.000Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
**错误码**
|
||
|
||
| 状态码 | 说明 |
|
||
|-------|------|
|
||
| 400 | 余额不足 |
|
||
| 400 | 订单状态不允许支付 |
|
||
| 400 | 请先确认省市选择 |
|
||
| 404 | 订单不存在 |
|
||
|
||
---
|
||
|
||
#### POST /planting/orders/:orderNo/cancel
|
||
|
||
取消订单(仅未支付订单可取消)。
|
||
|
||
**请求**
|
||
```http
|
||
POST /api/v1/planting/orders/PO202411300001/cancel
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "订单取消成功"
|
||
}
|
||
```
|
||
|
||
**错误码**
|
||
|
||
| 状态码 | 说明 |
|
||
|-------|------|
|
||
| 400 | 订单状态不允许取消(已支付) |
|
||
| 404 | 订单不存在 |
|
||
| 403 | 无权操作此订单 |
|
||
|
||
---
|
||
|
||
### 持仓查询
|
||
|
||
#### GET /planting/position
|
||
|
||
查询用户持仓信息。
|
||
|
||
**请求**
|
||
```http
|
||
GET /api/v1/planting/position
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"totalTreeCount": 15,
|
||
"effectiveTreeCount": 12,
|
||
"pendingTreeCount": 3,
|
||
"firstMiningStartAt": "2024-11-20T00:00:00.000Z",
|
||
"distributions": [
|
||
{
|
||
"provinceCode": "440000",
|
||
"provinceName": "广东省",
|
||
"cityCode": "440100",
|
||
"cityName": "广州市",
|
||
"treeCount": 10
|
||
},
|
||
{
|
||
"provinceCode": "310000",
|
||
"provinceName": "上海市",
|
||
"cityCode": "310100",
|
||
"cityName": "上海市",
|
||
"treeCount": 5
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**字段说明**
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|-----|------|------|
|
||
| totalTreeCount | number | 总认种数量 |
|
||
| effectiveTreeCount | number | 有效数量(已开始挖矿)|
|
||
| pendingTreeCount | number | 待生效数量 |
|
||
| firstMiningStartAt | string | 首次挖矿开始时间 |
|
||
| distributions | array | 按省市分布 |
|
||
|
||
---
|
||
|
||
## 错误码
|
||
|
||
### HTTP 状态码
|
||
|
||
| 状态码 | 说明 |
|
||
|-------|------|
|
||
| 200 | 成功 |
|
||
| 201 | 创建成功 |
|
||
| 400 | 请求参数错误 |
|
||
| 401 | 未授权 |
|
||
| 403 | 禁止访问 |
|
||
| 404 | 资源不存在 |
|
||
| 500 | 服务器内部错误 |
|
||
|
||
### 业务错误码
|
||
|
||
| 错误信息 | 说明 |
|
||
|---------|------|
|
||
| 超过个人最大认种数量限制 | 单用户最多认种1000棵 |
|
||
| 余额不足 | USDT 余额不足以支付 |
|
||
| 订单不存在 | 订单号无效或已删除 |
|
||
| 无权操作此订单 | 订单不属于当前用户 |
|
||
| 订单状态不允许此操作 | 状态机校验失败 |
|
||
| 还需等待5秒才能确认 | 省市选择5秒机制 |
|
||
|
||
---
|
||
|
||
## 数据模型
|
||
|
||
### PlantingOrder
|
||
|
||
```typescript
|
||
interface PlantingOrder {
|
||
orderNo: string; // 订单号,格式:PO + 年月日 + 序号
|
||
userId: string; // 用户ID
|
||
treeCount: number; // 认种数量
|
||
totalAmount: number; // 总金额 (USDT)
|
||
status: PlantingOrderStatus;
|
||
selectedProvince?: string; // 选择的省份
|
||
selectedCity?: string; // 选择的城市
|
||
provinceCitySelectedAt?: Date;
|
||
provinceCityConfirmedAt?: Date;
|
||
paidAt?: Date;
|
||
fundAllocatedAt?: Date;
|
||
poolInjectionBatchId?: string;
|
||
poolInjectionScheduledTime?: Date;
|
||
poolInjectionActualTime?: Date;
|
||
poolInjectionTxHash?: string;
|
||
miningEnabledAt?: Date;
|
||
createdAt: Date;
|
||
updatedAt: Date;
|
||
}
|
||
```
|
||
|
||
### PlantingOrderStatus
|
||
|
||
```typescript
|
||
enum PlantingOrderStatus {
|
||
CREATED = 'CREATED',
|
||
PROVINCE_CITY_SELECTED = 'PROVINCE_CITY_SELECTED',
|
||
PROVINCE_CITY_CONFIRMED = 'PROVINCE_CITY_CONFIRMED',
|
||
PAID = 'PAID',
|
||
FUND_ALLOCATED = 'FUND_ALLOCATED',
|
||
POOL_SCHEDULED = 'POOL_SCHEDULED',
|
||
POOL_INJECTED = 'POOL_INJECTED',
|
||
MINING_ENABLED = 'MINING_ENABLED',
|
||
CANCELLED = 'CANCELLED'
|
||
}
|
||
```
|
||
|
||
### FundAllocationTargetType
|
||
|
||
```typescript
|
||
enum FundAllocationTargetType {
|
||
POOL = 'POOL', // 资金池 90%
|
||
OPERATION = 'OPERATION', // 运营 5%
|
||
PROVINCE_AUTH = 'PROVINCE_AUTH', // 省代 0.6%
|
||
CITY_AUTH = 'CITY_AUTH', // 市代 0.3%
|
||
COMMUNITY = 'COMMUNITY', // 社区长 0.5%
|
||
REFERRAL_L1 = 'REFERRAL_L1', // 一级推荐 1.5%
|
||
REFERRAL_L2 = 'REFERRAL_L2', // 二级推荐 1.0%
|
||
REFERRAL_L3 = 'REFERRAL_L3', // 三级推荐 0.5%
|
||
PLATFORM = 'PLATFORM', // 平台 0.3%
|
||
RESERVE = 'RESERVE' // 储备 0.3%
|
||
}
|
||
```
|
||
|
||
### PlantingPosition
|
||
|
||
```typescript
|
||
interface PlantingPosition {
|
||
userId: string;
|
||
totalTreeCount: number;
|
||
effectiveTreeCount: number;
|
||
pendingTreeCount: number;
|
||
firstMiningStartAt?: Date;
|
||
distributions: PositionDistribution[];
|
||
}
|
||
|
||
interface PositionDistribution {
|
||
provinceCode: string;
|
||
provinceName: string;
|
||
cityCode: string;
|
||
cityName: string;
|
||
treeCount: number;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 使用示例
|
||
|
||
### cURL 示例
|
||
|
||
```bash
|
||
# 1. 创建订单
|
||
curl -X POST http://localhost:3003/api/v1/planting/orders \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"treeCount": 5}'
|
||
|
||
# 2. 选择省市
|
||
curl -X POST http://localhost:3003/api/v1/planting/orders/PO202411300001/select-province-city \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"provinceCode": "440000",
|
||
"provinceName": "广东省",
|
||
"cityCode": "440100",
|
||
"cityName": "广州市"
|
||
}'
|
||
|
||
# 3. 等待5秒后确认省市
|
||
sleep 5
|
||
curl -X POST http://localhost:3003/api/v1/planting/orders/PO202411300001/confirm-province-city \
|
||
-H "Authorization: Bearer $TOKEN"
|
||
|
||
# 4. 支付订单
|
||
curl -X POST http://localhost:3003/api/v1/planting/orders/PO202411300001/pay \
|
||
-H "Authorization: Bearer $TOKEN"
|
||
|
||
# 5. 查询持仓
|
||
curl http://localhost:3003/api/v1/planting/position \
|
||
-H "Authorization: Bearer $TOKEN"
|
||
```
|
||
|
||
### JavaScript/TypeScript 示例
|
||
|
||
```typescript
|
||
import axios from 'axios';
|
||
|
||
const api = axios.create({
|
||
baseURL: 'http://localhost:3003/api/v1',
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`,
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
|
||
// 创建订单
|
||
const { data: order } = await api.post('/planting/orders', { treeCount: 5 });
|
||
|
||
// 选择省市
|
||
await api.post(`/planting/orders/${order.orderNo}/select-province-city`, {
|
||
provinceCode: '440000',
|
||
provinceName: '广东省',
|
||
cityCode: '440100',
|
||
cityName: '广州市'
|
||
});
|
||
|
||
// 等待5秒
|
||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||
|
||
// 确认省市
|
||
await api.post(`/planting/orders/${order.orderNo}/confirm-province-city`);
|
||
|
||
// 支付
|
||
const { data: payResult } = await api.post(`/planting/orders/${order.orderNo}/pay`);
|
||
console.log('支付成功,资金分配:', payResult.allocations);
|
||
```
|