rwadurian/backend/services/planting-service/docs/API.md

666 lines
13 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.

# 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);
```