# 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 ``` ### 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 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 ``` **参数说明** | 参数 | 类型 | 必填 | 默认值 | 说明 | |-----|------|-----|-------|------| | 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 ``` **响应** ```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 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 ``` **响应** ```json { "success": true, "message": "省市确认成功" } ``` **错误码** | 状态码 | 说明 | |-------|------| | 400 | 还需等待5秒才能确认 | | 400 | 订单状态不允许确认 | | 404 | 订单不存在 | --- #### POST /planting/orders/:orderNo/pay 支付订单。 **请求** ```http POST /api/v1/planting/orders/PO202411300001/pay Authorization: Bearer ``` **响应** ```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 ``` **响应** ```json { "success": true, "message": "订单取消成功" } ``` **错误码** | 状态码 | 说明 | |-------|------| | 400 | 订单状态不允许取消(已支付) | | 404 | 订单不存在 | | 403 | 无权操作此订单 | --- ### 持仓查询 #### GET /planting/position 查询用户持仓信息。 **请求** ```http GET /api/v1/planting/position Authorization: Bearer ``` **响应** ```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); ```