feat: 实现门店套餐分配功能并统一测试基础设施
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m30s

新增功能:
- 门店套餐分配管理(shop_package_allocation):支持门店套餐库存管理
- 门店套餐系列分配管理(shop_series_allocation):支持套餐系列分配和佣金层级设置
- 我的套餐查询(my_package):支持门店查询自己的套餐分配情况

测试改进:
- 统一集成测试基础设施,新增 testutils.NewIntegrationTestEnv
- 重构所有集成测试使用新的测试环境设置
- 移除旧的测试辅助函数和冗余测试文件
- 新增 test_helpers_test.go 统一任务测试辅助

技术细节:
- 新增数据库迁移 000025_create_shop_allocation_tables
- 新增 3 个 Handler、Service、Store 和对应的单元测试
- 更新 OpenAPI 文档和文档生成器
- 测试覆盖率:Service 层 > 90%

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-28 10:45:16 +08:00
parent 5fefe9d0cb
commit 23eb0307bb
73 changed files with 8716 additions and 4558 deletions

View File

@@ -260,6 +260,88 @@ testutils.CleanTestRedisKeys(t, rdb)
store := postgres.NewXxxStore(tx, rdb)
```
## 集成测试环境
对于需要完整 HTTP 请求测试的场景,使用 `IntegrationTestEnv`
### 基础用法
```go
func TestAPI_Create(t *testing.T) {
env := testutils.NewIntegrationTestEnv(t)
t.Run("成功创建资源", func(t *testing.T) {
reqBody := dto.CreateRequest{
Name: fmt.Sprintf("test_%d", time.Now().UnixNano()),
}
jsonBody, _ := json.Marshal(reqBody)
resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/resources", jsonBody)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
})
}
```
### IntegrationTestEnv API
| 方法 | 说明 |
|------|------|
| `NewIntegrationTestEnv(t)` | 创建集成测试环境,自动初始化所有依赖 |
| `AsSuperAdmin()` | 以超级管理员身份发送请求 |
| `AsUser(account)` | 以指定账号身份发送请求 |
| `Request(method, path, body)` | 发送 HTTP 请求 |
| `CreateTestAccount(...)` | 创建测试账号 |
| `CreateTestShop(...)` | 创建测试店铺 |
| `CreateTestRole(...)` | 创建测试角色 |
| `CreateTestPermission(...)` | 创建测试权限 |
### 数据隔离最佳实践
**必须使用动态生成的测试数据**,避免固定值导致的测试冲突:
```go
t.Run("创建资源", func(t *testing.T) {
// ✅ 正确:使用动态值
name := fmt.Sprintf("test_resource_%d", time.Now().UnixNano())
phone := fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000)
// ❌ 错误:使用固定值(会导致并发测试冲突)
name := "test_resource"
phone := "13800000001"
})
```
### 完整示例
```go
func TestAccountAPI_Create(t *testing.T) {
env := testutils.NewIntegrationTestEnv(t)
t.Run("成功创建平台账号", func(t *testing.T) {
username := fmt.Sprintf("platform_user_%d", time.Now().UnixNano())
phone := fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000)
reqBody := dto.CreateAccountRequest{
Username: username,
Phone: phone,
Password: "Password123",
UserType: constants.UserTypePlatform,
}
jsonBody, _ := json.Marshal(reqBody)
resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/accounts", jsonBody)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
// 验证数据库中账号已创建
var count int64
env.TX.Model(&model.Account{}).Where("username = ?", username).Count(&count)
assert.Equal(t, int64(1), count)
})
}
```
## 故障排查
### 连接超时
@@ -282,3 +364,10 @@ store := postgres.NewXxxStore(tx, rdb)
1. 确保使用 `CleanTestRedisKeys`
2. 检查是否正确使用 `GetTestRedisKeyPrefix`
3. 验证键名是否包含测试名称前缀
### 测试数据冲突
如果看到 "用户名已存在" 或 "手机号已存在" 错误:
1. 确保使用 `time.Now().UnixNano()` 生成唯一值
2. 不要在子测试之间共享固定的测试数据
3. 检查是否有遗留的测试数据未被事务回滚