From 2995cfdb6daa5343bf87097f04e91bb9c6a05dc7 Mon Sep 17 00:00:00 2001 From: zetaloop Date: Thu, 26 Feb 2026 05:42:12 +0800 Subject: [PATCH] docs(api): add TypeA and TypeB API docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two API design documents: 接口文档_TypeA.md (comprehensive v1 API spec) and 接口文档_TypeB.md (Juwan API design). --- 接口文档_TypeA.md | 1357 +++++++++++++++++++++++++++++++++++++++++++++ 接口文档_TypeB.md | 764 +++++++++++++++++++++++++ 2 files changed, 2121 insertions(+) create mode 100644 接口文档_TypeA.md create mode 100644 接口文档_TypeB.md diff --git a/接口文档_TypeA.md b/接口文档_TypeA.md new file mode 100644 index 0000000..7bbb356 --- /dev/null +++ b/接口文档_TypeA.md @@ -0,0 +1,1357 @@ +# 聚玩 后端 API 接口文档(v1 设计) + +最后更新:2026-02-25 +文档版本:0.1 + +本文档面向真实后端落地,覆盖当前前端演示项目已使用的接口形态,以及计划内但前端尚未接通的关键能力(认证、上传、通知推送、邀请流程等)。接口以可实现、可演进为目标,尽量与前端现有数据模型保持对齐,同时补足演示阶段缺失的安全边界与数据契约。 + +## 1. 通用约定 + +### 1.1 Base URL 与版本 + +- Base URL:`{API_BASE}/v1` +- 生产环境建议通过配置注入 `{API_BASE}`,前端不硬编码域名 + +### 1.2 数据格式 + +- 请求与响应:`application/json; charset=utf-8` +- 时间:ISO 8601 字符串(示例:`2026-02-25T12:34:56.789Z`) +- ID:不限定格式,统一使用 string。示例沿用演示项目风格:`ord-1700000000000-1` + +### 1.3 鉴权与身份视角 + +鉴权采用 Bearer Token: + +``` +Authorization: Bearer +``` + +多身份视角(consumer/player/owner)用于列表筛选与权限校验。服务端以登录用户的已认证角色为准,客户端可选传递当前视角: + +``` +X-Actor-Role: consumer | player | owner +``` + +约定: + +- `X-Actor-Role` 仅用于表达前端当前视角,服务端需要校验该角色是否在用户已认证角色集合中 +- 未提供 `X-Actor-Role` 时,服务端默认以用户的主角色或 consumer 视角处理 + +### 1.4 分页 + +列表接口统一支持: + +- `limit`:默认 20,最大 100 +- `offset`:默认 0 + +响应统一携带: + +```json +{ + "items": [], + "meta": { "total": 0, "offset": 0, "limit": 20 } +} +``` + +### 1.5 幂等 + +涉及资金、下单、创建类操作建议支持幂等键: + +``` +Idempotency-Key: +``` + +服务端应以 `(userId, Idempotency-Key)` 维度去重,返回相同结果。 + +### 1.6 错误响应 + +错误响应使用 HTTP 状态码表达大类,响应体提供机器可读 code 与人类可读 message: + +```json +{ + "code": "AUTH_REQUIRED", + "message": "请先登录", + "requestId": "req_xxx" +} +``` + +`code` 推荐复用前端演示项目中已有的 ReasonCode(便于迁移): + +- `AUTH_REQUIRED`:未登录或 token 失效(401) +- `ROLE_FORBIDDEN`:身份不允许(403) +- `NOT_PARTICIPANT`:非资源参与方(403) +- `NOT_FOUND`:资源不存在(404) +- `INVALID_STATUS`:状态机不允许该操作(409) +- `ALREADY_DONE`:重复提交(409) +- `DUPLICATE_REQUEST`:幂等冲突或短时间重复请求(409) +- `VALIDATION_FAILED`:参数校验失败(422) +- `PAYMENT_FAILED`:支付失败(409/422) +- `DISPUTE_LOCKED`:争议锁定导致操作受限(409) + +服务端可扩展: + +- `RATE_LIMITED`(429) +- `INTERNAL_ERROR`(500) + +## 2. 核心数据模型(摘要) + +以下为接口层建议使用的字段集合。为了匹配真实落地场景,对演示模型做了两处补充: + +1) 订单补充 `quantity`,用于表达服务数量(演示前端已有数量输入) +2) 服务 `unit` 不限制为固定枚举,推荐使用字符串并约束可选值集合 + +### 2.1 User + +```json +{ + "id": "u1", + "username": "chen", + "nickname": "小陈", + "avatar": "/avatars/u1.png", + "role": "consumer", + "verifiedRoles": ["consumer", "player"], + "verificationStatus": { "player": "approved", "owner": "pending" }, + "phone": "138****0000", + "bio": "简介", + "createdAt": "2026-02-25T12:00:00.000Z" +} +``` + +### 2.2 PlayerService + +```json +{ + "id": "svc-1", + "playerId": "u5", + "gameId": "g1", + "gameName": "英雄联盟", + "title": "LOL双排上分", + "description": "服务描述", + "price": 25, + "unit": "局", + "rankRange": "黄金-铂金", + "availability": ["工作日晚上9点后", "周末全天"] +} +``` + +`unit` 推荐值:`局`、`星`、`次`、`小时`、`段`(服务端可按业务扩展)。 + +### 2.3 Order + +```json +{ + "id": "ord-1", + "consumerId": "u1", + "consumerName": "小陈", + "playerId": "u6", + "playerName": "Winter", + "shopId": "shop2", + "shopName": "Yuki 小屋", + "service": { "id": "s4", "playerId": "u6", "gameId": "g2", "gameName": "王者荣耀", "title": "王者双排稳上星", "description": "...", "price": 18, "unit": "星", "availability": [] }, + "quantity": 3, + "status": "pending_accept", + "totalPrice": 54, + "note": "3星,我用鲁班", + "createdAt": "2026-02-25T12:00:00.000Z", + "acceptedAt": null, + "closedAt": null, + "completedAt": null +} +``` + +`status` 取值与状态机见 7.5。 + +### 2.4 Dispute(详情态) + +争议详情建议返回扩展字段,匹配前端现有展示与时间线: + +```json +{ + "id": "disp-1", + "orderId": "ord-1", + "initiatorId": "u1", + "initiatorName": "小陈", + "reason": "争议原因", + "evidence": ["https://cdn/.../a.png"], + "respondentReason": "回应理由", + "respondentEvidence": ["https://cdn/.../b.png"], + "status": "reviewing", + "result": null, + "appealReason": null, + "appealedAt": null, + "createdAt": "2026-02-25T12:00:00.000Z", + "timeline": [ + { "id": "timeline-1", "type": "created", "content": "小陈 提交争议", "createdAt": "2026-02-25T12:00:00.000Z" }, + { "id": "timeline-2", "type": "response", "content": "对方已提交回应材料", "createdAt": "2026-02-25T12:10:00.000Z" } + ] +} +``` + +### 2.5 Game + +```json +{ + "id": "g1", + "name": "英雄联盟", + "icon": "lol", + "category": "MOBA" +} +``` + +### 2.6 Player + +```json +{ + "id": "u5", + "user": { "id": "u5", "username": "jun", "nickname": "俊俊", "avatar": "/avatars/u5.png", "role": "player", "createdAt": "..." }, + "rating": 4.2, + "totalOrders": 156, + "completionRate": 0.94, + "status": "available", + "games": ["英雄联盟", "王者荣耀"], + "services": [], + "shopId": "shop1", + "shopName": "老王游戏工作室", + "tags": ["准时上线"] +} +``` + +### 2.7 Shop + +```json +{ + "id": "shop1", + "owner": { "id": "u10", "username": "wang", "nickname": "老王", "avatar": "/avatars/u10.png", "role": "owner", "createdAt": "..." }, + "name": "老王游戏工作室", + "banner": "/banners/shop1.jpg", + "description": "店铺简介", + "rating": 4.7, + "totalOrders": 3842, + "playerCount": 14, + "commissionType": "percentage", + "commissionValue": 15, + "allowMultiShop": false, + "allowIndependentOrders": false, + "dispatchMode": "manual", + "announcements": ["公告1"], + "templateConfig": { + "sections": [ + { "type": "intro", "enabled": true, "order": 1 }, + { "type": "players", "enabled": true, "order": 2 } + ] + } +} +``` + +### 2.8 Review + +```json +{ + "id": "rev-1", + "orderId": "ord-1", + "fromUserId": "u1", + "fromUserName": "小陈", + "fromUserAvatar": "/avatars/u1.png", + "toUserId": "u6", + "rating": 5, + "content": "体验很好", + "sealed": true, + "createdAt": "..." +} +``` + +### 2.9 Post + +```json +{ + "id": "post-1", + "author": { "id": "u1", "username": "chen", "nickname": "小陈", "avatar": "/avatars/u1.png", "role": "consumer", "createdAt": "..." }, + "authorRole": "consumer", + "title": "标题", + "content": "内容", + "images": ["https://cdn.../p1.png"], + "tags": ["英雄联盟", "攻略"], + "linkedOrderId": "ord-1", + "quotedPostId": null, + "likeCount": 10, + "commentCount": 2, + "liked": false, + "pinned": false, + "createdAt": "..." +} +``` + +### 2.10 Comment + +```json +{ + "id": "comment-1", + "postId": "post-1", + "author": { "id": "u2", "username": "ling", "nickname": "玲玲", "avatar": "/avatars/u2.png", "role": "consumer", "createdAt": "..." }, + "content": "评论内容", + "likeCount": 0, + "liked": false, + "createdAt": "..." +} +``` + +### 2.11 Notification + +```json +{ + "id": "notif-1", + "type": "order", + "title": "订单待接单", + "content": "xxx 已支付,等待接单", + "read": false, + "link": "/order/ord-1", + "createdAt": "..." +} +``` + +### 2.12 Favorite + +```json +{ + "id": "fav-1", + "userId": "u1", + "targetType": "player", + "targetId": "u6", + "createdAt": "..." +} +``` + +### 2.13 WalletTransaction(建议扩展字段) + +```json +{ + "id": "tx-1", + "type": "payment", + "amount": -54, + "orderId": "ord-1", + "shopId": "shop2", + "description": "支付订单 ord-1", + "createdAt": "..." +} +``` + +### 2.14 ChatSession + +```json +{ + "id": "chat-ord-1", + "type": "order", + "orderId": "ord-1", + "participants": [ + { "id": "u1", "name": "小陈", "avatar": "/avatars/u1.png" }, + { "id": "u6", "name": "Winter", "avatar": "/avatars/u6.png" } + ], + "lastMessage": "你好", + "lastMessageAt": "...", + "unreadCount": 0, + "readonly": false +} +``` + +### 2.15 ChatMessage + +```json +{ + "id": "msg-1", + "sessionId": "chat-ord-1", + "senderId": "u1", + "senderName": "小陈", + "senderAvatar": "/avatars/u1.png", + "type": "text", + "content": "你好", + "createdAt": "..." +} +``` + +### 2.16 File(上传结果) + +```json +{ + "id": "file-1", + "url": "https://cdn.example.com/files/file-1.png", + "contentType": "image/png", + "size": 123456 +} +``` + +## 3. 上传(文件/图片) + +演示前端目前大量使用 `URL.createObjectURL` 或固定占位路径。切真实后端时,建议统一通过上传接口获得可复现的 URL,再写入业务资源(头像、帖子图片、聊天图片、争议证据、认证材料等)。 + +### 3.1 上传文件 + +`POST /v1/files` + +- Auth:需要 +- Content-Type:`multipart/form-data` + +表单字段: + +- `file`:文件二进制 +- `purpose`:`avatar | post_image | chat_image | dispute_evidence | verification_material` + +响应: + +```json +{ + "id": "file-1", + "url": "https://cdn.example.com/files/file-1.png", + "contentType": "image/png", + "size": 123456 +} +``` + +约束建议: + +- 图片类限制 MIME 与大小(例如单张 ≤ 5MB) +- 上传结果 URL 建议为不可枚举地址或带访问控制 + +## 4. Auth(登录/注册/会话) + +### 4.1 注册 + +`POST /v1/auth/register` + +请求: + +```json +{ + "username": "chen", + "password": "******", + "nickname": "小陈" +} +``` + +响应: + +```json +{ + "accessToken": "token...", + "refreshToken": "token...", + "user": { "id": "u1", "username": "chen", "nickname": "小陈", "avatar": "", "role": "consumer", "createdAt": "..." } +} +``` + +### 4.2 登录 + +`POST /v1/auth/login` + +请求: + +```json +{ + "username": "chen", + "password": "******", + "remember": true +} +``` + +响应同 4.1。 + +### 4.3 刷新 token + +`POST /v1/auth/refresh` + +请求: + +```json +{ "refreshToken": "token..." } +``` + +响应: + +```json +{ "accessToken": "token..." } +``` + +### 4.4 获取当前用户 + +`GET /v1/auth/me` + +响应:User + +### 4.5 退出登录 + +`POST /v1/auth/logout` + +响应: + +```json +{ "ok": true } +``` + +### 4.6 找回密码(计划内) + +前端已有找回密码页面的 UI 行为,真实后端建议提供: + +- `POST /v1/auth/password/forgot`:发送验证码或重置链接 +- `POST /v1/auth/password/reset`:提交新密码 + +## 5. 用户与设置 + +### 5.1 获取用户信息 + +`GET /v1/users/{userId}` + +响应:User + +### 5.2 更新个人资料 + +`PATCH /v1/users/me` + +请求: + +```json +{ + "nickname": "新昵称", + "bio": "新简介", + "avatar": "https://cdn.../avatar.png" +} +``` + +响应:User + +### 5.3 更新通知偏好与外观偏好 + +`PATCH /v1/users/me/preferences` + +请求: + +```json +{ + "notificationPrefs": { "order": true, "community": true, "system": false }, + "themePreference": "system" +} +``` + +响应: + +```json +{ "ok": true } +``` + +### 5.4 身份认证(打手/店主) + +演示前端的身份认证页面包含真实姓名、身份证号、游戏资质描述与证明材料占位。真实后端建议将认证作为独立资源管理,并避免在任何对外接口返回敏感字段(例如身份证号原文与证件照片的可公开 URL)。 + +`GET /v1/verifications/me` + +响应示例: + +```json +{ + "items": [ + { "id": "ver-1", "role": "player", "status": "pending", "reason": null, "createdAt": "..." } + ], + "meta": { "total": 1, "offset": 0, "limit": 20 } +} +``` + +`POST /v1/verifications` + +请求示例: + +```json +{ + "role": "player", + "realName": "张三", + "idNumber": "110***********1234", + "qualificationText": "段位、经历等", + "materialFileIds": ["file-1", "file-2", "file-3"] +} +``` + +响应: + +```json +{ + "id": "ver-1", + "role": "player", + "status": "pending", + "createdAt": "..." +} +``` + +审核(平台后台,计划内): + +- `GET /v1/admin/verifications?status=pending` +- `POST /v1/admin/verifications/{verificationId}/approve` +- `POST /v1/admin/verifications/{verificationId}/reject`(携带 reason) + +## 6. 店铺、打手、服务 + +### 6.1 游戏列表 + +`GET /v1/games` + +响应: + +```json +{ + "items": [{ "id": "g1", "name": "英雄联盟", "icon": "lol", "category": "MOBA" }], + "meta": { "total": 1, "offset": 0, "limit": 20 } +} +``` + +### 6.2 打手列表 / 详情 + +`GET /v1/players` + +查询参数(可选): + +- `shopId` +- `gameId` 或 `gameName` +- `status`:`available | busy | offline` + +响应:分页 `items: Player[]` + +`GET /v1/players/{playerId}` + +响应:Player(可选包含 services 简要信息,或通过 6.4 获取) + +### 6.2.1 更新打手状态(计划内) + +`PATCH /v1/players/me/status` + +- Auth:需要 +- 允许角色:`player` + +请求: + +```json +{ "status": "available" } +``` + +响应: + +```json +{ "ok": true } +``` + +### 6.3 店铺列表 / 详情 + +`GET /v1/shops` + +查询参数(可选): + +- `gameId` 或 `gameName` +- `dispatchMode`:`manual | auto` + +响应:分页 `items: Shop[]` + +`GET /v1/shops/{shopId}` + +响应:Shop + +### 6.3.1 创建店铺(计划内) + +`POST /v1/shops` + +- Auth:需要 +- 允许角色:`owner` + +请求: + +```json +{ + "name": "我的店铺", + "description": "店铺简介", + "banner": "https://cdn.../banner.png" +} +``` + +响应:Shop + +### 6.4 服务列表 / 详情 + +`GET /v1/services` + +查询参数(可选): + +- `playerId` +- `shopId` +- `gameId` + +响应:分页 `items: PlayerService[]` + +`GET /v1/services/{serviceId}` + +响应:PlayerService + +### 6.5 服务管理(打手/店主) + +`POST /v1/services` + +- Auth:需要 +- 允许角色:`player`(创建自己的服务),`owner`(为自己店铺的员工创建服务) + +请求: + +```json +{ + "playerId": "u6", + "gameId": "g2", + "title": "王者双排稳上星", + "description": "描述", + "price": 18, + "unit": "星", + "rankRange": "钻石-星耀", + "availability": ["每天下午3点到凌晨"] +} +``` + +响应:PlayerService + +`PATCH /v1/services/{serviceId}`:更新服务 +`DELETE /v1/services/{serviceId}`:删除服务 + +### 6.6 店铺管理(店主) + +`GET /v1/shops/me` + +- Auth:需要 +- 允许角色:`owner` + +响应:Shop + +`PATCH /v1/shops/{shopId}` + +用于店铺规则、抽成、派单模式、公告等更新。建议服务端按字段做权限与范围校验。 + +请求示例(规则设置页): + +```json +{ + "allowMultiShop": true, + "allowIndependentOrders": true, + "dispatchMode": "auto", + "commissionType": "percentage", + "commissionValue": 12 +} +``` + +请求示例(模板编辑页): + +```json +{ + "templateConfig": { + "sections": [ + { "type": "intro", "enabled": true, "order": 1 }, + { "type": "players", "enabled": true, "order": 2 } + ] + } +} +``` + +请求示例(公告编辑): + +```json +{ + "announcements": ["公告1", "公告2"] +} +``` + +响应:Shop + +### 6.7 员工邀请与店铺归属(计划内,替代直接改字段) + +演示前端当前通过直接修改 `player.shopId/shopName` 模拟邀请,真实后端建议改为显式的邀请与同意流。 + +`POST /v1/shops/{shopId}/employee-invitations` + +请求: + +```json +{ "playerId": "u7", "message": "欢迎加入" } +``` + +响应: + +```json +{ + "id": "inv-1", + "shopId": "shop1", + "playerId": "u7", + "status": "pending", + "createdAt": "..." +} +``` + +`GET /v1/employee-invitations/me`:当前用户收到的邀请 +`POST /v1/employee-invitations/{invitationId}/accept`:接受邀请 +`POST /v1/employee-invitations/{invitationId}/reject`:拒绝邀请 + +店铺员工列表与移除(店主): + +- `GET /v1/shops/{shopId}/employees` +- `DELETE /v1/shops/{shopId}/employees/{playerId}` + +移除建议约束: + +- 存在进行中订单时,服务端可拒绝移除并返回 `INVALID_STATUS` 或 `VALIDATION_FAILED` +- 移除行为建议记录审计字段(操作者、原因、时间) + +服务端校验建议: + +- `allowMultiShop=false` 时,禁止玩家同时存在多个有效 shop 归属 +- `allowIndependentOrders=false` 时,玩家只能接本店订单(见 7.6) + +## 7. 订单 + +### 7.1 订单列表 + +`GET /v1/orders` + +查询参数(可选): + +- `status`(可重复):例如 `status=pending_accept&status=in_progress` +- `asRole`:`consumer | player | owner`(等价于 `X-Actor-Role`,二选一) +- `shopId`:店主视角下可选 + +响应:分页 `items: Order[]` + +权限: + +- consumer:仅可见自己下的单 +- player:仅可见自己作为打手的单 +- owner:仅可见自己店铺的单 + +### 7.2 订单详情 + +`GET /v1/orders/{orderId}` + +响应:Order + +### 7.3 下单与支付 + +`POST /v1/orders` + +- Auth:需要(consumer 视角) + +请求: + +```json +{ + "serviceId": "s4", + "quantity": 3, + "note": "3星,我用鲁班", + "payNow": true, + "paymentMethod": "wallet" +} +``` + +响应:Order(创建成功时建议直接返回 `pending_accept` 状态) + +### 7.4 订单动作(状态机) + +以下接口建议返回更新后的 Order。 + +`POST /v1/orders/{orderId}/pay` +`POST /v1/orders/{orderId}/accept` +`POST /v1/orders/{orderId}/close/request` +`POST /v1/orders/{orderId}/close/confirm` +`POST /v1/orders/{orderId}/cancel` + +状态约束建议与演示状态机一致(见 7.5)。 + +### 7.5 状态机(与前端演示一致) + +订单状态: + +- `pending_payment` → `pending_accept` → `in_progress` → `pending_close` → `pending_review` → `completed` +- 取消:`pending_payment | pending_accept` → `cancelled` +- 争议:`in_progress | pending_close` → `disputed` →(店主/平台处理)→ `pending_review` + +动作与允许迁移: + +- `PAY`:`pending_payment` → `pending_accept` +- `ACCEPT`:`pending_accept` → `in_progress` +- `REQUEST_CLOSE`:`in_progress` → `pending_close` +- `CONFIRM_CLOSE`:`pending_close` → `pending_review`(或超时自动进入) +- `CANCEL_PRE_ACCEPT`:`pending_payment | pending_accept` → `cancelled`(已扣款场景需退款) +- `OPEN_DISPUTE`:`in_progress | pending_close` → `disputed` +- `RESOLVE_DISPUTE`:`disputed` → `pending_review` +- `SUBMIT_REVIEW`:`pending_review` → `completed`(双方评价均提交后揭晓) + +### 7.6 店铺规则在订单侧的执行(计划内) + +当玩家已挂靠店铺且 `allowIndependentOrders=false` 时: + +- 玩家不得接店铺外的独立订单 +- 服务的可见范围建议由服务端控制(例如只暴露店铺内服务) + +当 `dispatchMode=auto` 时: + +- 接单动作可由服务端自动分配给某位员工,并记录分配原因与时间 + +## 8. 评价(密封机制) + +### 8.1 按订单查看评价 + +`GET /v1/orders/{orderId}/reviews` + +响应:分页 `items: Review[]` + +约束建议: + +- `sealed=true` 且双方未同时提交时,对非作者隐藏 `content`(可返回空字符串或省略字段) + +### 8.2 提交评价 + +`POST /v1/orders/{orderId}/reviews` + +请求: + +```json +{ "rating": 5, "content": "体验很好" } +``` + +响应: + +```json +{ "ok": true } +``` + +### 8.3 按打手查看评价 + +`GET /v1/users/{userId}/reviews` + +查询参数(可选): + +- `targetRole=player` + +响应:分页 `items: Review[]` + +## 9. 争议 + +### 9.1 发起争议 + +`POST /v1/orders/{orderId}/dispute` + +请求: + +```json +{ + "reason": "争议原因", + "evidence": ["https://cdn.../a.png"] +} +``` + +响应:Dispute(详情态) + +### 9.2 获取争议详情 + +`GET /v1/orders/{orderId}/dispute` + +响应:Dispute(详情态) + +### 9.3 提交回应材料 + +`POST /v1/disputes/{disputeId}/response` + +请求: + +```json +{ + "reason": "回应理由", + "evidence": ["https://cdn.../b.png"] +} +``` + +响应: + +```json +{ "ok": true } +``` + +### 9.4 提交申诉 + +`POST /v1/disputes/{disputeId}/appeal` + +请求: + +```json +{ "reason": "申诉理由" } +``` + +响应: + +```json +{ "ok": true } +``` + +### 9.5 仲裁(店主/平台) + +演示前端用定时器自动推进争议状态。真实后端建议提供显式仲裁接口,并落审计字段。 + +`POST /v1/disputes/{disputeId}/resolve` + +请求: + +```json +{ + "result": "partial_refund", + "refundAmount": 10, + "note": "处理说明" +} +``` + +响应:Dispute(详情态) + +### 9.6 争议列表(店主/平台) + +`GET /v1/disputes` + +查询参数(可选): + +- `status=open | reviewing | resolved | appealed` + +响应:分页 `items: Dispute(详情态或摘要态)[]` + +## 10. 社区(帖子/评论/点赞/置顶) + +### 10.1 帖子列表 + +`GET /v1/posts` + +查询参数(可选): + +- `authorId` +- `tag`(可重复) +- `sort=latest | hot` + +响应:分页 `items: Post[]` + +### 10.2 帖子详情 + +`GET /v1/posts/{postId}` + +响应:Post + +### 10.3 发帖 + +`POST /v1/posts` + +请求: + +```json +{ + "title": "标题", + "content": "内容", + "images": ["https://cdn.../p1.png"], + "tags": ["英雄联盟", "攻略"], + "linkedOrderId": "ord-1", + "quotedPostId": "post-1" +} +``` + +约束建议: + +- `linkedOrderId` 仅允许 consumer 且订单已完成 +- `quotedPostId` 必须存在,且引用关系不应形成环 + +响应:Post + +### 10.4 点赞(帖子) + +`PUT /v1/posts/{postId}/like`:点赞(幂等) +`DELETE /v1/posts/{postId}/like`:取消点赞(幂等) + +响应: + +```json +{ "ok": true } +``` + +### 10.5 置顶(计划内) + +`PUT /v1/posts/{postId}/pin`:作者置顶(建议限制每人最多 N 条) +`DELETE /v1/posts/{postId}/pin`:取消置顶 + +响应: + +```json +{ "ok": true } +``` + +### 10.6 评论列表与发表评论 + +`GET /v1/posts/{postId}/comments`:分页评论列表 + +`POST /v1/posts/{postId}/comments` + +请求: + +```json +{ "content": "评论内容" } +``` + +响应:Comment + +### 10.7 点赞(评论) + +`PUT /v1/comments/{commentId}/like` +`DELETE /v1/comments/{commentId}/like` + +响应: + +```json +{ "ok": true } +``` + +## 11. 收藏(关注) + +演示前端以 Favorite 表达收藏打手或店铺。后端建议以当前用户为主语管理收藏关系。 + +### 11.1 获取我的收藏 + +`GET /v1/favorites/me` + +查询参数(可选): + +- `targetType=player | shop` + +响应:分页 `items: Favorite[]` + +### 11.2 收藏与取消收藏 + +`PUT /v1/favorites/{targetType}/{targetId}`:收藏(幂等) +`DELETE /v1/favorites/{targetType}/{targetId}`:取消收藏(幂等) + +响应: + +```json +{ "ok": true } +``` + +### 11.3 获取某用户的公开收藏(可选) + +`GET /v1/users/{userId}/favorites` + +约束建议: + +- 默认仅允许本人读取 +- 若产品需要公开展示,可增加用户侧开关并在此接口执行隐私校验 + +## 12. 通知 + +### 12.1 通知列表 + +`GET /v1/notifications` + +查询参数(可选): + +- `type=order | community | system` +- `unreadOnly=1` + +响应:分页 `items: Notification[]` + +### 12.2 标记已读 + +`POST /v1/notifications/{notificationId}/read` +`POST /v1/notifications/read-all` + +响应: + +```json +{ "ok": true } +``` + +### 12.3 Web Push 订阅(计划内) + +`POST /v1/push/subscriptions` + +请求(示例,按 Web Push 标准字段落地): + +```json +{ + "endpoint": "https://push.service/...", + "keys": { "p256dh": "...", "auth": "..." }, + "userAgent": "..." +} +``` + +响应: + +```json +{ "id": "push-1", "ok": true } +``` + +`DELETE /v1/push/subscriptions/{subscriptionId}` + +## 13. 钱包与交易 + +### 13.1 获取钱包信息 + +`GET /v1/wallets/me` + +响应示例: + +```json +{ + "consumerBalance": 275, + "earningsBalance": 0 +} +``` + +### 13.2 交易流水 + +`GET /v1/wallets/me/transactions` + +查询参数(可选): + +- `type=topup | payment | income | withdrawal | refund` +- `shopId`:按店铺过滤(店主视角) +- `orderId`:按订单过滤 + +响应:分页 `items: WalletTransaction[]` + +建议在 WalletTransaction 中补充结构化字段,替代前端演示中的 description 解析: + +```json +{ + "id": "tx-1", + "type": "payment", + "amount": -54, + "orderId": "ord-1", + "shopId": "shop2", + "description": "支付订单 ord-1", + "createdAt": "..." +} +``` + +### 13.3 充值(计划内真实支付) + +`POST /v1/wallets/me/topups` + +请求: + +```json +{ "amount": 100, "channel": "mock" } +``` + +响应: + +```json +{ "ok": true, "transactionId": "tx-1" } +``` + +### 13.4 提现 + +`POST /v1/wallets/me/withdrawals` + +请求: + +```json +{ "amount": 320, "destination": { "type": "bank_card", "maskedNo": "****1234" } } +``` + +响应: + +```json +{ "ok": true, "transactionId": "tx-2" } +``` + +## 14. 搜索 + +演示项目唯一的 HTTP 链路是 `/api/search`。真实后端建议提供等价能力,并保留相同查询语义,便于迁移。 + +`GET /v1/search` + +查询参数: + +- `q`:关键词 +- `game`:可重复,游戏名或 id(建议最终统一为 id) +- `min`/`max`:价格区间(字符串或数字均可,推荐数字) +- `online=1`:仅在线 +- `minRating`:最低评分 +- `sort`:`composite | rating | orders | price_asc | price_desc` +- `limit`/`offset` + +响应(与前端 `SearchResponse` 对齐): + +```json +{ + "items": [ + { + "type": "player", + "player": { "id": "u5", "user": { "id": "u5", "nickname": "俊俊", "avatar": "", "username": "", "role": "player", "createdAt": "..." }, "rating": 4.2, "totalOrders": 156, "completionRate": 0.94, "status": "available", "games": ["英雄联盟"], "services": [], "tags": [] }, + "minPrice": 25, + "unit": "局", + "rating": 4.2, + "orders": 156 + }, + { + "type": "shop", + "shop": { "id": "shop1", "owner": { "id": "u10", "nickname": "老王", "avatar": "", "username": "", "role": "owner", "createdAt": "..." }, "name": "老王游戏工作室", "description": "店铺简介", "rating": 4.7, "totalOrders": 3842, "playerCount": 14, "commissionType": "percentage", "commissionValue": 15, "allowMultiShop": false, "allowIndependentOrders": false, "dispatchMode": "manual", "announcements": [], "templateConfig": { "sections": [] } }, + "minPrice": 25, + "unit": "局", + "rating": 4.7, + "orders": 3842, + "games": ["英雄联盟", "王者荣耀"], + "hasAvailable": true + } + ], + "meta": { "total": 2, "offset": 0, "limit": 12 } +} +``` + +## 15. 聊天 + +演示计划提到 WebSocket + 持久化。真实后端建议提供 REST 用于会话与历史消息,WebSocket 用于实时收发。 + +### 15.1 会话列表 + +`GET /v1/chat/sessions` + +响应:分页 `items: ChatSession[]` + +服务端必须按当前用户过滤会话参与者,避免返回无关会话。 + +### 15.2 会话消息 + +`GET /v1/chat/sessions/{sessionId}/messages` + +响应:分页 `items: ChatMessage[]` + +### 15.3 发送消息(HTTP 兜底) + +`POST /v1/chat/sessions/{sessionId}/messages` + +请求: + +```json +{ "type": "text", "content": "你好" } +``` + +或: + +```json +{ "type": "image", "content": "https://cdn.../img.png" } +``` + +响应:ChatMessage + +### 15.4 咨询会话(计划内) + +`POST /v1/chat/sessions` + +请求: + +```json +{ "type": "consultation", "targetUserId": "u5" } +``` + +响应:ChatSession + +### 15.5 WebSocket(建议) + +`GET /v1/ws/chat`(升级为 WebSocket) + +- 鉴权:在 query 或 headers 里携带 access token +- 服务端按 sessionId 与 participant 校验收发权限 diff --git a/接口文档_TypeB.md b/接口文档_TypeB.md new file mode 100644 index 0000000..79b71be --- /dev/null +++ b/接口文档_TypeB.md @@ -0,0 +1,764 @@ +# 聚玩 (Juwan) API 设计文档 + +## 1. 文档概述 +本文档为“聚玩”游戏陪玩平台的后端 API 设计规范,旨在指导后端开发人员实现真实的 API 接口,以替换当前纯前端的 Mock 系统。 +- **基础路径 (Base URL)**: `/api/v1` +- **数据格式**: `application/json` + +## 2. 全局约定 + +### 2.1 认证与授权 (Auth) +- 采用 JWT (JSON Web Token) 进行无状态认证。 +- 客户端在请求头中携带 Token:`Authorization: Bearer `。 +- 刷新令牌 (Refresh Token) 存储在 HttpOnly Cookie 中,用于获取新的 Access Token。 +- 接口权限标识: + - 🔒:需要登录认证。 + - 🛡️:需要管理员权限。 + +### 2.2 错误响应格式 +所有失败的请求均返回统一的 JSON 结构,并附带 HTTP 状态码(如 400, 401, 403, 404, 500)。 +```json +{ + "ok": false, + "reasonCode": "AUTH_REQUIRED", + "message": "请先登录" +} +``` +**ReasonCode 枚举值**: +- `AUTH_REQUIRED`: 未登录或 Token 失效 +- `NOT_FOUND`: 资源不存在 +- `NOT_PARTICIPANT`: 非参与者(无权操作该订单/会话等) +- `ROLE_FORBIDDEN`: 角色权限不足 +- `INVALID_STATUS`: 状态不合法(如订单状态不允许当前操作) +- `ALREADY_DONE`: 操作已完成(如已评价) +- `DISPUTE_LOCKED`: 处于争议状态,操作被锁定 +- `PAYMENT_FAILED`: 支付失败或余额不足 +- `IDEMPOTENT_NOOP`: 幂等操作(重复请求,无副作用) +- `VALIDATION_FAILED`: 参数校验失败 +- `DUPLICATE_REQUEST`: 重复请求 + +### 2.3 分页规范 +采用 `offset` 和 `limit` 进行分页(与前端 `SearchResponse` 保持一致)。 +**请求参数**:`?offset=0&limit=20` +**响应格式**: +```json +{ + "items": [...], + "meta": { + "total": 100, + "offset": 0, + "limit": 20 + } +} +``` + +### 2.4 文件上传 +- **端点**: `POST /api/v1/upload` +- **Content-Type**: `multipart/form-data` +- **参数**: `file` (文件), `type` (枚举: `avatar`, `chat`, `post`, `verification`, `dispute`) +- **响应**: 返回文件的 CDN URL。 + +### 2.5 时间戳格式 +所有时间字段均采用 ISO 8601 格式的 UTC 时间字符串,例如:`2024-03-20T12:00:00.000Z`。 + +### 2.6 角色系统 (UserRole) +- `consumer`: 普通消费者(老板) +- `player`: 陪玩打手 +- `owner`: 店铺店长 +- `admin`: 平台管理员(隐藏角色,用于后台管理) + +--- + +## 3. 数据模型 + +### 3.1 User (用户) +| 字段 | 类型 | 说明 | +| ------------------ | ---------- | ---------------------------------------------------- | +| id | string | 用户唯一标识 | +| username | string | 登录用户名 | +| nickname | string | 昵称 | +| avatar | string | 头像 URL | +| role | UserRole | 当前激活的角色 | +| verifiedRoles | UserRole[] | 已认证的角色列表 | +| verificationStatus | object | 各角色的认证状态 (`pending`, `approved`, `rejected`) | +| phone | string | 手机号 (可选) | +| bio | string | 个人简介 (可选) | +| createdAt | string | 注册时间 | + +### 3.2 Game (游戏) +| 字段 | 类型 | 说明 | +| -------- | ------ | ----------------------------------- | +| id | string | 游戏 ID | +| name | string | 游戏名称 | +| icon | string | 游戏图标 URL | +| category | string | 分类 (`MOBA`, `FPS`, `动作`, `RPG`) | + +### 3.3 Player & PlayerService (打手与服务) +**PlayerService**: +| 字段 | 类型 | 说明 | +| ------------ | -------- | --------------------------- | +| id | string | 服务 ID | +| playerId | string | 打手 ID | +| gameId | string | 游戏 ID | +| gameName | string | 游戏名称 | +| title | string | 服务标题 | +| description | string | 服务描述 | +| price | number | 价格 | +| unit | string | 计价单位 (`局`, `星`, `次`) | +| rankRange | string | 段位范围 (可选) | +| availability | string[] | 可接单时间段 | + +**Player**: +| 字段 | 类型 | 说明 | +| -------------- | --------------- | ------------------------------------- | +| id | string | 打手 ID | +| user | User | 关联的用户信息 | +| rating | number | 综合评分 | +| totalOrders | number | 总接单数 | +| completionRate | number | 完单率 (0-1) | +| status | string | 状态 (`available`, `busy`, `offline`) | +| games | string[] | 擅长游戏 ID 列表 | +| services | PlayerService[] | 提供的服务列表 | +| shopId | string | 所属店铺 ID (可选) | +| shopName | string | 所属店铺名称 (可选) | +| tags | string[] | 个人标签 | + +### 3.4 Shop (店铺) +| 字段 | 类型 | 说明 | +| ---------------------- | -------- | -------------------------------- | +| id | string | 店铺 ID | +| owner | User | 店长信息 | +| name | string | 店铺名称 | +| banner | string | 店铺招牌图 (可选) | +| description | string | 店铺简介 | +| rating | number | 综合评分 | +| totalOrders | number | 总单量 | +| playerCount | number | 打手数量 | +| commissionType | string | 抽成方式 (`fixed`, `percentage`) | +| commissionValue | number | 抽成数值 | +| allowMultiShop | boolean | 是否允许打手兼职 | +| allowIndependentOrders | boolean | 是否允许打手私下接单 | +| dispatchMode | string | 派单模式 (`manual`, `auto`) | +| announcements | string[] | 店铺公告列表 | +| templateConfig | object | 店铺主页模板配置 | + +### 3.5 Order (订单) +| 字段 | 类型 | 说明 | +| ------------ | ------------- | ------------------- | +| id | string | 订单 ID | +| consumerId | string | 消费者 ID | +| consumerName | string | 消费者昵称 | +| playerId | string | 打手 ID | +| playerName | string | 打手昵称 | +| shopId | string | 店铺 ID (可选) | +| shopName | string | 店铺名称 (可选) | +| service | PlayerService | 购买的服务快照 | +| status | OrderStatus | 订单状态 | +| totalPrice | number | 订单总价 | +| note | string | 备注 (可选) | +| createdAt | string | 创建时间 | +| acceptedAt | string | 接单时间 (可选) | +| closedAt | string | 申请结算时间 (可选) | +| completedAt | string | 完成时间 (可选) | + +**OrderStatus 枚举**: `pending_payment`, `pending_accept`, `in_progress`, `pending_close`, `pending_review`, `disputed`, `completed`, `cancelled` + +### 3.6 Review (评价) +| 字段 | 类型 | 说明 | +| -------------- | ------- | -------------------------------------- | +| id | string | 评价 ID | +| orderId | string | 关联订单 ID | +| fromUserId | string | 评价人 ID | +| fromUserName | string | 评价人昵称 | +| fromUserAvatar | string | 评价人头像 | +| toUserId | string | 被评价人 ID | +| rating | number | 评分 (1-5) | +| content | string | 评价内容 (可选) | +| sealed | boolean | 是否处于密封状态(双方未互评前不可见) | +| createdAt | string | 评价时间 | + +### 3.7 Dispute & DisputeRecord (争议) +| 字段 | 类型 | 说明 | +| ------------------ | -------- | ---------------------------------------------------------- | +| id | string | 争议 ID | +| orderId | string | 关联订单 ID | +| initiatorId | string | 发起人 ID | +| initiatorName | string | 发起人昵称 | +| reason | string | 争议原因 | +| evidence | string[] | 证据图片 URL 列表 | +| status | string | 状态 (`open`, `reviewing`, `resolved`, `appealed`) | +| result | string | 仲裁结果 (`full_refund`, `full_payment`, `partial_refund`) | +| respondentReason | string | 被诉方回应理由 (可选) | +| respondentEvidence | string[] | 被诉方证据 (可选) | +| appealReason | string | 申诉理由 (可选) | +| appealedAt | string | 申诉时间 (可选) | +| timeline | object[] | 争议处理时间线 | +| createdAt | string | 创建时间 | + +### 3.8 ChatSession & ChatMessage (聊天) +**ChatSession**: +| 字段 | 类型 | 说明 | +| ------------- | -------- | ---------------------------------- | +| id | string | 会话 ID | +| type | string | 会话类型 (`order`, `consultation`) | +| orderId | string | 关联订单 ID (仅 order 类型) | +| participants | object[] | 参与者列表 `{id, name, avatar}` | +| lastMessage | string | 最后一条消息内容 | +| lastMessageAt | string | 最后一条消息时间 | +| unreadCount | number | 未读消息数 | +| readonly | boolean | 是否只读(如订单完成/咨询超时后) | + +**ChatMessage**: +| 字段 | 类型 | 说明 | +| ------------ | ------ | ------------------------------------ | +| id | string | 消息 ID | +| sessionId | string | 会话 ID | +| senderId | string | 发送者 ID | +| senderName | string | 发送者昵称 | +| senderAvatar | string | 发送者头像 | +| type | string | 消息类型 (`text`, `image`, `system`) | +| content | string | 消息内容 | +| createdAt | string | 发送时间 | + +### 3.9 Post & Comment (社区) +**Post**: +| 字段 | 类型 | 说明 | +| ------------- | -------- | ---------------------- | +| id | string | 帖子 ID | +| author | User | 作者信息 | +| authorRole | UserRole | 作者发帖时的角色 | +| title | string | 标题 | +| content | string | 内容 | +| images | string[] | 图片列表 | +| tags | string[] | 标签列表 | +| linkedOrderId | string | 关联的订单 ID (秀单帖) | +| quotedPostId | string | 引用的帖子 ID (引用帖) | +| likeCount | number | 点赞数 | +| commentCount | number | 评论数 | +| liked | boolean | 当前用户是否已赞 | +| pinned | boolean | 是否被作者置顶 | +| createdAt | string | 发布时间 | + +**Comment**: +| 字段 | 类型 | 说明 | +| --------- | ------- | ---------------- | +| id | string | 评论 ID | +| postId | string | 关联帖子 ID | +| author | User | 评论者信息 | +| content | string | 评论内容 | +| likeCount | number | 点赞数 | +| liked | boolean | 当前用户是否已赞 | +| createdAt | string | 评论时间 | + +### 3.10 WalletTransaction (钱包流水) +| 字段 | 类型 | 说明 | +| ----------- | ------ | ----------------------------------------------------------- | +| id | string | 流水 ID | +| type | string | 类型 (`topup`, `payment`, `income`, `withdrawal`, `refund`) | +| amount | number | 金额 (正数为收入/充值/退款,负数为支出/提现) | +| description | string | 描述 | +| orderId | string | 关联订单 ID (可选,用于结构化统计,替代前端正则匹配) | +| createdAt | string | 发生时间 | + +### 3.11 Favorite (收藏) +| 字段 | 类型 | 说明 | +| ---------- | ------ | ------------------------------- | +| id | string | 收藏 ID | +| userId | string | 用户 ID | +| targetType | string | 收藏目标类型 (`player`, `shop`) | +| targetId | string | 收藏目标 ID | +| createdAt | string | 收藏时间 | + +### 3.12 Notification (通知) +| 字段 | 类型 | 说明 | +| --------- | ------- | ----------------------------------------- | +| id | string | 通知 ID | +| type | string | 通知类型 (`order`, `community`, `system`) | +| title | string | 通知标题 | +| content | string | 通知内容 | +| read | boolean | 是否已读 | +| link | string | 关联跳转链接 (可选) | +| createdAt | string | 通知时间 | +--- + +## 4. 认证与用户 + +| 方法 | 路径 | 权限 | 说明 | +| ------ | ------------------------------------- | ---- | --------------------------------- | +| POST | `/auth/register` | | 用户注册 | +| POST | `/auth/login` | | 用户登录,返回 JWT | +| POST | `/auth/logout` | 🔒 | 退出登录,清除 Cookie | +| POST | `/auth/refresh` | | 刷新 Token | +| POST | `/auth/forgot-password` | | 忘记密码(发送验证码) | +| POST | `/auth/reset-password` | | 重置密码 | +| GET | `/users/me` | 🔒 | 获取当前登录用户信息 | +| PUT | `/users/me` | 🔒 | 更新个人资料 (昵称, 头像, 简介等) | +| POST | `/users/me/switch-role` | 🔒 | 切换当前激活角色 | +| POST | `/users/me/verification` | 🔒 | 提交角色认证材料 | +| GET | `/users/me/verification` | 🔒 | 获取认证状态 | +| PUT | `/users/me/preferences/notifications` | 🔒 | 更新通知偏好设置 | +| PUT | `/users/me/preferences/theme` | 🔒 | 更新主题偏好设置 | +| GET | `/users/:id` | | 获取指定用户信息 | +| POST | `/users/:id/follow` | 🔒 | 关注用户 | +| DELETE | `/users/:id/follow` | 🔒 | 取消关注用户 | + +**请求示例:登录** +```json +// POST /api/v1/auth/login +{ + "phone": "13800138000", + "password": "...", + "remember": true +} +``` +**响应示例:登录成功** +```json +{ + "accessToken": "eyJhbGciOiJIUzI1NiIs...", + "refreshToken": "dGhpcyBpcyBhIHJlZnJlc2g...", + "user": { + "id": "u1", + "username": "zhangsan", + "nickname": "张三", + "avatar": "https://cdn.juwan.com/avatars/u1.jpg", + "role": "consumer", + "verifiedRoles": ["consumer", "player"], + "verificationStatus": { "consumer": "approved", "player": "approved" }, + "createdAt": "2024-01-15T08:00:00.000Z" + } +} +``` + +**请求示例:提交认证材料** +```json +// POST /api/v1/users/me/verification +{ + "role": "player", + "materials": { + "idCardFront": "https://cdn.juwan.com/uploads/xxx.jpg", + "idCardBack": "https://cdn.juwan.com/uploads/yyy.jpg", + "gameScreenshot": "https://cdn.juwan.com/uploads/zzz.jpg" + } +} +``` + +**请求示例:切换角色** +```json +// POST /api/v1/users/me/switch-role +{ "role": "player" } +``` + +**请求示例:更新通知偏好** +```json +// PUT /api/v1/users/me/preferences/notifications +{ "order": true, "community": true, "system": false } +``` +--- + +## 5. 游戏数据 + +| 方法 | 路径 | 权限 | 说明 | +| ---- | ------------ | ---- | ----------------------- | +| GET | `/games` | | 获取游戏列表 (支持分页) | +| GET | `/games/:id` | | 获取指定游戏详情 | + +--- + +## 6. 打手与服务 + +| 方法 | 路径 | 权限 | 说明 | +| ------ | ----------------------- | ---- | ------------------------------------------------- | +| GET | `/players` | | 获取打手列表 (支持分页、筛选) | +| GET | `/players/:id` | | 获取打手公开主页详情 | +| PUT | `/players/me/status` | 🔒 | 更新打手接单状态 (`available`, `busy`, `offline`) | +| GET | `/services` | | 获取所有服务列表 | +| GET | `/services/:id` | | 获取服务详情 | +| GET | `/players/:id/services` | | 获取指定打手的服务列表 | +| POST | `/services` | 🔒 | 创建服务 (仅 player) | +| PUT | `/services/:id` | 🔒 | 更新服务 (仅 player) | +| DELETE | `/services/:id` | 🔒 | 删除服务 (仅 player) | + +--- + +## 7. 店铺系统 + +| 方法 | 路径 | 权限 | 说明 | +| ------ | ------------------------------- | ---- | --------------------------- | +| GET | `/shops` | | 获取店铺列表 | +| GET | `/shops/:id` | | 获取店铺公开主页详情 | +| GET | `/users/:id/shop` | | 获取指定店长的店铺 | +| POST | `/shops` | 🔒 | 创建店铺 (仅 owner) | +| PUT | `/shops/:id` | 🔒 | 更新店铺基础信息及规则 | +| PUT | `/shops/:id/template` | 🔒 | 更新店铺主页模板配置 | +| PUT | `/shops/:id/announcements` | 🔒 | 更新店铺公告 | +| POST | `/shops/:id/invitations` | 🔒 | 邀请打手加入店铺 | +| POST | `/shops/invitations/:id/accept` | 🔒 | 打手接受店铺邀请 | +| DELETE | `/shops/:id/players/:playerId` | 🔒 | 将打手移出店铺 | +| GET | `/shops/:id/income-stats` | 🔒 | 获取店铺收入统计 (仅 owner) | + +| GET | `/shops/mine` | 🔒 | 获取当前登录店主的店铺详情 | +| GET | `/shops/:id/players` | | 获取店铺下的打手列表 | +| POST | `/shops/:id/announcements` | 🔒 | 新增店铺公告 | +| DELETE | `/shops/:id/announcements/:index` | 🔒 | 删除店铺公告 | +| DELETE | `/shops/invitations/:id` | 🔒 | 拒绝店铺邀请 (打手调用) | + +**请求示例:更新店铺规则** +```json +// PUT /api/v1/shops/:id +{ + "name": "星耀电竞工作室", + "description": "专业英雄联盟代练工作室", + "commissionType": "percentage", + "commissionValue": 15, + "allowMultiShop": false, + "allowIndependentOrders": true, + "dispatchMode": "auto" +} +``` +**请求示例:更新店铺模板** +```json +// PUT /api/v1/shops/:id/template +{ + "sections": [ + { "type": "banner", "enabled": true, "order": 0 }, + { "type": "intro", "enabled": true, "order": 1 }, + { "type": "services", "enabled": true, "order": 2 }, + { "type": "players", "enabled": true, "order": 3 }, + { "type": "announcements", "enabled": false, "order": 4 }, + { "type": "reviews", "enabled": true, "order": 5 } + ] +} +``` +**响应示例:店铺收入统计** +```json +// GET /api/v1/shops/:id/income-stats +{ + "monthlyIncome": 12800.00, + "pendingSettlement": 3200.00, + "totalWithdrawn": 54000.00, + "totalOrders": 156, + "completedOrders": 142 +} +``` +--- + +## 8. 订单系统 + +### 8.1 订单状态机 (Order State Machine) + +| 当前状态 | 触发动作 | 下一状态 | 允许的角色 | 副作用 | +| ----------------- | ----------------------------- | ---------------- | ---------------- | -------------------------------------------------- | +| `pending_payment` | `PAY` | `pending_accept` | consumer | CLEAR_TIMEOUT, SCHEDULE_TIMEOUT | +| `pending_accept` | `ACCEPT` | `in_progress` | player, owner | CLEAR_TIMEOUT, SYNC_CHAT_SESSION | +| `pending_accept` | `CANCEL_PRE_ACCEPT` | `cancelled` | consumer | CLEAR_TIMEOUT | +| `pending_accept` | `AUTO_TIMEOUT_PENDING_ACCEPT` | `cancelled` | system | CLEAR_TIMEOUT | +| `in_progress` | `REQUEST_CLOSE` | `pending_close` | consumer, player | CLEAR_TIMEOUT, SCHEDULE_TIMEOUT, SYNC_CHAT_SESSION | +| `in_progress` | `OPEN_DISPUTE` | `disputed` | consumer, player | CLEAR_TIMEOUT, SYNC_CHAT_SESSION | +| `pending_close` | `CONFIRM_CLOSE` | `pending_review` | consumer, player | CLEAR_TIMEOUT, SCHEDULE_TIMEOUT, SYNC_CHAT_SESSION | +| `pending_close` | `OPEN_DISPUTE` | `disputed` | consumer, player | CLEAR_TIMEOUT, SYNC_CHAT_SESSION | +| `pending_close` | `AUTO_TIMEOUT_PENDING_CLOSE` | `pending_review` | system | CLEAR_TIMEOUT, SCHEDULE_TIMEOUT, SYNC_CHAT_SESSION | +| `pending_review` | `SUBMIT_REVIEW` | `completed` | consumer, player | CLEAR_TIMEOUT, PAYOUT_INCOME, SYNC_CHAT_SESSION | +| `pending_review` | `AUTO_TIMEOUT_PENDING_REVIEW` | `completed` | system | CLEAR_TIMEOUT, PAYOUT_INCOME, SYNC_CHAT_SESSION | +| `disputed` | `RESOLVE_DISPUTE` | `pending_review` | owner, admin | CLEAR_TIMEOUT, SCHEDULE_TIMEOUT, SYNC_CHAT_SESSION | + +### 8.2 超时配置 (Timeout Configs) +后端需实现定时任务(如 Redis 延迟队列)来处理以下超时逻辑: +- `ORDER_ACCEPT_TIMEOUT_MS`: 待接单超时自动取消(默认 5 分钟)。 +- `ORDER_CLOSE_TIMEOUT_MS`: 申请结算后对方未确认,超时自动进入待评价(默认 24 小时)。 +- `ORDER_REVIEW_TIMEOUT_MS`: 待评价超时自动好评并完成订单(默认 72 小时)。 + +### 8.3 订单接口 + +| 方法 | 路径 | 权限 | 说明 | +| ---- | --------------------------- | ---- | ----------------------------------- | +| GET | `/orders` | 🔒 | 获取当前用户的订单列表 | +| GET | `/orders/:id` | 🔒 | 获取订单详情 | +| POST | `/orders` | 🔒 | 创建订单 (状态: `pending_payment`) | +| POST | `/orders/paid` | 🔒 | 创建并直接支付订单 (快捷下单) | +| POST | `/orders/:id/pay` | 🔒 | 支付订单 (`PAY`) | +| POST | `/orders/:id/accept` | 🔒 | 接单 (`ACCEPT`) | +| POST | `/orders/:id/request-close` | 🔒 | 申请结算 (`REQUEST_CLOSE`) | +| POST | `/orders/:id/confirm-close` | 🔒 | 确认结算 (`CONFIRM_CLOSE`) | +| POST | `/orders/:id/cancel` | 🔒 | 接单前取消 (`CANCEL_PRE_ACCEPT`) | +| POST | `/orders/:id/reorder` | 🔒 | 再来一单 (基于原订单快速创建新订单) | +`GET /orders` 支持查询参数:`role` (consumer/player/owner)、`status` (状态过滤)、`offset`、`limit`。后端根据 Token 中的 userId 和 role 参数自动过滤,不允许查看他人订单。 + +**请求示例:创建并支付订单 (快捷下单)** +```json +// POST /api/v1/orders/paid +{ + "playerId": "p1", + "shopId": "shop1", + "serviceId": "svc1", + "quantity": 3, + "note": "希望晚上8点后开始" +} +``` +**响应示例:订单创建成功** +```json +{ + "ok": true, + "order": { + "id": "ord-20240320-001", + "consumerId": "u1", + "consumerName": "张三", + "playerId": "p1", + "playerName": "小明", + "shopId": "shop1", + "shopName": "星耀电竞", + "service": { "id": "svc1", "title": "英雄联盟代练", "price": 50, "unit": "局" }, + "status": "pending_accept", + "totalPrice": 150, + "createdAt": "2024-03-20T12:00:00.000Z" + } +} +``` +**请求示例:再来一单** +```json +// POST /api/v1/orders/:id/reorder +// 无请求体,后端基于原订单的 playerId、serviceId、shopId 创建新订单 +``` +--- + +## 9. 争议仲裁 + +| 方法 | 路径 | 权限 | 说明 | +| ---- | ------------------------ | ---- | ------------------------- | +| GET | `/disputes` | 🔒 | 获取当前用户的争议列表 | +| GET | `/orders/:id/dispute` | 🔒 | 获取指定订单的争议详情 | +| POST | `/orders/:id/dispute` | 🔒 | 发起争议 (`OPEN_DISPUTE`) | +| POST | `/disputes/:id/response` | 🔒 | 被诉方提交回应及证据 | +| POST | `/disputes/:id/appeal` | 🔒 | 对仲裁结果发起申诉 | + +**争议处理时间线 (Timeline)**: +- `created`: 争议发起 +- `response`: 被诉方回应 +- `reviewing`: 店长/平台介入审查 +- `resolved`: 给出仲裁结果 +- `appealed`: 发起申诉,转交平台管理员 +**请求示例:发起争议** +```json +// POST /api/v1/orders/:id/dispute +{ + "reason": "打手未按约定时间上线", + "evidence": [ + "https://cdn.juwan.com/uploads/evidence1.jpg", + "https://cdn.juwan.com/uploads/evidence2.jpg" + ] +} +``` +**请求示例:被诉方回应** +```json +// POST /api/v1/disputes/:id/response +{ + "reason": "当时网络故障,已与客户沟通并补时", + "evidence": ["https://cdn.juwan.com/uploads/response1.jpg"] +} +``` +**请求示例:申诉** +```json +// POST /api/v1/disputes/:id/appeal +{ + "reason": "仲裁结果不合理,请求平台复核" +} +``` + +--- + +## 10. 评价系统 + +**密封机制 (Sealed Mechanics)**: +评价提交后默认为 `sealed: true`。只有当双方都完成评价,或者评价超时(`ORDER_REVIEW_TIMEOUT_MS`)后,评价才会解封并公开显示。 + +| 方法 | 路径 | 权限 | 说明 | +| ---- | --------------------- | ---- | ------------------------------------ | +| POST | `/orders/:id/review` | 🔒 | 提交评价 (`SUBMIT_REVIEW`) | +| GET | `/reviews` | | 获取公开评价列表 | +| GET | `/orders/:id/reviews` | 🔒 | 获取指定订单的评价(受密封机制限制) | +| GET | `/users/:id/reviews` | | 获取指定用户收到的评价 | + +--- + +## 11. 聊天系统 + +### 11.1 会话类型 +- **`order`**: 订单会话。随订单创建而建立,订单完成后变为只读。 +- **`consultation`**: 咨询会话。用户在下单前与打手沟通。24小时无回复自动关闭。可升级为订单会话。 + +| 方法 | 路径 | 权限 | 说明 | +| ---- | ----------------------------- | ---- | ----------------------------------- | +| GET | `/chat/sessions` | 🔒 | 获取当前用户的会话列表 | +| GET | `/chat/sessions/:id` | 🔒 | 获取会话详情 | +| GET | `/chat/sessions/:id/messages` | 🔒 | 获取会话历史消息 (分页) | +| POST | `/chat/sessions/order` | 🔒 | 确保订单会话存在 (不存在则创建) | +| POST | `/chat/sessions/consultation` | 🔒 | 创建咨询会话 | +| POST | `/chat/sessions/:id/upgrade` | 🔒 | 将咨询会话升级为订单会话 | +| POST | `/chat/sessions/:id/messages` | 🔒 | 发送消息 (文本/图片) | +| GET | `/shops/:id/chat-sessions` | 🔒 | 店长查看员工的业务会话 (需员工同意) | + +--- + +## 12. 社区系统 + +| 方法 | 路径 | 权限 | 说明 | +| ------ | --------------------- | ---- | ------------------------------------- | +| GET | `/posts` | | 获取帖子列表 (支持分页、标签筛选) | +| GET | `/posts/:id` | | 获取帖子详情 | +| POST | `/posts` | 🔒 | 发布帖子 (支持普通帖、秀单帖、引用帖) | +| POST | `/posts/:id/like` | 🔒 | 点赞帖子 | +| DELETE | `/posts/:id/like` | 🔒 | 取消点赞帖子 | +| POST | `/posts/:id/pin` | 🔒 | 作者置顶帖子 (最多 N 条) | +| DELETE | `/posts/:id/pin` | 🔒 | 取消置顶 | +| GET | `/posts/:id/comments` | | 获取帖子评论列表 | +| POST | `/posts/:id/comments` | 🔒 | 发表评论 | +| POST | `/comments/:id/like` | 🔒 | 点赞评论 | +| DELETE | `/comments/:id/like` | 🔒 | 取消点赞评论 | +| GET | `/users/:id/posts` | | 获取指定用户的帖子列表 | +`GET /posts` 支持查询参数:`tags` (标签过滤)、`gameId` (游戏过滤)、`sortBy` (new/hot)、`offset`、`limit`。 +**请求示例:发布秀单帖** +```json +// POST /api/v1/posts +{ + "title": "超棒的代练体验", + "content": "从银到铂金,只用了三天!", + "images": ["https://cdn.juwan.com/uploads/post1.jpg"], + "tags": ["英雄联盟", "代练"], + "linkedOrderId": "ord-20240320-001" +} +``` + +--- + +## 13. 收藏与关注 + +| 方法 | 路径 | 权限 | 说明 | +| ------ | ---------------------------- | ---- | ---------------------------------- | +| GET | `/favorites` | 🔒 | 获取当前用户的收藏列表 (打手/店铺) | +| POST | `/favorites` | 🔒 | 添加收藏 | +| DELETE | `/favorites/:id` | 🔒 | 取消收藏 | +| GET | `/users/:id/favorites/check` | 🔒 | 检查是否已收藏指定目标 | +**请求示例:添加收藏** +```json +// POST /api/v1/favorites +{ "targetType": "player", "targetId": "p1" } +``` +**检查收藏状态** +``` +GET /api/v1/users/u1/favorites/check?targetType=player&targetId=p1 +→ { "favorited": true } +``` + +--- + +## 14. 搜索与发现 + +| 方法 | 路径 | 权限 | 说明 | +| ---- | --------- | ---- | ------------ | +| GET | `/search` | | 统一搜索接口 | + +**请求参数**: +- `q`: 搜索关键词 +- `selectedGames`: 游戏 ID 数组 +- `min`, `max`: 价格区间 +- `onlyOnline`: 是否仅看在线 (`true`/`false`) +- `minRating`: 最低评分 +- `sort`: 排序方式 (`composite`, `rating`, `orders`, `price_asc`, `price_desc`) +- `offset`, `limit`: 分页参数 +| GET | `/recommendations/home` | | 首页推荐信息流 (混合打手与店铺卡片) | +**响应示例:搜索结果** +```json +{ + "items": [ + { + "type": "player", + "player": { "id": "p1", "user": {...}, "rating": 4.8, "status": "available", ... }, + "minPrice": 30, + "unit": "局", + "rating": 4.8, + "orders": 256 + }, + { + "type": "shop", + "shop": { "id": "shop1", "name": "星耀电竞", ... }, + "minPrice": 25, + "unit": "局", + "rating": 4.6, + "orders": 1024, + "games": ["英雄联盟", "CS2"], + "hasAvailable": true + } + ], + "meta": { "total": 42, "offset": 0, "limit": 12 } +} +``` +--- + +## 15. 钱包与资金 + +### 15.1 收入计算公式 (Income Calculation) +订单完成后,系统根据店铺规则计算打手实际收入: +- **无店铺 (独立接单)**: `income = totalPrice` +- **比例抽成 (percentage)**: `income = totalPrice * (1 - commissionValue / 100)` +- **固定抽成 (fixed)**: `income = Math.max(0, totalPrice - commissionValue)` +剩余部分作为店铺收入(若有店铺)。 + +### 15.2 钱包接口 + +| 方法 | 路径 | 权限 | 说明 | +| ---- | ---------------------- | ---- | ------------------------------------- | +| GET | `/wallet/balance` | 🔒 | 获取当前余额 | +| GET | `/wallet/transactions` | 🔒 | 获取资金流水 (包含结构化的 `orderId`) | +| POST | `/wallet/topup` | 🔒 | 充值 | +| POST | `/wallet/withdraw` | 🔒 | 提现 | + +--- + +## 16. 通知系统 + +| 方法 | 路径 | 权限 | 说明 | +| ---- | ---------------------------------- | ---- | ------------------ | +| GET | `/notifications` | 🔒 | 获取通知列表 | +| PUT | `/notifications/:id/read` | 🔒 | 标记单条通知为已读 | +| PUT | `/notifications/read-all` | 🔒 | 标记所有通知为已读 | +| POST | `/notifications/push-subscription` | 🔒 | 订阅 Web Push 推送 | + +--- + +## 17. 管理后台 (Admin) + +| 方法 | 路径 | 权限 | 说明 | +| ---- | ---------------------------------- | ---- | -------------------------- | +| GET | `/admin/verifications` | 🛡️ | 获取待审核的认证申请 | +| POST | `/admin/verifications/:id/approve` | 🛡️ | 批准认证申请 | +| POST | `/admin/verifications/:id/reject` | 🛡️ | 拒绝认证申请 | +| GET | `/admin/disputes` | 🛡️ | 获取需要平台介入的争议列表 | +| POST | `/admin/disputes/:id/resolve` | 🛡️ | 平台管理员给出最终仲裁结果 | + +--- + +## 18. WebSocket 事件 + +客户端连接到 `wss://api.juwan.com/api/v1/ws`,通过 JWT 鉴权。 + +**下发事件类型**: +- `chat:message`: 收到新聊天消息。 +- `order:status_changed`: 订单状态变更(触发前端重新拉取订单详情)。 +- `notification:new`: 收到新系统通知。 +- `dispute:updated`: 争议状态或时间线更新。 +- `wallet:balance_changed`: 余额变动通知。 + +--- + +## 19. 安全与校验清单 + +1. **数据隔离 (Data Isolation)**: + - 用户只能查询自己的订单、钱包流水、私聊会话。 + - 店长只能查询本店铺的订单和员工数据。 +2. **并发控制 (Concurrency Control)**: + - 订单状态流转必须使用乐观锁或数据库事务,防止并发导致状态机错乱。 + - 钱包扣款必须保证原子性,防止超扣。 +3. **权限校验 (Authorization)**: + - 严格校验 `Actor` 的 `role`。例如,只有 `player` 才能创建服务,只有 `owner` 才能修改店铺规则。 + - 订单操作必须校验操作者是否为该订单的 `consumerId`、`playerId` 或关联的 `shopId` 的店长。 +4. **幂等性 (Idempotency)**: + - 支付、接单、结算等核心操作必须保证幂等性,重复请求返回 `IDEMPOTENT_NOOP`。