feat: include account info in balance response
This commit is contained in:
@@ -180,6 +180,7 @@ class BalanceApiTests(TestCase):
|
|||||||
username=f"balance-user-{suffix}",
|
username=f"balance-user-{suffix}",
|
||||||
email=f"balance-user-{suffix}@example.com",
|
email=f"balance-user-{suffix}@example.com",
|
||||||
password="password",
|
password="password",
|
||||||
|
first_name="主账号",
|
||||||
)
|
)
|
||||||
self.api_key, self.raw_key = ApiKey.create_for_user(self.user, name="balance")
|
self.api_key, self.raw_key = ApiKey.create_for_user(self.user, name="balance")
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
@@ -225,12 +226,24 @@ class BalanceApiTests(TestCase):
|
|||||||
self.assertEqual(response.data["user"], self.user.username)
|
self.assertEqual(response.data["user"], self.user.username)
|
||||||
self.assertEqual(response.data["points_balance"], 100)
|
self.assertEqual(response.data["points_balance"], 100)
|
||||||
self.assertEqual(response.data["points_balance"], ledger_sum)
|
self.assertEqual(response.data["points_balance"], ledger_sum)
|
||||||
|
self.assertEqual(
|
||||||
|
response.data["account"],
|
||||||
|
{
|
||||||
|
"username": self.user.username,
|
||||||
|
"display_name": "主账号",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.assertNotIn("email", response.data["account"])
|
||||||
|
self.assertNotIn("id", response.data["account"])
|
||||||
|
|
||||||
def test_balance_returns_zero_without_creating_missing_wallet(self):
|
def test_balance_returns_zero_without_creating_missing_wallet(self):
|
||||||
response = self.client.get(self.url, **self.auth_header())
|
response = self.client.get(self.url, **self.auth_header())
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.data["user"], self.user.username)
|
||||||
self.assertEqual(response.data["points_balance"], 0)
|
self.assertEqual(response.data["points_balance"], 0)
|
||||||
|
self.assertEqual(response.data["account"]["username"], self.user.username)
|
||||||
|
self.assertEqual(response.data["account"]["display_name"], "主账号")
|
||||||
self.assertFalse(UserWallet.objects.filter(user=self.user).exists())
|
self.assertFalse(UserWallet.objects.filter(user=self.user).exists())
|
||||||
|
|
||||||
def test_balance_does_not_accept_web_session_without_api_key(self):
|
def test_balance_does_not_accept_web_session_without_api_key(self):
|
||||||
|
|||||||
+7
-1
@@ -109,10 +109,16 @@ class GenerateImageView(ExternalApiView):
|
|||||||
class BalanceView(ExternalApiView):
|
class BalanceView(ExternalApiView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
balance = get_balance_snapshot(request.user)
|
balance = get_balance_snapshot(request.user)
|
||||||
|
username = request.user.get_username()
|
||||||
|
display_name = request.user.get_full_name() or username
|
||||||
return Response(
|
return Response(
|
||||||
{
|
{
|
||||||
"user": request.user.get_username(),
|
"user": username,
|
||||||
"points_balance": balance.points_balance,
|
"points_balance": balance.points_balance,
|
||||||
|
"account": {
|
||||||
|
"username": username,
|
||||||
|
"display_name": display_name,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
status=status.HTTP_200_OK,
|
status=status.HTTP_200_OK,
|
||||||
)
|
)
|
||||||
|
|||||||
+8
-2
@@ -21,7 +21,7 @@ T-301 已实现对外 API 鉴权基线:`apps.api.authentication.ApiKeyAuthenti
|
|||||||
|
|
||||||
T-302 已实现生成接口基线:`POST /api/v1/generate/title` 与 `POST /api/v1/generate/image` 已接入 API Key 鉴权、别名解析、计费规则、预扣点、Provider 调用、成功确认和失败退点;图片结果当前以本地 `MEDIA_ROOT` 保存并返回 `image_url`,后续可替换为对象存储。
|
T-302 已实现生成接口基线:`POST /api/v1/generate/title` 与 `POST /api/v1/generate/image` 已接入 API Key 鉴权、别名解析、计费规则、预扣点、Provider 调用、成功确认和失败退点;图片结果当前以本地 `MEDIA_ROOT` 保存并返回 `image_url`,后续可替换为对象存储。
|
||||||
|
|
||||||
T-303 已实现余额查询基线:`GET /api/v1/balance` 已接入 API Key 鉴权,返回当前 `UserWallet.points_balance`;测试覆盖响应余额与 `PointsLedger.points_delta` 累加值一致的账务场景,并确认 Web session 不能调用该外部接口。
|
T-303 已实现余额查询基线:`GET /api/v1/balance` 已接入 API Key 鉴权,返回当前 `UserWallet.points_balance`,并保留旧字段同时新增不含邮箱的 `account` 账号展示对象;测试覆盖响应余额与 `PointsLedger.points_delta` 累加值一致的账务场景,并确认 Web session 不能调用该外部接口。
|
||||||
|
|
||||||
T-601 已实现可用别名发现:`GET /api/v1/models` 已接入 API Key 鉴权,只返回当前 active 且具备对应能力的公开别名、能力、是否需要原图和点数单价;接口不调用 `AiModel.to_resolved_model()`,不解密 provider key,不返回底层 SKU、模型 URL、`api_key_encrypted`、`extra_body` 等内部配置,且成功请求不占用生成接口限流额度。
|
T-601 已实现可用别名发现:`GET /api/v1/models` 已接入 API Key 鉴权,只返回当前 active 且具备对应能力的公开别名、能力、是否需要原图和点数单价;接口不调用 `AiModel.to_resolved_model()`,不解密 provider key,不返回底层 SKU、模型 URL、`api_key_encrypted`、`extra_body` 等内部配置,且成功请求不占用生成接口限流额度。
|
||||||
|
|
||||||
@@ -141,10 +141,16 @@ T-504/T-505 已实现用户端充值页基线:`/recharge` 走 Django session +
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"user": "demo-client",
|
"user": "demo-client",
|
||||||
"points_balance": 88
|
"points_balance": 88,
|
||||||
|
"account": {
|
||||||
|
"username": "demo-client",
|
||||||
|
"display_name": "主账号"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
要点:`user` 与 `points_balance` 是旧客户端兼容字段;`account` 供桌面端测试连接时展示账号信息。`account.display_name` 取用户姓名,未设置时回退用户名;不返回邮箱、数据库用户 ID 或其他个人敏感字段。
|
||||||
|
|
||||||
### `GET /api/v1/models`
|
### `GET /api/v1/models`
|
||||||
|
|
||||||
查询当前可调用的能力别名。成功响应:
|
查询当前可调用的能力别名。成功响应:
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+18
@@ -1282,6 +1282,24 @@
|
|||||||
- ID 说明:T-605 已用于「免邮箱验证」,故本任务用 T-606。
|
- ID 说明:T-605 已用于「免邮箱验证」,故本任务用 T-606。
|
||||||
- 下一步:实现 T-606(路由改造 + DownloadRelease 模型/admin/迁移 + 首页/下载 SSR 模板 + 测试)。
|
- 下一步:实现 T-606(路由改造 + DownloadRelease 模型/admin/迁移 + 首页/下载 SSR 模板 + 测试)。
|
||||||
|
|
||||||
|
## 2026-07-06 变更:余额接口返回桌面端测试连接账号信息
|
||||||
|
|
||||||
|
- 背景:桌面端“测试连接”希望在余额点数外展示当前 API Key 所属账号。为兼容已有调用方,保留旧字段 `user` 与 `points_balance`,新增 `account` 对象;按决策不返回邮箱。
|
||||||
|
- 文档先行:先更新 `docs/api.md` 的 `GET /api/v1/balance` 成功响应结构:
|
||||||
|
- `user`: 旧兼容字段,用户名。
|
||||||
|
- `points_balance`: 当前点数余额。
|
||||||
|
- `account.username`: 用户名。
|
||||||
|
- `account.display_name`: 用户姓名;未设置时回退用户名。
|
||||||
|
- 不返回 `email`、数据库用户 ID 或其他敏感字段。
|
||||||
|
- 代码变更:
|
||||||
|
- `apps/api/views.py`:`BalanceView` 响应新增 `account`,`display_name = request.user.get_full_name() or username`。
|
||||||
|
- `apps/api/tests.py`:更新 `BalanceApiTests`,断言旧字段保留、新字段存在,且 `account` 不含 `email` / `id`。
|
||||||
|
- 验证:
|
||||||
|
- `py -3.12 -m py_compile apps\api\views.py apps\api\tests.py`:通过。
|
||||||
|
- `py -3.12 manage.py check`:通过,0 issues。
|
||||||
|
- `py -3.12 manage.py test apps.api.tests.BalanceApiTests --keepdb --noinput --verbosity 2`:通过,3 tests OK。
|
||||||
|
- 测试期仅保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
|
||||||
|
|
||||||
## 2026-07-06 决策:T-606 首页视觉方向定为 v1「生成台」
|
## 2026-07-06 决策:T-606 首页视觉方向定为 v1「生成台」
|
||||||
|
|
||||||
- 背景:为 T-606(公开首页 + 客户端下载)出了两版 SVG 原型,写入 `prototypes/`:
|
- 背景:为 T-606(公开首页 + 客户端下载)出了两版 SVG 原型,写入 `prototypes/`:
|
||||||
|
|||||||
Reference in New Issue
Block a user