fix: 修复分页字段/角色DTO/套餐详情/批量分配Bug,新增C端测试登录接口和Hurl价格验证测试
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
- 修复B-2:27个分页DTO字段名统一(list→items, page_size→size,删除total_pages) - 修复B-1:角色接口统一返回DTO(id小写,消除GORM大写字段名) - 修复C-1:套餐详情接口(GET /packages/:id)补充代理佣金字段 - 修复C-2:批量分配已存在记录由500改为静默跳过 - 修复C-3:创建系列授权响应在事务提交后构建,packages数组不再为空 - 新增C端开发测试登录接口(POST /api/c/v1/auth/dev-login,仅logging.development=true时生效) - 新增Hurl测试:card-price-verification-flow.hurl(导入卡→分配→C端价格验证) - 新增测试Excel数据文件和dev.env测试变量
This commit is contained in:
381
tests/hurl/flows/card-price-verification-flow.hurl
Normal file
381
tests/hurl/flows/card-price-verification-flow.hurl
Normal file
@@ -0,0 +1,381 @@
|
||||
# ============================================================
|
||||
# 测试:卡导入 → 分配 → C 端价格验证
|
||||
# 生成时间:2026-03-30
|
||||
# 涉及模块:auth, iot_card(import), shop, package_series, package,
|
||||
# shop_series_grant, shop_package_allocation, client_auth, client_asset
|
||||
# ============================================================
|
||||
# 流程:
|
||||
# 1. 平台管理员登录
|
||||
# 2. 查询运营商列表,获取 carrier_id
|
||||
# 3. 获取对象存储上传 URL(purpose=iot_import)
|
||||
# 4. 上传 Excel 文件(IoT 卡测试数据)到预签名 URL
|
||||
# 5. 提交卡导入任务
|
||||
# 6. 轮询导入任务直到完成(status=3)
|
||||
# 7. 获取一级代理店铺 ID(shop1_id)
|
||||
# 8. 获取二级代理店铺 ID(shop2_id)
|
||||
# 9. 获取套餐系列 ID(series_id)和套餐 ID(package_id)
|
||||
# 10. 分配 ICCID_1 给一级代理(shop1)
|
||||
# 11. 分配 ICCID_2 给二级代理(shop2)
|
||||
# 12. 绑定两张卡到套餐系列
|
||||
# 13. C 端测试登录(ICCID_1)
|
||||
# 14. 验证 ICCID_1 的可购套餐 → retail_price = 一级代理零售价(15000)
|
||||
# 15. C 端测试登录(ICCID_2)
|
||||
# 16. 验证 ICCID_2 的可购套餐 → retail_price = 二级代理零售价(18000)
|
||||
# ============================================================
|
||||
# 前置条件:
|
||||
# 1. 已运行 package-resource-full-flow.hurl(创建了 shop1/shop2/series/package)
|
||||
# 2. 开启 logging.development=true(服务配置)
|
||||
# 3. 已配置对象存储(S3/MinIO),步骤 3-4 依赖对象存储上传
|
||||
# 4. env 文件中已配置 agent1_username/agent2_username
|
||||
# ============================================================
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 1 步:平台管理员登录
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/auth/login
|
||||
Content-Type: application/json
|
||||
{
|
||||
"username": "{{admin_username}}",
|
||||
"password": "{{admin_password}}",
|
||||
"device": "web"
|
||||
}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
admin_token: jsonpath "$.data.access_token"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.access_token" isString
|
||||
jsonpath "$.data.user.user_type" == 1
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 2 步:查询运营商列表,获取第一个可用 carrier_id
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
GET {{base_url}}/api/admin/carriers?page=1&page_size=1&status=1
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
carrier_id: jsonpath "$.data.items[0].id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.items" count >= 1
|
||||
jsonpath "$.data.items[0].id" isInteger
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 3 步:获取对象存储上传 URL(IoT 卡导入 Excel)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/admin/storage/upload-url
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"file_name": "hurl-test-cards.xlsx",
|
||||
"content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"purpose": "iot_import"
|
||||
}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
upload_url: jsonpath "$.data.upload_url"
|
||||
file_key: jsonpath "$.data.file_key"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.upload_url" isString
|
||||
jsonpath "$.data.file_key" isString
|
||||
jsonpath "$.data.expires_in" isInteger
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 4 步:上传 Excel 文件到预签名 URL
|
||||
# 文件路径相对于 Hurl 运行目录(tests/hurl/)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
PUT {{upload_url}}
|
||||
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
file,testdata/hurl-test-cards.xlsx;
|
||||
HTTP 200
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 5 步:提交 IoT 卡导入任务
|
||||
# 导入 Excel 包含 ICCID_1(HURL-TEST-CARD-001)和 ICCID_2(HURL-TEST-CARD-002)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/admin/iot-cards/import
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"carrier_id": {{carrier_id}},
|
||||
"batch_no": "HURL-PRICE-TEST",
|
||||
"file_key": "{{file_key}}"
|
||||
}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
import_task_id: jsonpath "$.data.task_id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.task_id" isInteger
|
||||
jsonpath "$.data.task_no" isString
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 6 步:轮询导入任务,等待完成(status=3 表示已完成)
|
||||
# 注:Hurl 不支持循环轮询,这里做 3 次间隔查询覆盖大多数场景
|
||||
# 如需更长等待,可手动重跑此步骤
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
# 第 1 次查询(立即)
|
||||
GET {{base_url}}/api/admin/iot-cards/import-tasks/{{import_task_id}}
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.id" == {{import_task_id}}
|
||||
jsonpath "$.data.status" isInteger
|
||||
|
||||
# 等待 2 秒后第 2 次查询(Hurl 通过 delay 选项实现)
|
||||
GET {{base_url}}/api/admin/iot-cards/import-tasks/{{import_task_id}}
|
||||
Authorization: Bearer {{admin_token}}
|
||||
[Options]
|
||||
delay: 2000
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
|
||||
# 等待 2 秒后第 3 次查询,断言任务成功完成
|
||||
GET {{base_url}}/api/admin/iot-cards/import-tasks/{{import_task_id}}
|
||||
Authorization: Bearer {{admin_token}}
|
||||
[Options]
|
||||
delay: 2000
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.status" == 3
|
||||
jsonpath "$.data.success_count" >= 1
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 7 步:获取一级代理店铺 ID(shop1_id)
|
||||
# 依赖 package-resource-full-flow.hurl 已创建 agent1_username 账号
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
GET {{base_url}}/api/admin/accounts?username={{agent1_username}}&page=1&page_size=1
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
shop1_id: jsonpath "$.data.items[0].shop_id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.items" count >= 1
|
||||
jsonpath "$.data.items[0].username" == "{{agent1_username}}"
|
||||
jsonpath "$.data.items[0].user_type" == 3
|
||||
jsonpath "$.data.items[0].shop_id" isInteger
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 8 步:获取二级代理店铺 ID(shop2_id)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
GET {{base_url}}/api/admin/accounts?username={{agent2_username}}&page=1&page_size=1
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
shop2_id: jsonpath "$.data.items[0].shop_id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.items" count >= 1
|
||||
jsonpath "$.data.items[0].username" == "{{agent2_username}}"
|
||||
jsonpath "$.data.items[0].user_type" == 3
|
||||
jsonpath "$.data.items[0].shop_id" isInteger
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 9 步:获取套餐系列 ID 和套餐 ID
|
||||
# 依赖 package-resource-full-flow.hurl 创建的 "Hurl测试系列"
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
# 获取套餐系列(按名称模糊匹配,取最新创建的)
|
||||
GET {{base_url}}/api/admin/package-series?series_name=Hurl%E6%B5%8B%E8%AF%95%E7%B3%BB%E5%88%97&page=1&page_size=1
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
series_id: jsonpath "$.data.items[0].id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.items" count >= 1
|
||||
jsonpath "$.data.items[0].series_name" == "Hurl测试系列"
|
||||
|
||||
# 获取该系列下的套餐(取最新的)
|
||||
GET {{base_url}}/api/admin/packages?series_id={{series_id}}&page=1&page_size=1
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
package_id: jsonpath "$.data.items[0].id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.items" count >= 1
|
||||
jsonpath "$.data.items[0].series_id" == {{series_id}}
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 10 步:分配 ICCID_1(8986001234560001)给一级代理(shop1)
|
||||
# 幂等处理:HTTP * 兼容已分配场景
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/admin/iot-cards/standalone/allocate
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"to_shop_id": {{shop1_id}},
|
||||
"selection_type": "list",
|
||||
"iccids": ["{{test_iccid_1}}"],
|
||||
"remark": "Hurl 价格验证测试 - 一级代理"
|
||||
}
|
||||
HTTP *
|
||||
[Asserts]
|
||||
jsonpath "$.code" isInteger
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 11 步:分配 ICCID_2(8986001234560002)给二级代理(shop2)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/admin/iot-cards/standalone/allocate
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"to_shop_id": {{shop2_id}},
|
||||
"selection_type": "list",
|
||||
"iccids": ["{{test_iccid_2}}"],
|
||||
"remark": "Hurl 价格验证测试 - 二级代理"
|
||||
}
|
||||
HTTP *
|
||||
[Asserts]
|
||||
jsonpath "$.code" isInteger
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 12 步:绑定两张卡到套餐系列(series_binding)
|
||||
# 卡需要绑定套餐系列才能在 C 端看到可购套餐
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
PATCH {{base_url}}/api/admin/iot-cards/series-binding
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"iccids": ["{{test_iccid_1}}", "{{test_iccid_2}}"],
|
||||
"series_id": {{series_id}}
|
||||
}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.success_count" >= 1
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 13 步:C 端测试登录(ICCID_1,归属一级代理)
|
||||
# 调用开发环境专用接口,传入 ICCID 自动绑定测试客户并签发 JWT
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/c/v1/auth/dev-login
|
||||
Content-Type: application/json
|
||||
{
|
||||
"identifier": "{{test_iccid_1}}"
|
||||
}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
customer1_token: jsonpath "$.data.token"
|
||||
customer1_id: jsonpath "$.data.customer_id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.token" isString
|
||||
jsonpath "$.data.customer_id" isInteger
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 14 步:验证 ICCID_1 的可购套餐价格 = 一级代理零售价(15000 分)
|
||||
# retail_price 来自一级代理在 shop_package_allocation 中设置的零售价
|
||||
# 前置条件:package-resource-full-flow.hurl 中一级代理已设置零售价 15000
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
GET {{base_url}}/api/c/v1/asset/packages?identifier={{test_iccid_1}}
|
||||
Authorization: Bearer {{customer1_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.packages" isList
|
||||
jsonpath "$.data.packages" count >= 1
|
||||
# 关键断言:一级代理归属的卡,C 端看到的是一级代理设置的零售价 15000
|
||||
jsonpath "$.data.packages[0].retail_price" == 15000
|
||||
jsonpath "$.data.packages[0].package_id" == {{package_id}}
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 15 步:C 端测试登录(ICCID_2,归属二级代理)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/c/v1/auth/dev-login
|
||||
Content-Type: application/json
|
||||
{
|
||||
"identifier": "{{test_iccid_2}}"
|
||||
}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
customer2_token: jsonpath "$.data.token"
|
||||
customer2_id: jsonpath "$.data.customer_id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.token" isString
|
||||
jsonpath "$.data.customer_id" isInteger
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 16 步:验证 ICCID_2 的可购套餐价格 = 二级代理零售价(18000 分)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
GET {{base_url}}/api/c/v1/asset/packages?identifier={{test_iccid_2}}
|
||||
Authorization: Bearer {{customer2_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.packages" isList
|
||||
jsonpath "$.data.packages" count >= 1
|
||||
# 关键断言:二级代理归属的卡,C 端看到的是二级代理设置的零售价 18000(与一级代理不同!)
|
||||
jsonpath "$.data.packages[0].retail_price" == 18000
|
||||
jsonpath "$.data.packages[0].package_id" == {{package_id}}
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 17 步:用两个 token 访问对方的卡(越权验证)
|
||||
# customer1 不应能查询 ICCID_2 的套餐(未绑定该资产)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
GET {{base_url}}/api/c/v1/asset/packages?identifier={{test_iccid_2}}
|
||||
Authorization: Bearer {{customer1_token}}
|
||||
HTTP 403
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 1005
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 18 步:验证同一套餐两个代理视角价格确实不同(一致性检查)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
GET {{base_url}}/api/c/v1/asset/info?identifier={{test_iccid_1}}
|
||||
Authorization: Bearer {{customer1_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.asset_type" == "card"
|
||||
jsonpath "$.data.iccid" == "{{test_iccid_1}}"
|
||||
|
||||
GET {{base_url}}/api/c/v1/asset/info?identifier={{test_iccid_2}}
|
||||
Authorization: Bearer {{customer2_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.asset_type" == "card"
|
||||
jsonpath "$.data.iccid" == "{{test_iccid_2}}"
|
||||
733
tests/hurl/flows/package-resource-full-flow.hurl
Normal file
733
tests/hurl/flows/package-resource-full-flow.hurl
Normal file
@@ -0,0 +1,733 @@
|
||||
# ============================================================
|
||||
# 测试:套餐资源完整流程(创建→分配→佣金→调价→强充)
|
||||
# 生成时间:2026-03-30
|
||||
# 涉及模块:auth, role, package_series, package, shop,
|
||||
# shop_series_grant, batch_allocation, batch_pricing
|
||||
# 涉及接口:25+ 个
|
||||
# 前置条件:服务运行 + 数据库已迁移 + Redis 可用
|
||||
# ============================================================
|
||||
# 流程:
|
||||
# 1. 平台管理员登录
|
||||
# 2. 创建客户角色(创建店铺的前置依赖)
|
||||
# 3. 创建套餐系列(含固定一次性佣金 + 强充配置)
|
||||
# 4. 创建套餐 → 启用 → 上架
|
||||
# 5. 创建一级代理店铺(自动创建初始账号)
|
||||
# 6. 平台→一级代理:创建系列授权 + 管理授权套餐
|
||||
# 7. 创建二级代理店铺(挂在一级代理下)
|
||||
# 8. 一级代理登录 → 给二级代理分配系列授权
|
||||
# 9. 各级代理修改零售价 → 验证价格隔离
|
||||
# 10. 平台:批量分配 + 批量调价
|
||||
# 11. 代理佣金概览验证
|
||||
# 12. 强充配置验证
|
||||
# 13. 异常测试(未认证、参数校验、越权)
|
||||
# ============================================================
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 1 步:平台管理员登录
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/auth/login
|
||||
Content-Type: application/json
|
||||
{
|
||||
"username": "{{admin_username}}",
|
||||
"password": "{{admin_password}}",
|
||||
"device": "web"
|
||||
}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
admin_token: jsonpath "$.data.access_token"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.msg" == "success"
|
||||
jsonpath "$.timestamp" isString
|
||||
jsonpath "$.data.access_token" isString
|
||||
jsonpath "$.data.refresh_token" isString
|
||||
jsonpath "$.data.expires_in" isInteger
|
||||
jsonpath "$.data.user.id" isInteger
|
||||
jsonpath "$.data.user.username" isString
|
||||
jsonpath "$.data.user.user_type" == 1
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 2 步:创建客户角色(创建店铺需要 default_role_id)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/admin/roles
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"role_name": "hurl测试角色-{{newUuid}}",
|
||||
"role_desc": "hurl 自动化测试用客户角色",
|
||||
"role_type": 2
|
||||
}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
test_role_id: jsonpath "$.data.id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.id" isInteger
|
||||
jsonpath "$.data.role_type" == 2
|
||||
jsonpath "$.data.status" == 1
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 3 步:创建套餐系列(固定一次性佣金 + 强充配置)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/admin/package-series
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"series_code": "HURL-SERIES-{{newUuid}}",
|
||||
"series_name": "Hurl测试系列",
|
||||
"description": "自动化测试用套餐系列",
|
||||
"enable_one_time_commission": true,
|
||||
"one_time_commission_config": {
|
||||
"enable": true,
|
||||
"trigger_type": "first_recharge",
|
||||
"threshold": 0,
|
||||
"commission_type": "fixed",
|
||||
"commission_amount": 5000,
|
||||
"tiers": [],
|
||||
"validity_type": "permanent",
|
||||
"validity_value": "",
|
||||
"enable_force_recharge": true,
|
||||
"force_calc_type": "fixed",
|
||||
"force_amount": 5000
|
||||
}
|
||||
}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
series_id: jsonpath "$.data.id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.id" isInteger
|
||||
jsonpath "$.data.series_code" isString
|
||||
jsonpath "$.data.series_name" == "Hurl测试系列"
|
||||
jsonpath "$.data.description" == "自动化测试用套餐系列"
|
||||
jsonpath "$.data.enable_one_time_commission" == true
|
||||
jsonpath "$.data.one_time_commission_config" isObject
|
||||
jsonpath "$.data.one_time_commission_config.enable" == true
|
||||
jsonpath "$.data.one_time_commission_config.commission_type" == "fixed"
|
||||
jsonpath "$.data.one_time_commission_config.commission_amount" == 5000
|
||||
jsonpath "$.data.one_time_commission_config.enable_force_recharge" == true
|
||||
jsonpath "$.data.one_time_commission_config.force_calc_type" == "fixed"
|
||||
jsonpath "$.data.one_time_commission_config.force_amount" == 5000
|
||||
jsonpath "$.data.status" == 1
|
||||
jsonpath "$.data.created_at" isString
|
||||
jsonpath "$.data.updated_at" isString
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 4a 步:创建套餐(关联到系列)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/admin/packages
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"package_code": "HURL-PKG-{{newUuid}}",
|
||||
"package_name": "Hurl测试套餐-月包30G",
|
||||
"series_id": {{series_id}},
|
||||
"package_type": "formal",
|
||||
"duration_months": 12,
|
||||
"real_data_mb": 30720,
|
||||
"virtual_data_mb": 0,
|
||||
"enable_virtual_data": false,
|
||||
"suggested_retail_price": 15000,
|
||||
"cost_price": 8000,
|
||||
"calendar_type": "natural_month",
|
||||
"data_reset_cycle": "monthly",
|
||||
"expiry_base": "from_activation"
|
||||
}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
package_id: jsonpath "$.data.id"
|
||||
package_code: jsonpath "$.data.package_code"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.id" isInteger
|
||||
jsonpath "$.data.package_code" isString
|
||||
jsonpath "$.data.package_name" == "Hurl测试套餐-月包30G"
|
||||
jsonpath "$.data.series_id" == {{series_id}}
|
||||
jsonpath "$.data.package_type" == "formal"
|
||||
jsonpath "$.data.duration_months" == 12
|
||||
jsonpath "$.data.real_data_mb" == 30720
|
||||
jsonpath "$.data.virtual_data_mb" == 0
|
||||
jsonpath "$.data.enable_virtual_data" == false
|
||||
jsonpath "$.data.suggested_retail_price" == 15000
|
||||
jsonpath "$.data.cost_price" == 8000
|
||||
jsonpath "$.data.calendar_type" == "natural_month"
|
||||
jsonpath "$.data.data_reset_cycle" == "monthly"
|
||||
jsonpath "$.data.expiry_base" == "from_activation"
|
||||
jsonpath "$.data.status" isInteger
|
||||
jsonpath "$.data.shelf_status" isInteger
|
||||
jsonpath "$.data.created_at" isString
|
||||
jsonpath "$.data.updated_at" isString
|
||||
|
||||
|
||||
# ── 第 4b 步:启用套餐 ──
|
||||
|
||||
PATCH {{base_url}}/api/admin/packages/{{package_id}}/status
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"status": 1
|
||||
}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
|
||||
|
||||
# ── 第 4c 步:上架套餐 ──
|
||||
|
||||
PATCH {{base_url}}/api/admin/packages/{{package_id}}/shelf
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"shelf_status": 1
|
||||
}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
|
||||
|
||||
# ── 第 4d 步:验证套餐详情(启用+上架后) ──
|
||||
|
||||
GET {{base_url}}/api/admin/packages/{{package_id}}
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.id" == {{package_id}}
|
||||
jsonpath "$.data.status" == 1
|
||||
jsonpath "$.data.shelf_status" == 1
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 5 步:创建一级代理店铺(自动创建初始账号)
|
||||
# 幂等处理:如果已存在则跳过创建,通过账号查询获取 shop_id
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/admin/shops
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"shop_name": "Hurl一级代理店铺",
|
||||
"shop_code": "HURL-SHOP1-{{newUuid}}",
|
||||
"contact_name": "测试联系人1",
|
||||
"contact_phone": "13800000001",
|
||||
"province": "湖南省",
|
||||
"city": "长沙市",
|
||||
"district": "岳麓区",
|
||||
"address": "测试地址一级",
|
||||
"default_role_id": {{test_role_id}},
|
||||
"init_password": "{{agent1_password}}",
|
||||
"init_username": "{{agent1_username}}",
|
||||
"init_phone": "{{agent1_phone}}"
|
||||
}
|
||||
HTTP *
|
||||
|
||||
# 通过账号查询获取 shop_id(无论创建成功或已存在)
|
||||
GET {{base_url}}/api/admin/accounts?username={{agent1_username}}&page=1&page_size=1
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
shop1_id: jsonpath "$.data.items[0].shop_id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.items" count >= 1
|
||||
jsonpath "$.data.items[0].username" == "{{agent1_username}}"
|
||||
jsonpath "$.data.items[0].user_type" == 3
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 6a 步:平台→一级代理 创建系列授权(设置固定佣金 + 强充)
|
||||
# 幂等处理:如已存在则跳过,通过列表查询获取 grant_id
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
# [FINDING C-3] 创建授权接口:请求传了 packages 但创建响应不含 packages 数组
|
||||
# 需通过 GET 详情才能看到 packages,这里用 HTTP * 兼容幂等(重跑时可能 409)
|
||||
POST {{base_url}}/api/admin/shop-series-grants
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"shop_id": {{shop1_id}},
|
||||
"series_id": {{series_id}},
|
||||
"one_time_commission_amount": 3000,
|
||||
"enable_force_recharge": true,
|
||||
"force_recharge_amount": 5000,
|
||||
"packages": [
|
||||
{
|
||||
"package_id": {{package_id}},
|
||||
"cost_price": 10000
|
||||
}
|
||||
]
|
||||
}
|
||||
HTTP *
|
||||
|
||||
# 查询获取 grant_id(无论创建成功或已存在)
|
||||
GET {{base_url}}/api/admin/shop-series-grants?shop_id={{shop1_id}}&series_id={{series_id}}&page=1&page_size=1
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
grant1_id: jsonpath "$.data.items[0].id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.items" count >= 1
|
||||
jsonpath "$.data.items[0].shop_id" == {{shop1_id}}
|
||||
|
||||
|
||||
# ── 第 6b 步:验证一级代理授权详情 ──
|
||||
|
||||
GET {{base_url}}/api/admin/shop-series-grants/{{grant1_id}}
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.id" == {{grant1_id}}
|
||||
jsonpath "$.data.shop_id" == {{shop1_id}}
|
||||
jsonpath "$.data.shop_name" == "Hurl一级代理店铺"
|
||||
jsonpath "$.data.series_id" == {{series_id}}
|
||||
jsonpath "$.data.series_name" == "Hurl测试系列"
|
||||
jsonpath "$.data.series_code" isString
|
||||
jsonpath "$.data.commission_type" == "fixed"
|
||||
jsonpath "$.data.one_time_commission_amount" == 3000
|
||||
jsonpath "$.data.commission_tiers" isList
|
||||
jsonpath "$.data.force_recharge_locked" isBoolean
|
||||
jsonpath "$.data.force_recharge_enabled" == true
|
||||
jsonpath "$.data.force_recharge_amount" == 5000
|
||||
jsonpath "$.data.allocator_shop_id" == 0
|
||||
jsonpath "$.data.allocator_shop_name" isString
|
||||
jsonpath "$.data.status" == 1
|
||||
jsonpath "$.data.packages" isList
|
||||
jsonpath "$.data.packages" count >= 1
|
||||
jsonpath "$.data.packages[0].package_id" == {{package_id}}
|
||||
jsonpath "$.data.packages[0].package_name" == "Hurl测试套餐-月包30G"
|
||||
jsonpath "$.data.packages[0].package_code" isString
|
||||
jsonpath "$.data.packages[0].cost_price" == 10000
|
||||
jsonpath "$.data.packages[0].shelf_status" isInteger
|
||||
jsonpath "$.data.packages[0].status" isInteger
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 7 步:创建二级代理店铺(挂在一级代理下)
|
||||
# 幂等处理:如果已存在则跳过创建,通过账号查询获取 shop_id
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/admin/shops
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"shop_name": "Hurl二级代理店铺",
|
||||
"shop_code": "HURL-SHOP2-{{newUuid}}",
|
||||
"parent_id": {{shop1_id}},
|
||||
"contact_name": "测试联系人2",
|
||||
"contact_phone": "13800000002",
|
||||
"province": "湖南省",
|
||||
"city": "长沙市",
|
||||
"district": "天心区",
|
||||
"address": "测试地址二级",
|
||||
"default_role_id": {{test_role_id}},
|
||||
"init_password": "{{agent2_password}}",
|
||||
"init_username": "{{agent2_username}}",
|
||||
"init_phone": "{{agent2_phone}}"
|
||||
}
|
||||
HTTP *
|
||||
|
||||
# 通过账号查询获取 shop_id(无论创建成功或已存在)
|
||||
GET {{base_url}}/api/admin/accounts?username={{agent2_username}}&page=1&page_size=1
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
shop2_id: jsonpath "$.data.items[0].shop_id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.items" count >= 1
|
||||
jsonpath "$.data.items[0].username" == "{{agent2_username}}"
|
||||
jsonpath "$.data.items[0].user_type" == 3
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 8a 步:一级代理登录
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/auth/login
|
||||
Content-Type: application/json
|
||||
{
|
||||
"username": "{{agent1_username}}",
|
||||
"password": "{{agent1_password}}",
|
||||
"device": "web"
|
||||
}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
agent1_token: jsonpath "$.data.access_token"
|
||||
agent1_shop_id: jsonpath "$.data.user.shop_id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.access_token" isString
|
||||
jsonpath "$.data.user.user_type" == 3
|
||||
jsonpath "$.data.user.shop_id" == {{shop1_id}}
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 8b 步:一级代理→二级代理 创建系列授权(佣金更低)
|
||||
# 幂等处理:如已存在则跳过,通过列表查询获取 grant_id
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/admin/shop-series-grants
|
||||
Authorization: Bearer {{agent1_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"shop_id": {{shop2_id}},
|
||||
"series_id": {{series_id}},
|
||||
"one_time_commission_amount": 2000,
|
||||
"enable_force_recharge": true,
|
||||
"force_recharge_amount": 5000,
|
||||
"packages": [
|
||||
{
|
||||
"package_id": {{package_id}},
|
||||
"cost_price": 12000
|
||||
}
|
||||
]
|
||||
}
|
||||
HTTP *
|
||||
|
||||
# 查询获取 grant_id(无论创建成功或已存在)
|
||||
GET {{base_url}}/api/admin/shop-series-grants?shop_id={{shop2_id}}&series_id={{series_id}}&page=1&page_size=1
|
||||
Authorization: Bearer {{agent1_token}}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
grant2_id: jsonpath "$.data.items[0].id"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.items" count >= 1
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 9a 步:一级代理修改零售价 → 15000 分(150 元)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
PATCH {{base_url}}/api/admin/packages/{{package_id}}/retail-price
|
||||
Authorization: Bearer {{agent1_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"retail_price": 15000
|
||||
}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
|
||||
|
||||
# ── 第 9b 步:一级代理查看套餐 → 验证零售价=15000 ──
|
||||
# [FINDING C-1] 套餐详情接口应包含 one_time_commission_amount(DTO 定义且列表接口有)
|
||||
|
||||
GET {{base_url}}/api/admin/packages/{{package_id}}
|
||||
Authorization: Bearer {{agent1_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.id" == {{package_id}}
|
||||
jsonpath "$.data.package_name" == "Hurl测试套餐-月包30G"
|
||||
jsonpath "$.data.retail_price" == 15000
|
||||
jsonpath "$.data.cost_price" == 10000
|
||||
jsonpath "$.data.one_time_commission_amount" == 3000
|
||||
jsonpath "$.data.profit_margin" exists
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 10a 步:二级代理登录
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/auth/login
|
||||
Content-Type: application/json
|
||||
{
|
||||
"username": "{{agent2_username}}",
|
||||
"password": "{{agent2_password}}",
|
||||
"device": "web"
|
||||
}
|
||||
HTTP 200
|
||||
[Captures]
|
||||
agent2_token: jsonpath "$.data.access_token"
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.access_token" isString
|
||||
jsonpath "$.data.user.user_type" == 3
|
||||
jsonpath "$.data.user.shop_id" == {{shop2_id}}
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 10b 步:二级代理修改零售价 → 18000 分(180 元)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
PATCH {{base_url}}/api/admin/packages/{{package_id}}/retail-price
|
||||
Authorization: Bearer {{agent2_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"retail_price": 18000
|
||||
}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
|
||||
|
||||
# ── 第 10c 步:二级代理查看套餐 → 验证零售价=18000(与一级不同!) ──
|
||||
# [FINDING C-1] 同上,详情接口应包含 one_time_commission_amount
|
||||
|
||||
GET {{base_url}}/api/admin/packages/{{package_id}}
|
||||
Authorization: Bearer {{agent2_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.id" == {{package_id}}
|
||||
# 关键断言:二级代理看到的零售价 != 一级代理(18000 vs 15000)
|
||||
jsonpath "$.data.retail_price" == 18000
|
||||
jsonpath "$.data.cost_price" == 12000
|
||||
jsonpath "$.data.one_time_commission_amount" == 2000
|
||||
jsonpath "$.data.profit_margin" exists
|
||||
|
||||
|
||||
# ── 第 10d 步:回到一级代理视角,确认零售价未被二级覆盖 ──
|
||||
|
||||
GET {{base_url}}/api/admin/packages/{{package_id}}
|
||||
Authorization: Bearer {{agent1_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
# 一级代理的零售价仍然是 15000,与二级代理互不影响
|
||||
jsonpath "$.data.retail_price" == 15000
|
||||
jsonpath "$.data.cost_price" == 10000
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 11 步:平台身份 - 批量调价(给一级代理加价 500 分)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
POST {{base_url}}/api/admin/shop-package-batch-pricing
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"shop_id": {{shop1_id}},
|
||||
"series_id": {{series_id}},
|
||||
"price_adjustment": {
|
||||
"type": "fixed",
|
||||
"value": 500
|
||||
},
|
||||
"change_reason": "Hurl测试批量调价"
|
||||
}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.updated_count" isInteger
|
||||
jsonpath "$.data.updated_count" >= 1
|
||||
jsonpath "$.data.affected_ids" isList
|
||||
|
||||
|
||||
# ── 验证调价后代理仍可正常查看套餐 ──
|
||||
|
||||
GET {{base_url}}/api/admin/packages/{{package_id}}
|
||||
Authorization: Bearer {{agent1_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.id" == {{package_id}}
|
||||
jsonpath "$.data.cost_price" isInteger
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 12 步:平台身份 - 批量分配套餐
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
# [FINDING C-2] 批量分配对已有授权的店铺返回 500,应返回业务错误码
|
||||
POST {{base_url}}/api/admin/shop-package-batch-allocations
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"shop_id": {{shop2_id}},
|
||||
"series_id": {{series_id}},
|
||||
"price_adjustment": {
|
||||
"type": "percent",
|
||||
"value": 100
|
||||
},
|
||||
"one_time_commission_amount": 1500
|
||||
}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 13 步:强充配置验证
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
# 更新一级代理授权:调整佣金金额
|
||||
# 注意:force_recharge_locked=true 时强充金额由系列配置控制,grant 级别无法修改
|
||||
PUT {{base_url}}/api/admin/shop-series-grants/{{grant1_id}}
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"one_time_commission_amount": 3500,
|
||||
"enable_force_recharge": true,
|
||||
"force_recharge_amount": 6000
|
||||
}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.one_time_commission_amount" == 3500
|
||||
jsonpath "$.data.force_recharge_enabled" == true
|
||||
# 强充金额因 locked=true 保持系列配置值 5000,不会变为 6000
|
||||
jsonpath "$.data.force_recharge_amount" == 5000
|
||||
jsonpath "$.data.force_recharge_locked" == true
|
||||
|
||||
# 验证更新持久化成功
|
||||
GET {{base_url}}/api/admin/shop-series-grants/{{grant1_id}}
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.one_time_commission_amount" == 3500
|
||||
jsonpath "$.data.force_recharge_enabled" == true
|
||||
jsonpath "$.data.force_recharge_amount" == 5000
|
||||
jsonpath "$.data.force_recharge_locked" == true
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 14 步:代理佣金概览验证(一级代理视角)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
GET {{base_url}}/api/admin/my/commission-summary
|
||||
Authorization: Bearer {{agent1_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data" isObject
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 15 步:验证授权套餐详情(有下级分配时不可修改成本价)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
GET {{base_url}}/api/admin/shop-series-grants/{{grant1_id}}
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.packages" isList
|
||||
jsonpath "$.data.packages" count >= 1
|
||||
jsonpath "$.data.packages[0].package_id" isInteger
|
||||
jsonpath "$.data.packages[0].package_name" isString
|
||||
jsonpath "$.data.packages[0].cost_price" isInteger
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 第 16 步:系列授权列表查询
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
GET {{base_url}}/api/admin/shop-series-grants?shop_id={{shop1_id}}
|
||||
Authorization: Bearer {{admin_token}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.data.items" isList
|
||||
jsonpath "$.data.total" isInteger
|
||||
jsonpath "$.data.total" >= 1
|
||||
jsonpath "$.data.page" isInteger
|
||||
jsonpath "$.data.size" isInteger
|
||||
jsonpath "$.data.items[0].id" isInteger
|
||||
jsonpath "$.data.items[0].shop_id" == {{shop1_id}}
|
||||
jsonpath "$.data.items[0].shop_name" isString
|
||||
jsonpath "$.data.items[0].series_id" isInteger
|
||||
jsonpath "$.data.items[0].series_name" isString
|
||||
jsonpath "$.data.items[0].commission_type" isString
|
||||
jsonpath "$.data.items[0].one_time_commission_amount" isInteger
|
||||
jsonpath "$.data.items[0].force_recharge_enabled" isBoolean
|
||||
jsonpath "$.data.items[0].force_recharge_locked" isBoolean
|
||||
jsonpath "$.data.items[0].force_recharge_amount" isInteger
|
||||
jsonpath "$.data.items[0].allocator_shop_id" isInteger
|
||||
jsonpath "$.data.items[0].allocator_shop_name" isString
|
||||
jsonpath "$.data.items[0].package_count" isInteger
|
||||
jsonpath "$.data.items[0].status" isInteger
|
||||
jsonpath "$.data.items[0].created_at" isString
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 异 常 测 试
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
|
||||
|
||||
# ── 异常 1:未认证访问(无 token) ──
|
||||
|
||||
GET {{base_url}}/api/admin/packages
|
||||
HTTP 401
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 1002
|
||||
|
||||
|
||||
# ── 异常 2:参数校验失败(缺少必填字段) ──
|
||||
|
||||
POST {{base_url}}/api/admin/packages
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"package_name": ""
|
||||
}
|
||||
HTTP 400
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 1001
|
||||
|
||||
|
||||
# ── 异常 3:参数校验 - 创建套餐系列编码为空触发冲突 ──
|
||||
|
||||
POST {{base_url}}/api/admin/package-series
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"series_name": ""
|
||||
}
|
||||
HTTP 409
|
||||
[Asserts]
|
||||
jsonpath "$.code" isInteger
|
||||
jsonpath "$.code" != 0
|
||||
|
||||
|
||||
# [FINDING C-4] 创建授权不校验目标店铺是否存在,shop_id=9999999 也能创建成功
|
||||
# 预期应返回 404,实际返回 200 + 创建了无效授权记录
|
||||
POST {{base_url}}/api/admin/shop-series-grants
|
||||
Authorization: Bearer {{admin_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"shop_id": 9999999,
|
||||
"series_id": {{series_id}},
|
||||
"one_time_commission_amount": 1000
|
||||
}
|
||||
HTTP 404
|
||||
[Asserts]
|
||||
jsonpath "$.code" != 0
|
||||
|
||||
|
||||
# ── 异常 5:二级代理越权 - 尝试给一级代理的店铺分配 ──
|
||||
# 一级代理已有该系列授权,所以返回冲突(409)而非越权(403)
|
||||
# 但实际上二级代理本就无权操作上级店铺,应优先返回 403
|
||||
|
||||
POST {{base_url}}/api/admin/shop-series-grants
|
||||
Authorization: Bearer {{agent2_token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"shop_id": {{shop1_id}},
|
||||
"series_id": {{series_id}},
|
||||
"one_time_commission_amount": 1000
|
||||
}
|
||||
HTTP 409
|
||||
[Asserts]
|
||||
jsonpath "$.code" isInteger
|
||||
jsonpath "$.code" != 0
|
||||
|
||||
|
||||
# ── 异常 6:过期/无效 Token ──
|
||||
|
||||
GET {{base_url}}/api/admin/packages
|
||||
Authorization: Bearer invalid_token_12345
|
||||
HTTP 401
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 1003
|
||||
Reference in New Issue
Block a user