同步订单改造文档并标记交易中商品
This commit is contained in:
+5
-3
@@ -7,7 +7,7 @@ API 规划以 [项目计划](project-plan.md) 第 10 章为准。
|
|||||||
- 认证:短信验证码、手机号登录、Token 刷新、退出登录。
|
- 认证:短信验证码、手机号登录、Token 刷新、退出登录。
|
||||||
- 实名:发起认证、查询状态、服务回调。
|
- 实名:发起认证、查询状态、服务回调。
|
||||||
- 发布:创建、修改、提交审核、下架、列表、详情。
|
- 发布:创建、修改、提交审核、下架、列表、详情。
|
||||||
- 订单:创建、取消、确认收号、提交归还、确认归还、发起申诉。
|
- 订单:创建、取消、确认收号、发起结账、确认结账、修改结账、确认/拒绝修正、发起申诉。
|
||||||
- 交接:提交交接说明、查看交接记录。
|
- 交接:提交交接说明、查看交接记录。
|
||||||
- 钱包:余额、流水、提现预留。
|
- 钱包:余额、流水、提现预留。
|
||||||
- 通知:站内信列表、标记已读。
|
- 通知:站内信列表、标记已读。
|
||||||
@@ -37,8 +37,10 @@ API 规划以 [项目计划](project-plan.md) 第 10 章为准。
|
|||||||
- `POST /api/orders/{id}/handoff`
|
- `POST /api/orders/{id}/handoff`
|
||||||
- `GET /api/orders/{id}/handoff-records`
|
- `GET /api/orders/{id}/handoff-records`
|
||||||
- `POST /api/orders/{id}/confirm-receive`
|
- `POST /api/orders/{id}/confirm-receive`
|
||||||
- `POST /api/orders/{id}/return`
|
- `POST /api/orders/{id}/checkout`
|
||||||
- `POST /api/orders/{id}/confirm-return`
|
- `POST /api/orders/{id}/checkout/confirm`
|
||||||
|
- `POST /api/orders/{id}/checkout/counter`
|
||||||
|
- `POST /api/orders/{id}/checkout/accept`
|
||||||
- `POST /api/orders/{id}/dispute`
|
- `POST /api/orders/{id}/dispute`
|
||||||
- `GET /api/disputes`
|
- `GET /api/disputes`
|
||||||
- `GET /api/disputes/{id}`
|
- `GET /api/disputes/{id}`
|
||||||
|
|||||||
+18
-13
@@ -49,24 +49,27 @@
|
|||||||
- 确认收号时会重新计算租赁开始时间和结束时间。
|
- 确认收号时会重新计算租赁开始时间和结束时间。
|
||||||
- 交接记录保存在 `handoff_records`,订单双方都可以查看。
|
- 交接记录保存在 `handoff_records`,订单双方都可以查看。
|
||||||
|
|
||||||
## 开发态归还与完成
|
## 开发态结账与完成
|
||||||
|
|
||||||
- 租赁中订单可以由租客提交归还。
|
- 租赁中订单可以由租客发起结账,填写使用结束说明、消耗金额、哈夫币消耗量、其他扣款和证据链接。
|
||||||
- 逾期中订单仍允许租客提交归还,但后台和号主可以根据逾期情况发起申诉或客服处理。
|
- 逾期中订单仍允许租客发起结账,但后台和号主可以根据逾期情况发起申诉或客服处理。
|
||||||
- 租客提交归还后,订单状态变为 `pending_return_confirm`,交接状态变为 `pending_owner_return_confirm`。
|
- 租客发起结账后,订单状态变为 `pending_checkout_confirm`,交接状态变为 `pending_owner_checkout`。
|
||||||
- 只有号主可以确认归还。
|
- 只有号主可以确认结账或修改结账。
|
||||||
- 号主确认归还后,订单状态变为 `completed`,交接状态变为 `returned`,结算状态先标记为 `settled`。
|
- 号主直接确认结账后,订单状态变为 `completed`,交接状态变为 `returned`,结算状态标记为 `settled`。
|
||||||
|
- 号主修改结账后,订单状态变为 `pending_checkout_accept`,交接状态变为 `pending_renter_checkout`,必须等待租客确认修正。
|
||||||
|
- 待号主确认结账或待租客确认修正时,任一方都可以发起结账争议。
|
||||||
|
- 结账争议后订单状态变为 `checkout_disputing`,交接状态变为 `checkout_disputed`,由客服仲裁。
|
||||||
- 订单完成后,账号和发布状态恢复为 `published`。
|
- 订单完成后,账号和发布状态恢复为 `published`。
|
||||||
- 当前会生成开发态模拟钱包流水,不代表真实支付或提现。
|
- 当前会生成开发态模拟钱包流水,不代表真实支付或提现。
|
||||||
|
|
||||||
## 开发态超时任务
|
## 开发态超时任务
|
||||||
|
|
||||||
- 后端启动后会运行订单超时扫描任务,默认每 1 分钟执行一次。
|
- 后端启动后会运行订单超时扫描任务,默认每 1 分钟执行一次。
|
||||||
- 任务从 `system_configs` 读取超时阈值,包括号主待交接、租客确认收号、租客归还宽限和号主确认归还。
|
- 任务从 `system_configs` 读取超时阈值,包括号主待交接、租客确认收号、租客结账宽限和号主确认结账。
|
||||||
- 号主待交接超时:订单保持 `pending_handoff`,交接状态变为 `owner_timeout`,租客仍可取消订单或发起申诉。
|
- 号主待交接超时:订单保持 `pending_handoff`,交接状态变为 `owner_timeout`,租客仍可取消订单或发起申诉。
|
||||||
- 租客确认收号超时:订单状态变为 `abnormal`,交接状态变为 `renter_confirm_timeout`,进入客服介入。
|
- 租客确认收号超时:订单状态变为 `abnormal`,交接状态变为 `renter_confirm_timeout`,进入客服介入。
|
||||||
- 租客逾期未归还:订单状态变为 `overdue`,交接状态变为 `return_overdue`,租客仍可提交归还,号主可发起申诉。
|
- 租客逾期未结账:订单状态变为 `overdue`,交接状态变为 `return_overdue`,租客仍可发起结账,号主可发起申诉。
|
||||||
- 号主确认归还超时:订单状态变为 `abnormal`,交接状态变为 `owner_return_confirm_timeout`,进入客服复核。
|
- 号主确认结账超时:订单状态变为 `abnormal`,交接状态变为 `owner_checkout_confirm_timeout`,进入客服复核。
|
||||||
- 每个超时动作只推进一次状态,避免重复通知。
|
- 每个超时动作只推进一次状态,避免重复通知。
|
||||||
- 超时动作会给相关用户写入站内信,并以 `system` 身份写入 `audit_logs`。
|
- 超时动作会给相关用户写入站内信,并以 `system` 身份写入 `audit_logs`。
|
||||||
|
|
||||||
@@ -97,8 +100,10 @@
|
|||||||
- 租客取消订单后通知号主和租客。
|
- 租客取消订单后通知号主和租客。
|
||||||
- 号主提交交接说明后通知租客。
|
- 号主提交交接说明后通知租客。
|
||||||
- 租客确认收号后通知号主。
|
- 租客确认收号后通知号主。
|
||||||
- 租客提交归还后通知号主。
|
- 租客发起结账后通知号主。
|
||||||
- 号主确认归还后通知号主和租客。
|
- 号主确认结账后通知号主和租客。
|
||||||
|
- 号主修改结账后通知租客。
|
||||||
|
- 租客拒绝修正结账后通知双方并进入争议。
|
||||||
- 发起申诉后通知对方和发起人。
|
- 发起申诉后通知对方和发起人。
|
||||||
- 仲裁完成后通知号主和租客。
|
- 仲裁完成后通知号主和租客。
|
||||||
- 站内信支持列表查询和标记已读。
|
- 站内信支持列表查询和标记已读。
|
||||||
@@ -107,7 +112,7 @@
|
|||||||
|
|
||||||
- 订单双方都可以在非终态订单发起申诉。
|
- 订单双方都可以在非终态订单发起申诉。
|
||||||
- 同一个订单同一时间只允许存在一个 `open` 或 `processing` 申诉。
|
- 同一个订单同一时间只允许存在一个 `open` 或 `processing` 申诉。
|
||||||
- 发起申诉后订单状态变为 `disputing`,账号和发布保持锁定。
|
- 发起普通申诉后订单状态变为 `disputing`,发起结账争议后订单状态变为 `checkout_disputing`,账号和发布保持锁定。
|
||||||
- 开发态后台仲裁接口为 `/api/admin/disputes`,当前只要求登录,后续接后台管理员和 RBAC。
|
- 开发态后台仲裁接口为 `/api/admin/disputes`,当前只要求登录,后续接后台管理员和 RBAC。
|
||||||
- 仲裁会释放开发态模拟冻结金额,并根据结果生成钱包流水。
|
- 仲裁会释放开发态模拟冻结金额,并根据结果生成钱包流水。
|
||||||
- 全额退款:释放冻结金额后,将租金加押金作为可用余额退给租客,订单置为 `closed`。
|
- 全额退款:释放冻结金额后,将租金加押金作为可用余额退给租客,订单置为 `closed`。
|
||||||
@@ -139,7 +144,7 @@
|
|||||||
|
|
||||||
- 后台仪表盘接口为 `/api/admin/dashboard`,前端页面为 `/admin/dashboard`。
|
- 后台仪表盘接口为 `/api/admin/dashboard`,前端页面为 `/admin/dashboard`。
|
||||||
- 当前统计用户数、实名用户数、商品数、上架数、订单数、租赁中订单、今日订单、今日钱包流水。
|
- 当前统计用户数、实名用户数、商品数、上架数、订单数、租赁中订单、今日订单、今日钱包流水。
|
||||||
- 待处理事项包括待审核商品、待仲裁申诉、待交接订单和待归还确认订单。
|
- 待处理事项包括待审核商品、待仲裁申诉、待交接订单和待结账确认订单。
|
||||||
- 最近订单和最近申诉用于运营快速定位问题,后续接入后台订单管理和商品审核后再跳转到对应详情页。
|
- 最近订单和最近申诉用于运营快速定位问题,后续接入后台订单管理和商品审核后再跳转到对应详情页。
|
||||||
|
|
||||||
## 开发态用户管理
|
## 开发态用户管理
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# 订单支付确认改造 —— 实施计划
|
||||||
|
|
||||||
|
> 状态:核心订单支付链路已按分步兼容策略实施;旧字段暂保留兼容
|
||||||
|
> 日期:2026-05-24
|
||||||
|
|
||||||
|
说明:本文来自改造前计划,用于保留决策背景。当前分支没有直接删除旧价格/租期字段,也没有删除增量迁移文件;为了避免影响已有页面和开发库,采用 `price`、`rented_at` 等新字段优先、旧字段兜底的兼容迁移方式。
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
将当前"下单即锁定"的流程改造为:
|
||||||
|
|
||||||
|
```
|
||||||
|
下单 → 待支付(pending_payment) → 支付确认 → 冻结锁定 → 待交接(pending_handoff)
|
||||||
|
```
|
||||||
|
|
||||||
|
同时清理 `price_hourly`、`price_daily`、`price_weekly`、`rent_start_at`、`rent_end_at`、`rent_hours` 等时租残留字段。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 改造清单(按依赖顺序)
|
||||||
|
|
||||||
|
```
|
||||||
|
第1层(无依赖,可并行)
|
||||||
|
├── A. 合并 3 个 SQL 迁移文件 → 1 个 000001_init.sql
|
||||||
|
│ 修改 rental_listings:price + in_transaction
|
||||||
|
│ 修改 rental_orders:rented_at + estimated_duration_hours
|
||||||
|
│ 修改 notifications:category/link_type/link_id/extra_data
|
||||||
|
│ 新增 sms_logs 表
|
||||||
|
│ 同步改 scripts/dev.sh
|
||||||
|
│
|
||||||
|
├── B. Go 模型改造
|
||||||
|
│ model/listing.go → Price + InTransaction
|
||||||
|
│ model/order.go → RentedAt + EstimatedDurationHours
|
||||||
|
│ model/notification.go → Category/LinkType/LinkID/ExtraData
|
||||||
|
│
|
||||||
|
第2层(依赖第1层)
|
||||||
|
├── C. Listing 模块清理
|
||||||
|
│ listing/dto.go → ListingDTO + CreateRequest 适配
|
||||||
|
│ listing/repository.go → Create/Update/listQuery/toDTO 全部替换
|
||||||
|
│ listing/service.go → 校验适配
|
||||||
|
│
|
||||||
|
├── D. Order DTO 改造
|
||||||
|
│ order/dto.go → 删 RentStartAt/RentEndAt/RentHours,加 RentedAt/EstimatedDurationHours
|
||||||
|
│
|
||||||
|
第3层(依赖第2层)
|
||||||
|
├── E. Order 核心改造
|
||||||
|
│ order/service.go → 删 internalOrderHours,加错误,加 Pay()
|
||||||
|
│ order/repository.go → Create() → pending_payment + in_transaction
|
||||||
|
│ order/repository.go → 新增 Pay():余额→扣款→冻结→锁定→通知
|
||||||
|
│ order/repository.go → Cancel() 适配 pending_payment
|
||||||
|
│ order/handler.go → 新增 Pay handler
|
||||||
|
│ 路由注册 → POST /orders/:id/pay
|
||||||
|
│
|
||||||
|
├── F. 钱包充值
|
||||||
|
│ wallet/repository.go → Recharge()
|
||||||
|
│ wallet/service.go → Recharge()
|
||||||
|
│ wallet/handler.go → Recharge handler
|
||||||
|
│ 路由注册 → POST /wallet/recharge
|
||||||
|
│
|
||||||
|
第4层(依赖第3层 E)
|
||||||
|
└── G. 超时扫描
|
||||||
|
job.go → 新增 handlePendingPaymentTimeout(15min 超时取消)
|
||||||
|
system_configs → order.pending_payment_timeout_minutes: 15
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API 变更
|
||||||
|
|
||||||
|
| API | 类型 | 说明 |
|
||||||
|
|-----|------|------|
|
||||||
|
| `POST /api/orders` | 行为变更 | 返回 `pending_payment`,不锁商品,标记 `in_transaction` |
|
||||||
|
| `POST /api/orders/{id}/pay` | **新增** | 校验余额→扣款→冻结→锁定→通知 |
|
||||||
|
| `POST /api/orders/{id}/cancel` | 行为变更 | 允许取消 `pending_payment` |
|
||||||
|
| `POST /api/wallet/recharge` | **新增** | 开发环境充值 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 涉及文件
|
||||||
|
|
||||||
|
### 修改(17 个)
|
||||||
|
```
|
||||||
|
backend/migrations/000001_init.sql
|
||||||
|
scripts/dev.sh
|
||||||
|
backend/internal/model/listing.go
|
||||||
|
backend/internal/model/order.go
|
||||||
|
backend/internal/model/notification.go
|
||||||
|
backend/internal/modules/listing/dto.go
|
||||||
|
backend/internal/modules/listing/repository.go
|
||||||
|
backend/internal/modules/listing/service.go
|
||||||
|
backend/internal/modules/order/dto.go
|
||||||
|
backend/internal/modules/order/service.go
|
||||||
|
backend/internal/modules/order/repository.go
|
||||||
|
backend/internal/modules/order/handler.go
|
||||||
|
backend/internal/modules/wallet/repository.go
|
||||||
|
backend/internal/modules/wallet/service.go
|
||||||
|
backend/internal/modules/wallet/handler.go
|
||||||
|
backend/internal/router/router.go(或同等路由文件)
|
||||||
|
backend/internal/jobs/ordertimeout/job.go
|
||||||
|
backend/internal/modules/systemconfig/defaults.go(或同等文件)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 删除(2 个)
|
||||||
|
```
|
||||||
|
backend/migrations/000002_order_checkouts.sql
|
||||||
|
backend/migrations/000003_add_indexes.sql
|
||||||
|
```
|
||||||
@@ -0,0 +1,511 @@
|
|||||||
|
# HFB Sys 订单购买测试 & 消息系统增强 —— 综合分析报告
|
||||||
|
|
||||||
|
> 生成日期:2026-05-24
|
||||||
|
> 状态:订单支付与开发充值主链路已实施;消息系统增强留待后续
|
||||||
|
|
||||||
|
说明:本文来自改造前分析,用于保留问题背景和测试思路。文中对“当前链路”的描述对应改造前状态;当前分支已接入 `pending_payment`、支付冻结、开发充值、结账确认等订单系统能力,消息系统增强仍未纳入本轮改造。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第一部分:如何进行实际的订单购买测试
|
||||||
|
|
||||||
|
### 1. 当前完整订单购买链路
|
||||||
|
|
||||||
|
从前端到后端,当前链路如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
租客浏览商品列表 → 点击商品详情 → 点击"立即租赁"
|
||||||
|
↓
|
||||||
|
MobileListingDetailView.vue 调用 createOrder(listingId)
|
||||||
|
↓
|
||||||
|
POST /api/orders {listing_id}
|
||||||
|
↓
|
||||||
|
handler.CreateOrder() → 验证 listing 状态(published & approved)
|
||||||
|
↓
|
||||||
|
service.CreateOrder(ctx, userID, listingID)
|
||||||
|
↓
|
||||||
|
repository.Create(tx) → 一条大事务:
|
||||||
|
├── SELECT listing + account FOR UPDATE (悲观锁防并发)
|
||||||
|
├── 校验: listing.status=published, account.status=published
|
||||||
|
├── INSERT rental_orders (status=pending_handoff, rent_hours=24 硬编码)
|
||||||
|
├── UPDATE listing.status → rented, account.status → rented
|
||||||
|
├── INSERT account_snapshot (JSON 字段, 记录下单时资产快照)
|
||||||
|
├── INSERT wallet_ledger (order_lock, 模拟冻结 rent_amount + deposit_amount)
|
||||||
|
├── UPDATE wallet_accounts (frozen_balance ↑)
|
||||||
|
├── notification.Append() → 通知号主+租客
|
||||||
|
└── COMMIT
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键发现——无支付环节**:`POST /api/orders` 调用后直接进入 `pending_handoff`,完全没有支付确认步骤。这是因为项目计划明确说"一期先做账务模型,不直接接真实支付和提现"。
|
||||||
|
|
||||||
|
### 2. 当前存在的 9 个关键缺失
|
||||||
|
|
||||||
|
| # | 缺失项 | 影响 | 涉及文件 |
|
||||||
|
|---|--------|------|----------|
|
||||||
|
| 1 | **无支付确认步骤** | 订单创建即生效,无资金冻结验证 | `order/repository.go` Create() |
|
||||||
|
| 2 | **租期硬编码 24h** | `internalOrderHours = 24`,用户无法选择 | `order/repository.go:21` |
|
||||||
|
| 3 | **无余额校验** | 下单时未检查租客 `available_balance >= rent_amount + deposit_amount` | `order/repository.go` Create() |
|
||||||
|
| 4 | **前端余额硬编码** | `MobileProfileView.vue` 写死 `¥0.00`,未调 API | `MobileProfileView.vue` |
|
||||||
|
| 5 | **钱包首次查询才创建** | `GET /api/wallet/balance` 首次访问才初始化账户 | `wallet/repository.go` |
|
||||||
|
| 6 | **无充值入口** | 没有任何充值接口或页面 | 全项目 |
|
||||||
|
| 7 | **站内信前端空壳** | `MobileMessagesView.vue` 只显示 `van-empty` | `MobileMessagesView.vue` |
|
||||||
|
| 8 | **无租期即将结束提醒** | 项目计划中规划了但未实现 | 超时扫描任务 |
|
||||||
|
| 9 | **短信完全未接** | 只有登录验证码的 mock,业务通知短信完全缺失 | `integrations/sms/` |
|
||||||
|
|
||||||
|
### 3. 要完成端到端测试必须补充的最小功能集
|
||||||
|
|
||||||
|
```
|
||||||
|
必须在测试前补充的功能(按依赖顺序):
|
||||||
|
|
||||||
|
第1步:余额充值(测试前置条件)
|
||||||
|
└── POST /api/wallet/recharge {amount} → 给测试用户加余额
|
||||||
|
|
||||||
|
第2步:租期选择(核心流程)
|
||||||
|
└── 前端:下单时增加租期选择组件
|
||||||
|
└── 后端:CreateOrderDTO 增加 rent_hours 字段
|
||||||
|
|
||||||
|
第3步:支付确认步骤(状态机改造)
|
||||||
|
└── 新增订单状态:pending_payment
|
||||||
|
└── POST /api/orders → pending_payment(而非直接 pending_handoff)
|
||||||
|
└── POST /api/orders/{id}/pay → 校验余额 → 扣款 → pending_handoff
|
||||||
|
|
||||||
|
第4步:前端站内信接入
|
||||||
|
└── MobileMessagesView.vue 接入 fetchNotifications API
|
||||||
|
└── 底部导航栏增加未读消息角标
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 支付确认步骤的推荐设计
|
||||||
|
|
||||||
|
```
|
||||||
|
建议的状态机改造(最小侵入):
|
||||||
|
|
||||||
|
POST /api/orders
|
||||||
|
↓
|
||||||
|
[新增] pending_payment(待支付,默认 15 分钟超时自动取消)
|
||||||
|
↓
|
||||||
|
POST /api/orders/{id}/pay
|
||||||
|
├── 校验:订单归属租客、status=pending_payment、未超时
|
||||||
|
├── 校验:wallet_accounts.available_balance >= rent_amount + deposit_amount
|
||||||
|
├── UPDATE wallet_accounts: available_balance -= total, frozen_balance += total
|
||||||
|
├── INSERT wallet_ledger: order_lock (冻结流水)
|
||||||
|
├── UPDATE rental_orders: status → pending_handoff
|
||||||
|
├── UPDATE listing + account: status → rented
|
||||||
|
└── notification.Append() → 通知双方
|
||||||
|
```
|
||||||
|
|
||||||
|
对应的新 API:
|
||||||
|
- `POST /api/orders/{id}/pay` — 确认支付
|
||||||
|
- `GET /api/orders/{id}/payment-status` — 查询支付状态
|
||||||
|
- 超时扫描任务新增:`pending_payment` 超过 15 分钟 → 自动取消
|
||||||
|
|
||||||
|
### 5. 完整测试场景清单
|
||||||
|
|
||||||
|
#### 5.1 正常流程测试
|
||||||
|
|
||||||
|
| 场景 | 步骤 | 验证点 |
|
||||||
|
|------|------|--------|
|
||||||
|
| 完整租赁闭环 | 浏览→下单→支付→号主交接→收号确认→租赁→结账→号主确认→完成 | 所有状态流转正确,资金流水完整 |
|
||||||
|
| 租期选择 | 选择不同租期(1h/3h/12h/24h) | rent_end_at 正确计算,租金按比例 |
|
||||||
|
| 多商品下单 | 不同商品分别下单 | 互不影响,各自锁定 |
|
||||||
|
| 结账修改流程 | 号主修改结账金额→租客同意修正 | 状态流转:pending_checkout_confirm → pending_checkout_accept → completed |
|
||||||
|
|
||||||
|
#### 5.2 边界与异常测试
|
||||||
|
|
||||||
|
| 场景 | 验证点 |
|
||||||
|
|------|--------|
|
||||||
|
| 余额不足下单 | 返回明确错误,不创建订单 |
|
||||||
|
| 已租出商品下单 | `SELECT FOR UPDATE` 后检测到 status≠published,拒绝 |
|
||||||
|
| 审核未通过商品下单 | 拒绝(review_status≠approved) |
|
||||||
|
| 自己租自己 | 拒绝(owner_id = renter_id) |
|
||||||
|
| 未实名租客下单 | 根据 system_config 的 `realname_required_for_order` 配置 |
|
||||||
|
| 非法状态转换 | 已取消订单不能再次取消,已完成订单不能结账等 |
|
||||||
|
| 结账争议流程 | 任一方发起争议→客服仲裁→完成/关闭 |
|
||||||
|
|
||||||
|
#### 5.3 并发测试
|
||||||
|
|
||||||
|
| 场景 | 验证点 |
|
||||||
|
|------|--------|
|
||||||
|
| 2 人同时下单同一商品 | `SELECT FOR UPDATE` 保证只有一人成功 |
|
||||||
|
| 同时支付+超时取消 | 互斥,不会出现既支付又取消 |
|
||||||
|
| 并发结算 | wallet_ledger 不出现负数或重复入账 |
|
||||||
|
| 并发创建订单 | 库存锁定正确 |
|
||||||
|
|
||||||
|
#### 5.4 超时测试(依赖超时扫描任务)
|
||||||
|
|
||||||
|
| 超时类型 | 默认阈值 | 期望行为 |
|
||||||
|
|----------|----------|----------|
|
||||||
|
| 待支付超时 | 15 min | 订单自动取消 |
|
||||||
|
| 号主待交接超时 | 30 min | handoff_status → owner_timeout,通知双方 |
|
||||||
|
| 租客确认收号超时 | 30 min | status → abnormal,客服介入 |
|
||||||
|
| 租客逾期未结账 | 10 min 宽限 | status → overdue |
|
||||||
|
| 号主确认结账超时 | 120 min | status → abnormal |
|
||||||
|
|
||||||
|
#### 5.5 安全测试
|
||||||
|
|
||||||
|
| 场景 | 验证点 |
|
||||||
|
|------|--------|
|
||||||
|
| 未登录访问 | 所有写接口返回 401 |
|
||||||
|
| 跨用户操作 | A 不能取消 B 的订单,不能确认 B 的收号 |
|
||||||
|
| 后台接口保护 | 普通用户 token 不能访问 /api/admin/* |
|
||||||
|
| 重复提交 | 防重机制(前端 loading + 后端幂等) |
|
||||||
|
| SQL 注入 | 所有查询通过 GORM 参数化 |
|
||||||
|
| 越权查看 | 用户不能查看他人订单、钱包、交接记录 |
|
||||||
|
|
||||||
|
### 6. 具体测试实施方案
|
||||||
|
|
||||||
|
#### 6.1 测试环境搭建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 启动基础设施
|
||||||
|
docker compose -f deploy/docker-compose.dev.yml up -d
|
||||||
|
|
||||||
|
# 2. 初始化数据库
|
||||||
|
docker exec -i hfb-mysql mysql -uhfb -psecret hfb_sys < backend/migrations/000001_init.sql
|
||||||
|
|
||||||
|
# 3. 启动后端
|
||||||
|
cd backend && cp .env.example .env && go run ./cmd/api
|
||||||
|
|
||||||
|
# 4. 启动前端
|
||||||
|
cd frontend && npm install && npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6.2 手动端到端测试步骤(核心路径)
|
||||||
|
|
||||||
|
```
|
||||||
|
前置条件:准备两个测试手机号(号主A + 租客B)
|
||||||
|
|
||||||
|
Step 1: 号主A 注册/登录
|
||||||
|
POST /api/auth/sms/send {phone: "138xxxx0001"}
|
||||||
|
POST /api/auth/sms/login {phone: "138xxxx0001", code: "从日志获取"}
|
||||||
|
|
||||||
|
Step 2: 号主A 实名认证(mock)
|
||||||
|
POST /api/realname/start {name: "张三", id_no: "110101199001011234"}
|
||||||
|
|
||||||
|
Step 3: 号主A 发布商品
|
||||||
|
POST /api/listings {
|
||||||
|
game_name: "三角洲行动",
|
||||||
|
server_region: "微信区",
|
||||||
|
haf_coin_amount: 5000000,
|
||||||
|
price_hourly: 10,
|
||||||
|
deposit_amount: 100,
|
||||||
|
min_rent_hours: 1,
|
||||||
|
max_rent_hours: 24
|
||||||
|
}
|
||||||
|
POST /api/listings/{id}/submit-review
|
||||||
|
|
||||||
|
Step 4: 后台审核通过
|
||||||
|
POST /api/admin/auth/login {username: "admin", password: "admin123456", captcha: "..."}
|
||||||
|
POST /api/admin/listings/{id}/approve
|
||||||
|
|
||||||
|
Step 5: 租客B 注册/登录
|
||||||
|
(同 Step 1,使用 138xxxx0002)
|
||||||
|
|
||||||
|
Step 6: 租客B 充值(当前需手动)
|
||||||
|
直接改数据库或通过调试接口给租客B加余额
|
||||||
|
|
||||||
|
Step 7: 租客B 浏览并下单
|
||||||
|
GET /api/listings → 查看商品
|
||||||
|
POST /api/orders {listing_id, rent_hours: 3}
|
||||||
|
|
||||||
|
Step 8: 租客B 支付(当前直接跳到 pending_handoff,后续需补充)
|
||||||
|
验证订单状态
|
||||||
|
|
||||||
|
Step 9: 号主A 提交交接说明
|
||||||
|
POST /api/orders/{id}/handoff {content: "登录方式:..."}
|
||||||
|
|
||||||
|
Step 10: 租客B 确认收号
|
||||||
|
POST /api/orders/{id}/confirm-receive
|
||||||
|
|
||||||
|
Step 11: 租客B 发起结账
|
||||||
|
POST /api/orders/{id}/checkout {
|
||||||
|
content: "使用结束",
|
||||||
|
consumable_amount: 30,
|
||||||
|
coin_consumed_m: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
Step 12: 号主A 确认结账
|
||||||
|
POST /api/orders/{id}/checkout/confirm
|
||||||
|
|
||||||
|
Step 13: 验证结果
|
||||||
|
GET /api/orders/{id} → status=completed
|
||||||
|
GET /api/wallet/ledger → 流水完整
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6.3 自动化测试建议
|
||||||
|
|
||||||
|
| 层级 | 工具 | 覆盖内容 |
|
||||||
|
|------|------|----------|
|
||||||
|
| 单元测试 | Go testing + testify | Repository 的事务方法,Service 参数校验 |
|
||||||
|
| 集成测试 | Go testing + testcontainers | 真实 MySQL/Redis 下的完整流程 |
|
||||||
|
| API 测试 | Postman/Newman 或 Go httptest | 所有 API 端点的正常+异常路径 |
|
||||||
|
| E2E 测试 | Playwright 或 Cypress | 前端页面操作完整流程 |
|
||||||
|
| 并发测试 | Go benchmark + goroutines | 并发下单、并发结算 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第二部分:应增加什么消息系统
|
||||||
|
|
||||||
|
### 1. 现有消息系统现状评估
|
||||||
|
|
||||||
|
#### 1.1 站内信体系
|
||||||
|
|
||||||
|
**好消息**:后端站内信的写入非常完整——所有 18 个关键业务节点都在事务内通过 `notification.Append(tx, ...)` 写入,覆盖了订单全生命周期的每个状态变更。
|
||||||
|
|
||||||
|
| 文件 | notification.Append 调用次数 | 场景 |
|
||||||
|
|------|------------------------------|------|
|
||||||
|
| `order/repository.go` | 9 次 | 创建/取消/交接/收号/结账/修改结账/客服关闭/标记异常/完成结算 |
|
||||||
|
| `dispute/repository.go` | 4 次 | 发起申诉(2)/仲裁完成(2) |
|
||||||
|
| `ordertimeout/job.go` | 4 次 | 号主超时/收号超时/逾期/确认结账超时 |
|
||||||
|
|
||||||
|
**坏消息**:前端完全没接——`MobileMessagesView.vue` 只有一个 `van-empty` 组件,`Desktop NotificationsView.vue` 虽然接入了 API 但缺乏未读数角标和分类。
|
||||||
|
|
||||||
|
#### 1.2 表结构分析
|
||||||
|
|
||||||
|
```sql
|
||||||
|
notifications (
|
||||||
|
id, user_id, type, title, content,
|
||||||
|
biz_type, biz_id, read_at, created_at
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**当前优点**:
|
||||||
|
- 简洁高效
|
||||||
|
- `(user_id, read_at, created_at)` 联合索引支持快速分页查询
|
||||||
|
|
||||||
|
**当前不足**:
|
||||||
|
- 缺少 `category` 字段(订单消息/系统通知/公告无法区分)
|
||||||
|
- 缺少软删除标记
|
||||||
|
- 缺少 `extra_data` (JSON) 用于存储跳转链接等扩展信息
|
||||||
|
|
||||||
|
#### 1.3 对照项目计划的缺口
|
||||||
|
|
||||||
|
项目计划第 8 章规划的但**尚未实现**的场景:
|
||||||
|
|
||||||
|
| 规划场景 | 当前状态 | 优先级 |
|
||||||
|
|----------|----------|--------|
|
||||||
|
| 商品审核通过/拒绝通知号主 | ✅ 已实现 | — |
|
||||||
|
| 租期即将结束提醒 | ❌ 未实现 | 🔴 高 |
|
||||||
|
| 账户冻结/解冻通知用户 | ❌ 未实现 | 🟡 中 |
|
||||||
|
| 短信登录验证码 | ✅ mock | — |
|
||||||
|
| 租期即将到期短信 | ❌ 未实现 | 🔴 高 |
|
||||||
|
| 申诉被受理短信 | ❌ 未实现 | 🟡 中 |
|
||||||
|
| 仲裁结果短信 | ❌ 未实现 | 🟡 中 |
|
||||||
|
| WebSocket/SSE 实时推送 | ❌ 未实现 | 🟢 低 |
|
||||||
|
|
||||||
|
### 2. 推荐的消息系统增强架构
|
||||||
|
|
||||||
|
#### 2.1 站内信增强(Phase 1 — 立即做)
|
||||||
|
|
||||||
|
```
|
||||||
|
最小可行方案:
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ notifications 表扩展 │
|
||||||
|
├─────────────────────────────────────────────────┤
|
||||||
|
│ + category VARCHAR(32) -- order|system|announce │
|
||||||
|
│ + link_type VARCHAR(32) -- order_detail|... │
|
||||||
|
│ + link_id BIGINT │
|
||||||
|
│ + extra_data JSON -- 扩展数据 │
|
||||||
|
├─────────────────────────────────────────────────┤
|
||||||
|
│ 新增 API: │
|
||||||
|
│ GET /api/notifications/unread-count │
|
||||||
|
│ POST /api/notifications/batch-read │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
前端改动:
|
||||||
|
├── MobileMessagesView.vue:接入 fetchNotifications,消息列表
|
||||||
|
├── 底部导航栏:未读消息红点角标
|
||||||
|
├── Desktop NotificationsView.vue:增强消息分类和已读标记
|
||||||
|
└── Router:增加消息未读数 store
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.2 短信系统增强(Phase 2 — 短期)
|
||||||
|
|
||||||
|
```
|
||||||
|
设计短信 Provider 抽象层:
|
||||||
|
|
||||||
|
internal/integrations/sms/
|
||||||
|
├── provider.go ← Provider 接口
|
||||||
|
│ type Provider interface {
|
||||||
|
│ Send(ctx, phone, templateCode string, params map[string]string) error
|
||||||
|
│ SendBatch(ctx, phones []string, ...) error
|
||||||
|
│ }
|
||||||
|
├── mock.go ← MockProvider(开发环境)
|
||||||
|
├── aliyun.go ← AliyunProvider(生产环境)
|
||||||
|
├── limiter.go ← 限流器(基于 Redis)
|
||||||
|
│ - 手机号维度:60s/次,10次/天
|
||||||
|
│ - IP 维度:100次/小时
|
||||||
|
│ - 场景维度:可配置
|
||||||
|
└── logger.go ← 发送日志(sms_logs 表)
|
||||||
|
|
||||||
|
短信触发场景:
|
||||||
|
├── 登录验证码(已有 mock)
|
||||||
|
├── 租期即将到期提醒(新增 🔴)
|
||||||
|
├── 订单申诉被受理(新增 🟡)
|
||||||
|
├── 仲裁结果通知(新增 🟡)
|
||||||
|
└── 高风险安全提醒(预留)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.3 SSE 实时推送架构(Phase 3 — 中期)
|
||||||
|
|
||||||
|
```
|
||||||
|
推荐 SSE(Server-Sent Events)而非 WebSocket:
|
||||||
|
理由:本场景是单向推送(服务端→客户端),SSE 更轻量,
|
||||||
|
基于 HTTP,不需要额外协议升级,断线自动重连。
|
||||||
|
|
||||||
|
Go 侧架构:
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ SSEBroker (单例) │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ clients map[userID]*SSEClient │
|
||||||
|
│ Notify(userID, event) │
|
||||||
|
│ Broadcast(event) │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
↓ 事件分发
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ SSE Events: │
|
||||||
|
│ - order.status_changed │
|
||||||
|
│ - order.handoff_updated │
|
||||||
|
│ - order.checkout_updated │
|
||||||
|
│ - notification.new │
|
||||||
|
│ - dispute.updated │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
|
||||||
|
Vue 侧:
|
||||||
|
// composables/useSSE.ts
|
||||||
|
const eventSource = new EventSource('/api/sse/stream?token=xxx')
|
||||||
|
eventSource.addEventListener('notification.new', (e) => {
|
||||||
|
const data = JSON.parse(e.data)
|
||||||
|
notificationStore.incrementUnread()
|
||||||
|
})
|
||||||
|
|
||||||
|
重连策略:
|
||||||
|
- EventSource 原生支持自动重连
|
||||||
|
- 可设置 retry interval: 3000ms
|
||||||
|
- 重连时重新验证 token
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.4 订单全链路状态变更推送矩阵
|
||||||
|
|
||||||
|
```
|
||||||
|
订单状态变更 → 推送内容设计:
|
||||||
|
|
||||||
|
pending_payment → 租客: {type:"order.created", title:"订单已创建", orderId, amount, expireAt}
|
||||||
|
号主: {type:"order.new_rental", title:"有新的租用请求"}
|
||||||
|
|
||||||
|
pending_handoff → 租客: {type:"order.paid", title:"支付成功,等待号主交接"}
|
||||||
|
号主: {type:"order.pending_handoff", title:"请尽快完成账号交接"}
|
||||||
|
|
||||||
|
pending_renter_confirm → 租客: {type:"handoff.submitted", title:"号主已完成交接,请确认收号"}
|
||||||
|
|
||||||
|
renting → 号主: {type:"handoff.confirmed", title:"租客已确认收号,租赁开始"}
|
||||||
|
|
||||||
|
[租期结束前5分钟] → 租客: {type:"rental.expiring", title:"租期即将结束"}
|
||||||
|
|
||||||
|
pending_checkout_confirm → 号主: {type:"checkout.submitted", title:"租客已发起结账"}
|
||||||
|
|
||||||
|
completed → 双方: {type:"order.completed", title:"订单已完成"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 数据库设计建议
|
||||||
|
|
||||||
|
#### 3.1 新增表
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 短信发送日志
|
||||||
|
CREATE TABLE sms_logs (
|
||||||
|
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
phone VARCHAR(20) NOT NULL,
|
||||||
|
template_code VARCHAR(64) NOT NULL,
|
||||||
|
template_params JSON,
|
||||||
|
status VARCHAR(16) NOT NULL DEFAULT 'pending', -- pending|sent|failed
|
||||||
|
provider VARCHAR(32) NOT NULL,
|
||||||
|
provider_msg_id VARCHAR(128),
|
||||||
|
error_msg VARCHAR(512),
|
||||||
|
biz_type VARCHAR(32),
|
||||||
|
biz_id BIGINT UNSIGNED,
|
||||||
|
user_id BIGINT UNSIGNED,
|
||||||
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
INDEX idx_sms_logs_phone_created (phone, created_at),
|
||||||
|
INDEX idx_sms_logs_status (status)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- SSE 推送记录(可选,用于对账)
|
||||||
|
CREATE TABLE push_records (
|
||||||
|
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user_id BIGINT UNSIGNED NOT NULL,
|
||||||
|
event_type VARCHAR(64) NOT NULL,
|
||||||
|
payload JSON,
|
||||||
|
delivered TINYINT DEFAULT 0,
|
||||||
|
delivered_at DATETIME,
|
||||||
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
INDEX idx_push_records_user (user_id, created_at)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3.2 notifications 表扩展
|
||||||
|
|
||||||
|
```sql
|
||||||
|
ALTER TABLE notifications
|
||||||
|
ADD COLUMN category VARCHAR(32) NOT NULL DEFAULT 'order'
|
||||||
|
COMMENT 'order|system|announce|security',
|
||||||
|
ADD COLUMN link_type VARCHAR(32) DEFAULT NULL
|
||||||
|
COMMENT 'order_detail|listing_detail|dispute_detail|wallet|url',
|
||||||
|
ADD COLUMN link_id BIGINT UNSIGNED DEFAULT NULL,
|
||||||
|
ADD COLUMN extra_data JSON DEFAULT NULL;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 实施优先级总览
|
||||||
|
|
||||||
|
```
|
||||||
|
Phase 1(立即 — 让消息系统可用)
|
||||||
|
├── ✅ 让 MobileMessagesView.vue 接入 fetchNotifications
|
||||||
|
├── ✅ 底部导航栏增加未读消息角标
|
||||||
|
├── ✅ 增加 GET /api/notifications/unread-count API
|
||||||
|
├── ✅ 增加 POST /api/notifications/batch-read API
|
||||||
|
└── ✅ 消息列表支持点击跳转到对应订单详情
|
||||||
|
|
||||||
|
Phase 2(短期 — 完善通知体系)
|
||||||
|
├── notifications 表扩展 category/link_type/extra_data
|
||||||
|
├── 实现租期即将结束通知(站内信 + 前端定时检查)
|
||||||
|
├── 实现用户冻结/解冻通知
|
||||||
|
├── 短信 Provider 抽象层 + 限流器
|
||||||
|
├── sms_logs 表 + 短信日志后台查看
|
||||||
|
└── 接入一个真实短信服务商
|
||||||
|
|
||||||
|
Phase 3(中期 — 实时体验)
|
||||||
|
├── SSE Broker 实现
|
||||||
|
├── 订单状态实时推送接入前端
|
||||||
|
├── 租期倒计时实时更新
|
||||||
|
├── 客服消息实时通知
|
||||||
|
└── push_records 表(可选)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录:关键文件索引
|
||||||
|
|
||||||
|
### 后端核心文件
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `backend/internal/modules/order/repository.go` | 订单 Repository,核心业务逻辑(1089行) |
|
||||||
|
| `backend/internal/modules/order/handler.go` | 订单 HTTP Handler(376行) |
|
||||||
|
| `backend/internal/modules/order/service.go` | 订单 Service 层(160行) |
|
||||||
|
| `backend/internal/modules/order/dto.go` | 订单 DTO 定义(126行) |
|
||||||
|
| `backend/internal/model/order.go` | RentalOrder 模型定义 |
|
||||||
|
| `backend/internal/modules/wallet/repository.go` | 钱包 Repository + AppendEntries |
|
||||||
|
| `backend/internal/modules/notification/repository.go` | 通知 Repository + Append |
|
||||||
|
| `backend/internal/modules/dispute/repository.go` | 争议仲裁 Repository |
|
||||||
|
| `backend/internal/jobs/ordertimeout/job.go` | 超时扫描定时任务 |
|
||||||
|
|
||||||
|
### 前端核心文件
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `frontend/src/views/mobile/MobileListingDetailView.vue` | 商品详情/下单页 |
|
||||||
|
| `frontend/src/views/mobile/MobileOrdersView.vue` | 订单列表页 |
|
||||||
|
| `frontend/src/views/mobile/MobileMessagesView.vue` | 消息页(空壳) |
|
||||||
|
| `frontend/src/views/mobile/MobileProfileView.vue` | 个人中心(余额硬编码) |
|
||||||
|
| `frontend/src/api/orders.ts` | 订单 API 定义 |
|
||||||
|
| `frontend/src/api/notifications.ts` | 通知 API 定义 |
|
||||||
|
| `frontend/src/api/wallet.ts` | 钱包 API 定义 |
|
||||||
+28
-21
@@ -113,7 +113,7 @@
|
|||||||
|
|
||||||
- 租客选择商品和租期后创建订单。
|
- 租客选择商品和租期后创建订单。
|
||||||
- 系统锁定商品库存,避免同一账号被重复出租。
|
- 系统锁定商品库存,避免同一账号被重复出租。
|
||||||
- 订单流转覆盖待确认、待交接、租赁中、待归还确认、已完成、已取消、申诉中。
|
- 订单流转覆盖待确认、待交接、租赁中、待号主确认结账、待租客确认修正、结账争议中、已完成、已取消、申诉中。
|
||||||
|
|
||||||
钱包账务:
|
钱包账务:
|
||||||
|
|
||||||
@@ -142,10 +142,10 @@
|
|||||||
2. 订单确认后,商品进入锁定状态,订单进入待交接。
|
2. 订单确认后,商品进入锁定状态,订单进入待交接。
|
||||||
3. 号主在订单内提交交接说明,例如登录方式、注意事项、联系说明或外部交接备注。
|
3. 号主在订单内提交交接说明,例如登录方式、注意事项、联系说明或外部交接备注。
|
||||||
4. 租客确认收到账号后,订单进入租赁中,并记录租赁开始时间和预计结束时间。
|
4. 租客确认收到账号后,订单进入租赁中,并记录租赁开始时间和预计结束时间。
|
||||||
5. 租期即将结束时,系统通过站内信和短信提醒租客归还。
|
5. 租期即将结束时,系统通过站内信和短信提醒租客完成使用并发起结账。
|
||||||
6. 租客提交归还申请,上传必要的租后截图或说明。
|
6. 租客发起结账,填写使用结束说明、消耗金额、哈夫币消耗量和必要证据。
|
||||||
7. 号主确认账号状态无误后,订单进入结算。
|
7. 号主确认账号状态无误后直接完成结算,或修改结账金额后交由租客确认。
|
||||||
8. 如任一方对交接、使用、归还或资产变化有异议,订单进入申诉仲裁。
|
8. 租客同意修正后订单完成;拒绝修正或任一方对交接、使用、结账、资产变化有异议时,订单进入申诉仲裁。
|
||||||
|
|
||||||
回收边界:
|
回收边界:
|
||||||
|
|
||||||
@@ -158,8 +158,8 @@
|
|||||||
|
|
||||||
- 号主待交接超时:订单进入待交接后,默认 30 分钟未提交交接说明,租客可取消订单或发起申诉。
|
- 号主待交接超时:订单进入待交接后,默认 30 分钟未提交交接说明,租客可取消订单或发起申诉。
|
||||||
- 租客确认收号超时:号主提交交接说明后,默认 30 分钟未确认收号,订单进入客服介入,不自动开始租期。
|
- 租客确认收号超时:号主提交交接说明后,默认 30 分钟未确认收号,订单进入客服介入,不自动开始租期。
|
||||||
- 租客归还超时:超过租期结束时间仍未提交归还,系统提醒后进入逾期中,号主可发起申诉。
|
- 租客结账超时:超过租期结束时间仍未发起结账,系统提醒后进入逾期中,号主可发起申诉。
|
||||||
- 号主确认归还超时:租客提交归还后,默认 2 小时未确认,订单进入自动确认候选或客服复核。
|
- 号主确认结账超时:租客发起结账后,默认 2 小时未确认,订单进入客服复核。
|
||||||
- 所有超时阈值必须进入 `system_configs` 管理,后台可调整。
|
- 所有超时阈值必须进入 `system_configs` 管理,后台可调整。
|
||||||
|
|
||||||
订单状态机:
|
订单状态机:
|
||||||
@@ -173,12 +173,16 @@
|
|||||||
| 待交接 | 申诉中 | 租客/号主 | 交接争议或安全验证无法完成 |
|
| 待交接 | 申诉中 | 租客/号主 | 交接争议或安全验证无法完成 |
|
||||||
| 待收号确认 | 租赁中 | 租客 | 租客确认收到账号并可正常登录 |
|
| 待收号确认 | 租赁中 | 租客 | 租客确认收到账号并可正常登录 |
|
||||||
| 待收号确认 | 申诉中 | 租客/系统 | 租客确认超时、无法登录或描述不符 |
|
| 待收号确认 | 申诉中 | 租客/系统 | 租客确认超时、无法登录或描述不符 |
|
||||||
| 租赁中 | 逾期中 | 系统 | 超过租期结束时间仍未提交归还 |
|
| 租赁中 | 逾期中 | 系统 | 超过租期结束时间仍未发起结账 |
|
||||||
| 租赁中 | 待归还确认 | 租客 | 租客提交归还和租后凭证 |
|
| 租赁中 | 待号主确认结账 | 租客 | 租客发起结账和租后凭证 |
|
||||||
| 逾期中 | 待归还确认 | 租客 | 租客补交归还申请 |
|
| 逾期中 | 待号主确认结账 | 租客 | 租客补交结账申请 |
|
||||||
| 逾期中 | 申诉中 | 号主/系统 | 逾期未归还或疑似资产损失 |
|
| 逾期中 | 申诉中 | 号主/系统 | 逾期未归还或疑似资产损失 |
|
||||||
| 待归还确认 | 已完成 | 号主/系统 | 号主确认归还,或超时后客服/系统确认 |
|
| 待号主确认结账 | 已完成 | 号主 | 号主确认结账 |
|
||||||
| 待归还确认 | 申诉中 | 号主/租客 | 归还争议、资产损失或封号异常 |
|
| 待号主确认结账 | 待租客确认修正 | 号主 | 号主修改结账金额 |
|
||||||
|
| 待租客确认修正 | 已完成 | 租客 | 租客同意修正结账 |
|
||||||
|
| 待号主确认结账 | 结账争议中 | 任一方 | 发起结账争议 |
|
||||||
|
| 待租客确认修正 | 结账争议中 | 任一方 | 发起结账争议 |
|
||||||
|
| 结账争议中 | 已完成/已关闭/异常 | 客服 | 后台仲裁 |
|
||||||
| 申诉中 | 已完成 | 客服 | 仲裁为正常完成或部分扣款后完成 |
|
| 申诉中 | 已完成 | 客服 | 仲裁为正常完成或部分扣款后完成 |
|
||||||
| 申诉中 | 已取消 | 客服 | 仲裁为订单取消和退款 |
|
| 申诉中 | 已取消 | 客服 | 仲裁为订单取消和退款 |
|
||||||
| 申诉中 | 已关闭 | 客服 | 仲裁为异常关闭、冻结用户或冻结商品 |
|
| 申诉中 | 已关闭 | 客服 | 仲裁为异常关闭、冻结用户或冻结商品 |
|
||||||
@@ -216,7 +220,7 @@
|
|||||||
结算规则:
|
结算规则:
|
||||||
|
|
||||||
- 正常完成订单后,租金按平台抽成规则分账给号主。
|
- 正常完成订单后,租金按平台抽成规则分账给号主。
|
||||||
- 押金在号主确认归还后释放给租客。
|
- 押金在双方结账确认或客服仲裁后释放给租客。
|
||||||
- 如发生资产损失、无法登录、封号或超时归还,可由客服仲裁后扣除部分或全部押金。
|
- 如发生资产损失、无法登录、封号或超时归还,可由客服仲裁后扣除部分或全部押金。
|
||||||
- 如号主虚假描述、超时未交接或账号无法使用,可裁定全额或部分退款。
|
- 如号主虚假描述、超时未交接或账号无法使用,可裁定全额或部分退款。
|
||||||
- 仲裁导致的扣款、退款、赔付必须生成资金流水和审计日志。
|
- 仲裁导致的扣款、退款、赔付必须生成资金流水和审计日志。
|
||||||
@@ -281,8 +285,9 @@
|
|||||||
- 号主提交交接说明。
|
- 号主提交交接说明。
|
||||||
- 租客确认收号。
|
- 租客确认收号。
|
||||||
- 租期即将结束。
|
- 租期即将结束。
|
||||||
- 租客提交归还。
|
- 租客发起结账。
|
||||||
- 号主确认归还。
|
- 号主确认或修改结账。
|
||||||
|
- 租客确认或拒绝修正结账。
|
||||||
- 订单完成结算。
|
- 订单完成结算。
|
||||||
- 发起申诉。
|
- 发起申诉。
|
||||||
- 仲裁结果。
|
- 仲裁结果。
|
||||||
@@ -592,8 +597,10 @@
|
|||||||
- `GET /api/orders/{id}`:订单详情。
|
- `GET /api/orders/{id}`:订单详情。
|
||||||
- `POST /api/orders/{id}/cancel`:取消订单。
|
- `POST /api/orders/{id}/cancel`:取消订单。
|
||||||
- `POST /api/orders/{id}/confirm-receive`:租客确认收号。
|
- `POST /api/orders/{id}/confirm-receive`:租客确认收号。
|
||||||
- `POST /api/orders/{id}/return`:租客提交归还。
|
- `POST /api/orders/{id}/checkout`:租客发起结账。
|
||||||
- `POST /api/orders/{id}/confirm-return`:号主确认归还。
|
- `POST /api/orders/{id}/checkout/confirm`:号主确认结账。
|
||||||
|
- `POST /api/orders/{id}/checkout/counter`:号主修改结账。
|
||||||
|
- `POST /api/orders/{id}/checkout/accept`:租客同意修正结账。
|
||||||
- `POST /api/orders/{id}/dispute`:发起申诉。
|
- `POST /api/orders/{id}/dispute`:发起申诉。
|
||||||
|
|
||||||
交接:
|
交接:
|
||||||
@@ -835,8 +842,8 @@ hfb_sys/
|
|||||||
- 实现订单账号资产快照。
|
- 实现订单账号资产快照。
|
||||||
- 实现号主提交交接说明。
|
- 实现号主提交交接说明。
|
||||||
- 实现租客确认收号。
|
- 实现租客确认收号。
|
||||||
- 实现租客提交归还、号主确认归还。
|
- 实现租客发起结账、号主确认或修改结账、租客确认或拒绝修正结账。
|
||||||
- 实现交接、确认收号、归还、确认归还的超时处理。
|
- 实现交接、确认收号、结账逾期、确认结账超时处理。
|
||||||
- 实现钱包账户、押金冻结、租金流水、结算流水。
|
- 实现钱包账户、押金冻结、租金流水、结算流水。
|
||||||
- 预留支付和提现接口。
|
- 预留支付和提现接口。
|
||||||
|
|
||||||
@@ -887,9 +894,9 @@ hfb_sys/
|
|||||||
- 商品草稿、待审核、已上架、已下架、审核拒绝状态流转正确。
|
- 商品草稿、待审核、已上架、已下架、审核拒绝状态流转正确。
|
||||||
- 审核未通过商品不能下单。
|
- 审核未通过商品不能下单。
|
||||||
- 同一账号并发下单只能成功一单。
|
- 同一账号并发下单只能成功一单。
|
||||||
- 订单待交接、租赁中、待归还确认、已完成、申诉中状态流转正确。
|
- 订单待交接、租赁中、待号主确认结账、待租客确认修正、结账争议中、已完成、申诉中状态流转正确。
|
||||||
- 非法订单状态转换会被拒绝。
|
- 非法订单状态转换会被拒绝。
|
||||||
- 交接、收号、归还、确认归还超时会按配置触发取消、申诉、逾期或客服复核。
|
- 交接、收号、结账逾期、确认结账超时会按配置触发取消、申诉、逾期或客服复核。
|
||||||
- 押金冻结、释放、扣款、退款流水一致。
|
- 押金冻结、释放、扣款、退款流水一致。
|
||||||
- 每笔 `wallet_ledger` 正确记录 `balance_after`。
|
- 每笔 `wallet_ledger` 正确记录 `balance_after`。
|
||||||
- 租金、平台抽成、号主结算流水一致。
|
- 租金、平台抽成、号主结算流水一致。
|
||||||
|
|||||||
@@ -318,10 +318,11 @@ function isNavActive(path: string) {
|
|||||||
round
|
round
|
||||||
class="order-btn"
|
class="order-btn"
|
||||||
:loading="ordering"
|
:loading="ordering"
|
||||||
|
:disabled="listing.in_transaction"
|
||||||
loading-text="下单中..."
|
loading-text="下单中..."
|
||||||
@click="handleCreateOrder"
|
@click="handleCreateOrder"
|
||||||
>
|
>
|
||||||
立即下单
|
{{ listing.in_transaction ? "交易中" : "立即下单" }}
|
||||||
</van-button>
|
</van-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user