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