feat: 实现企业设备授权功能并归档 OpenSpec 变更
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m39s

- 新增企业设备授权模块(Model、DTO、Service、Handler、Store)
- 实现设备授权的创建、查询、更新、删除等完整业务逻辑
- 添加企业卡授权与设备授权的关联关系
- 新增 2 个数据库迁移脚本
- 同步 OpenSpec delta specs 到 main specs
- 归档 add-enterprise-device-authorization 变更
- 更新 API 文档和路由配置
- 新增完整的集成测试和单元测试覆盖
This commit is contained in:
2026-01-29 13:18:49 +08:00
parent e87513541b
commit b02175271a
118 changed files with 14306 additions and 472 deletions

View File

@@ -0,0 +1,3 @@
schema: spec-driven
created: 2026-01-29

View File

@@ -0,0 +1,40 @@
# 订单激活幂等性修复 - 设计
## 目标
1. 同一订单被重复支付/重复回调/重复请求时,只会激活一次套餐使用记录。
2. 重复请求返回“幂等成功”(不报错,不重复激活)。
3. 避免因并发导致的重复插入。
## 方案
### 1) 以状态机作为幂等门闸
将订单支付状态从 `pending` 变更为 `paid` 时使用条件更新:
- `UPDATE tb_order SET payment_status=paid,... WHERE id=? AND payment_status=pending`
-`RowsAffected == 0`
- 视为订单已被处理(可能已支付/已取消/已退款)
- 对“已支付”场景直接返回成功(幂等成功)
- 对“非待支付且非已支付”场景返回对应业务错误(例如已取消不允许支付)
这样可以确保并发下只有一个请求能“拿到激活资格”。
### 2) 激活逻辑只在首次成功支付后执行
`activatePackage` 只在上述条件更新成功后执行;并确保激活过程内的数据读取使用同一个事务 `tx`(避免出现读取不一致或部分写入)。
### 3) 防御性约束(可选但推荐)
`tb_package_usage` 增加唯一约束(示例):
- 同一订单下,同一 `package_id` 只能有一条 usage
-`order_id + package_id` 为主(按当前业务:一个订单对应一个资源,且一次购买不应重复同套餐)
如果未来允许同订单同套餐多份购买,则需要同时引入 `quantity` 或 usage 的明细拆分策略,再调整唯一约束。
## 验收标准
- 重复调用钱包支付/支付回调接口,不会重复生成 `tb_package_usage` 记录。
- 幂等重复请求返回成功。
- 新增测试通过。

View File

@@ -0,0 +1,23 @@
# 订单激活幂等性修复(同订单只激活一次)
## Why
业务规则确认:**同一个订单只能激活一次**,不允许重复生成套餐生效记录。
当前订单支付成功后会生成 `PackageUsage`(套餐使用记录)。在并发请求、回调重放、网络重试等场景下,如果缺少幂等控制,可能重复插入套餐使用记录,导致用户重复获得权益,属于高风险资金/权益漏洞。
## What Changes
- **支付状态原子转换**:将“待支付 -> 已支付”的状态变更做成原子操作(带条件更新),只有第一次成功转换才会触发套餐激活。
- **激活过程幂等**`activatePackage` 只在“首次支付成功”场景执行;重复请求返回“幂等成功”(你确认 A=1
- **可选防御性约束**:为 `tb_package_usage` 增加必要的唯一约束或幂等检查,防止异常路径插入重复记录。
- **补充测试**:覆盖并发/重复调用场景,验证不会重复激活。
## Impact
涉及模块(预期):
- Service`internal/service/order/service.go`
- Store可选`internal/store/postgres/order_store.go` / `internal/store/postgres/order_item_store.go`
- 迁移(可选):新增唯一索引
- 测试:`internal/service/order/service_test.go``tests/integration/*`

View File

@@ -0,0 +1,22 @@
# 订单激活幂等性修复 - 实现任务
## 1. 状态原子转换
- [ ] 1.1 在 `internal/service/order/service.go` 中将支付成功状态更新改为条件更新(仅 `pending -> paid` 成功时继续)
- [ ] 1.2 重复请求处理:当订单已支付时返回成功(幂等成功);当订单为已取消/已退款等非待支付状态时返回业务错误
## 2. 激活幂等与事务一致性
- [ ] 2.1 调整 `activatePackage`:只在首次支付成功后执行
- [ ] 2.2 确保 `activatePackage` 内部读取订单明细/套餐信息使用事务 `tx`(避免使用非事务 DB 导致不一致)
## 3. 防御性约束(可选)
- [ ] 3.1 评估并新增 `tb_package_usage` 的唯一索引(例如 `order_id + package_id`),并提供 down.sql
- [ ] 3.2 对应调整代码:若插入触发唯一冲突,按幂等成功处理或提前查询避免冲突
## 4. 测试与验证
- [ ] 4.1 新增单元/集成测试:重复支付/重复回调不会重复插入 usage
- [ ] 4.2 运行 `go test ./...` 确保通过