feat: 实现用户画像、占卜历史与后端用户管理模块

This commit is contained in:
ZL-Q
2026-04-06 01:28:10 +08:00
parent d87b2e1e3a
commit 8a18b3528b
77 changed files with 5850 additions and 2604 deletions
+143
View File
@@ -0,0 +1,143 @@
# Profile Protocol (Frontend <-> Backend)
This document defines the canonical backend contract for user profile read/write and avatar upload signing.
Protocol verification status:
- Backend model source: `backend/src/models/profile.py`
- Storage config source: `backend/src/core/config/settings.py`
- Current status: planned
## Compatibility strategy
- Current strategy: breaking changes allowed during implementation phase (no production compatibility burden).
- Once production compatibility is required, switch to additive-only evolution.
## Route overview
- Get profile: `GET /api/v1/users/me/profile`
- Update profile: `PATCH /api/v1/users/me/profile`
- Create avatar upload url: `POST /api/v1/users/me/avatar/upload-url`
- Upload avatar directly: `POST /api/v1/users/me/avatar` (multipart)
## Auth and trust boundary
- All routes require authenticated user context.
- `user_id` is derived from verified JWT `sub`; never accepted from client payload.
## Profile read contract
### `GET /api/v1/users/me/profile`
Response:
```json
{
"user_id": "uuid",
"display_name": "string",
"bio": "string|null",
"avatar_path": "avatars/{user_id}/{file}",
"avatar_url": "https://...signed-or-public...",
"settings": {
"version": 1,
"preferences": {
"interface_language": "zh-CN",
"ai_language": "zh-CN",
"timezone": "Asia/Shanghai",
"country": "CN"
},
"privacy": {},
"notification": {}
},
"updated_at": "2026-04-05T12:34:56+00:00"
}
```
Mapping note:
- `display_name` maps to `profiles.username`.
- `avatar_path` is stored in profile layer.
- `avatar_url` is render-ready URL generated from storage strategy.
## Profile update contract
### `PATCH /api/v1/users/me/profile`
Request:
```json
{
"display_name": "string(1..30)",
"bio": "string(0..200)",
"avatar_path": "avatars/{user_id}/{file}"
}
```
Rules:
- At least one field must be provided.
- `display_name` must be non-empty after trim.
- `bio` can be empty string and should be normalized to `null` only if agreed by API implementation.
- `avatar_path` must stay in current user prefix: `avatars/{current_user.id}/`.
Response:
- Returns the same shape as `GET /users/me/profile`.
## Avatar upload signing contract
### `POST /api/v1/users/me/avatar/upload-url`
Request:
```json
{
"mime_type": "image/png|image/jpeg|image/webp",
"file_size": 123456,
"ext": "png|jpg|jpeg|webp"
}
```
Response:
```json
{
"bucket": "avatars",
"path": "avatars/{user_id}/{uuid}.png",
"upload_url": "https://...signed...",
"expires_in": 600
}
```
Validation rules:
- `bucket` must equal `config.storage.avatar.bucket`.
- `file_size` must be `>0` and `<= config.storage.avatar.max_size_mb`.
- Only image mime types are allowed.
- Path must be server-generated and never trusted from client.
## Direct avatar upload contract
### `POST /api/v1/users/me/avatar`
Request:
- `multipart/form-data`
- field name: `file`
Validation rules:
- extension must be one of `png|jpg|jpeg|webp`
- mime must map to image type (`image/png|image/jpeg|image/webp`)
- payload size must be `<= config.storage.avatar.max_size_mb`
Behavior:
- backend writes avatar bytes to `bucket=config.storage.avatar.bucket`
- backend stores canonical path in profile
- response returns latest profile payload (`ProfileResponse`)
## Error contract linkage
- All errors must follow RFC7807 `application/problem+json`.
- `code` values must be registered in `docs/protocols/common/http-error-codes.md`.