Admin POST/PUT 复用 admin.cms.write;C 端 GET /home/feed-slots;首页无 active 槽隐藏推荐区、失败回退展示;无新 migration。 Co-authored-by: Cursor <cursoragent@cursor.com>
2558 lines
66 KiB
YAML
2558 lines
66 KiB
YAML
openapi: 3.0.3
|
||
info:
|
||
title: YuXinGu API
|
||
version: 0.2.0
|
||
description: |
|
||
愈心谷 P1 API 契约(冻结草案)。
|
||
用户可见文案遵循 .ai/product/lexicon.md。
|
||
统一信封:{ code, message, data };code=0 成功。
|
||
用户自由文字不合规(昵称/档案名/问答/备注等):HTTP 400 · code 40060 · message「文案不合规:…」
|
||
(见 feature-spec/input-compliance;后端 textsafe 权威校验)。
|
||
servers:
|
||
- url: http://127.0.0.1:8080
|
||
tags:
|
||
- name: system
|
||
- name: profile
|
||
- name: portrait
|
||
- name: scale
|
||
- name: relation
|
||
- name: report
|
||
- name: ask
|
||
- name: companion
|
||
- name: commerce
|
||
- name: admin
|
||
|
||
paths:
|
||
/api/v1/healthz:
|
||
get:
|
||
tags: [system]
|
||
summary: Liveness
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Envelope'
|
||
|
||
/api/v1/ping:
|
||
get:
|
||
tags: [system]
|
||
summary: Ping
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/auth/me:
|
||
get:
|
||
tags: [system]
|
||
summary: Current account
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
patch:
|
||
tags: [system]
|
||
summary: Update account nickname
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [nickname]
|
||
properties:
|
||
nickname: { type: string, maxLength: 16 }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/auth/me/avatar:
|
||
post:
|
||
tags: [system]
|
||
summary: Upload account avatar (multipart file)
|
||
description: jpeg/png/webp · max 2MB · field name `file` · returns AuthUser with avatar_url
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
multipart/form-data:
|
||
schema:
|
||
type: object
|
||
required: [file]
|
||
properties:
|
||
file: { type: string, format: binary }
|
||
responses:
|
||
'200':
|
||
description: OK · AuthUser with avatar_url
|
||
'400':
|
||
description: bad type / too large / empty (code 40114)
|
||
'401':
|
||
description: unauthorized
|
||
|
||
/api/v1/media/avatars/{file}:
|
||
get:
|
||
tags: [system]
|
||
summary: Serve account avatar image
|
||
description: Public read; `{uuid}.{jpg|jpeg|png|webp}` only; path traversal rejected
|
||
parameters:
|
||
- name: file
|
||
in: path
|
||
required: true
|
||
schema: { type: string }
|
||
responses:
|
||
'200':
|
||
description: image bytes
|
||
'404':
|
||
description: missing or invalid name
|
||
|
||
/api/v1/admin/auth/login:
|
||
post:
|
||
tags: [admin]
|
||
summary: Admin login
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [username, password]
|
||
properties:
|
||
username: { type: string }
|
||
password: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Envelope'
|
||
|
||
/api/v1/admin/auth/logout:
|
||
post:
|
||
tags: [admin]
|
||
summary: Admin logout
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/me:
|
||
get:
|
||
tags: [admin]
|
||
summary: Current admin (role + permissions[])
|
||
responses:
|
||
'200':
|
||
description: OK (id, username, role, permissions)
|
||
content:
|
||
application/json:
|
||
schema:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data:
|
||
$ref: '#/components/schemas/AdminMe'
|
||
|
||
/api/v1/admin/roles:
|
||
get:
|
||
tags: [admin]
|
||
summary: List admin roles
|
||
description: Requires admin.roles.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/roles/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Role detail with permissions
|
||
description: Requires admin.roles.read
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'403':
|
||
description: Forbidden
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/roles/{id}/permissions:
|
||
put:
|
||
tags: [admin]
|
||
summary: Replace role permission set
|
||
description: Requires admin.roles.write; writes AuditLog
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [permissions]
|
||
properties:
|
||
permissions:
|
||
type: array
|
||
items: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'400':
|
||
description: Invalid permission code
|
||
'403':
|
||
description: Forbidden
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/users:
|
||
get:
|
||
tags: [admin]
|
||
summary: List users
|
||
parameters:
|
||
- in: query
|
||
name: q
|
||
schema: { type: string }
|
||
- in: query
|
||
name: limit
|
||
schema: { type: integer }
|
||
- in: query
|
||
name: offset
|
||
schema: { type: integer }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/users/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: User detail
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/users/{id}/membership/grant:
|
||
post:
|
||
tags: [admin]
|
||
summary: Grant or extend membership
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [plan]
|
||
properties:
|
||
plan: { type: string, enum: [month, quarter, year] }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'403':
|
||
description: Forbidden (ops role)
|
||
|
||
/api/v1/admin/users/{id}/ban:
|
||
post:
|
||
tags: [admin]
|
||
summary: Ban user (Ops-E)
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/users/{id}/unban:
|
||
post:
|
||
tags: [admin]
|
||
summary: Unban user (Ops-E)
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/admins:
|
||
get:
|
||
tags: [admin]
|
||
summary: List admin accounts (super only)
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/admins/{id}:
|
||
patch:
|
||
tags: [admin]
|
||
summary: Update admin role (super only)
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [role]
|
||
properties:
|
||
role: { type: string, enum: [super, ops] }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'409':
|
||
description: Cannot demote last super
|
||
|
||
/api/v1/admin/push-jobs:
|
||
get:
|
||
tags: [admin]
|
||
summary: List push job stubs (Ops-E)
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
post:
|
||
tags: [admin]
|
||
summary: Create push draft (no dispatch)
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [title]
|
||
properties:
|
||
title: { type: string }
|
||
body: { type: string }
|
||
audience: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/push-jobs/{id}:
|
||
patch:
|
||
tags: [admin]
|
||
summary: Update push stub (draft or cancelled)
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/users/{id}/status:
|
||
post:
|
||
tags: [admin]
|
||
summary: Transition UserStatus
|
||
description: Requires admin.users.status.write; writes transition + AuditLog
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [status, reason]
|
||
properties:
|
||
status: { type: string, enum: [active, disabled, banned, suspended] }
|
||
reason: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'400':
|
||
description: Invalid edge or missing reason
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/users/{id}/status-transitions:
|
||
get:
|
||
tags: [admin]
|
||
summary: List UserStatus transitions
|
||
description: Requires admin.users.read
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
- in: query
|
||
name: limit
|
||
schema: { type: integer }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
|
||
/api/v1/admin/users/{id}/insight:
|
||
get:
|
||
tags: [admin]
|
||
summary: UserIntelligence insight (read-only aggregate)
|
||
description: Requires admin.users.read; no schema write
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
'404':
|
||
description: User not found
|
||
|
||
/api/v1/admin/users/{id}/entitlements:
|
||
get:
|
||
tags: [admin]
|
||
summary: User Entitlement aggregate (read-only)
|
||
description: Requires admin.users.read; Membership ∪ DeepAccess ∪ ask quotas
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'404':
|
||
description: User not found
|
||
|
||
/api/v1/admin/membership-plans:
|
||
get:
|
||
tags: [admin]
|
||
summary: List membership plans
|
||
description: Requires admin.membership.plans.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/membership-plans/{code}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get membership plan
|
||
parameters:
|
||
- in: path
|
||
name: code
|
||
required: true
|
||
schema: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
put:
|
||
tags: [admin]
|
||
summary: Update membership plan
|
||
description: Requires admin.membership.plans.write
|
||
parameters:
|
||
- in: path
|
||
name: code
|
||
required: true
|
||
schema: { type: string }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [title, duration_days, amount_cents]
|
||
properties:
|
||
title: { type: string }
|
||
duration_days: { type: integer }
|
||
amount_cents: { type: integer }
|
||
active: { type: boolean }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'400':
|
||
description: Invalid
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/redemption-batches:
|
||
get:
|
||
tags: [admin]
|
||
summary: List redemption batches
|
||
responses: { '200': { description: OK } }
|
||
post:
|
||
tags: [admin]
|
||
summary: Create redemption batch
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [label, plan_code, quantity]
|
||
properties:
|
||
label: { type: string }
|
||
plan_code: { type: string }
|
||
quantity: { type: integer, minimum: 1, maximum: 100 }
|
||
responses: { '200': { description: OK } }
|
||
|
||
/api/v1/admin/redemption-batches/{id}/codes:
|
||
get:
|
||
tags: [admin]
|
||
summary: List codes in batch
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses: { '200': { description: OK } }
|
||
|
||
/api/v1/admin/redemption-codes/{id}/disable:
|
||
post:
|
||
tags: [admin]
|
||
summary: Disable unused code
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses: { '200': { description: OK } }
|
||
|
||
/api/v1/admin/ask/threads:
|
||
get:
|
||
tags: [admin]
|
||
summary: List AskSessionView (read-only)
|
||
description: Requires admin.ask.read
|
||
parameters:
|
||
- in: query
|
||
name: user_id
|
||
schema: { type: string, format: uuid }
|
||
- in: query
|
||
name: limit
|
||
schema: { type: integer }
|
||
- in: query
|
||
name: offset
|
||
schema: { type: integer }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/ask/threads/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Ask session meta (no message bodies)
|
||
description: Requires admin.ask.read; transcript via /messages
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/ask/threads/{id}/messages:
|
||
get:
|
||
tags: [admin]
|
||
summary: Ask session transcript (audited)
|
||
description: Requires admin.ask.transcript.read; writes ask.transcript.read audit
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/content-safety/filter-rules:
|
||
get:
|
||
tags: [admin]
|
||
summary: List FilterRule
|
||
description: Requires admin.content_safety.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/content-safety/filter-rules/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get FilterRule
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/content-safety/evaluate:
|
||
post:
|
||
tags: [admin]
|
||
summary: Preview FilterRule matches (no ModerationCase write)
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [text]
|
||
properties:
|
||
text: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/ask/feedback:
|
||
get:
|
||
tags: [admin]
|
||
summary: List QualityFeedback
|
||
description: Requires admin.ask.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
|
||
/api/v1/admin/ask/threads/{id}/feedback:
|
||
post:
|
||
tags: [admin]
|
||
summary: Create QualityFeedback (ops)
|
||
description: Requires admin.ask.feedback.write; writes AuditLog
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [rating]
|
||
properties:
|
||
rating: { type: integer, minimum: 1, maximum: 5 }
|
||
tag: { type: string, enum: [helpful, off_topic, unsafe, other] }
|
||
note: { type: string }
|
||
message_id: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'400':
|
||
description: Invalid
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/ask/threads/{id}/feedback:
|
||
post:
|
||
tags: [ask]
|
||
summary: User QualityFeedback on owned thread
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [rating]
|
||
properties:
|
||
rating: { type: integer, minimum: 1, maximum: 5 }
|
||
tag: { type: string }
|
||
note: { type: string }
|
||
message_id: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/ai/system-prompts:
|
||
get:
|
||
tags: [admin]
|
||
summary: List SystemPrompt catalog
|
||
description: Requires admin.ai_config.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/ai/system-prompts/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get SystemPrompt
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/ai/knowledge-sources:
|
||
get:
|
||
tags: [admin]
|
||
summary: List KnowledgeSource catalog
|
||
description: Requires admin.ai_config.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/ai/knowledge-sources/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get KnowledgeSource
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/cms/banners:
|
||
get:
|
||
tags: [admin]
|
||
summary: List OpsCMS Banner catalog
|
||
description: Requires admin.cms.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
post:
|
||
tags: [admin]
|
||
summary: Create Banner
|
||
description: Requires admin.cms.write · ECR-041
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'400':
|
||
description: Invalid
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
'409':
|
||
description: Code conflict
|
||
|
||
/api/v1/admin/cms/banners/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get Banner
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
put:
|
||
tags: [admin]
|
||
summary: Update Banner
|
||
description: Requires admin.cms.write · deactivate via active=false
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'400':
|
||
description: Invalid
|
||
'403':
|
||
description: Forbidden
|
||
'404':
|
||
description: Not found
|
||
'409':
|
||
description: Code conflict
|
||
|
||
/api/v1/admin/cms/feed-slots:
|
||
get:
|
||
tags: [admin]
|
||
summary: List OpsCMS FeedSlot catalog
|
||
description: Requires admin.cms.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
post:
|
||
tags: [admin]
|
||
summary: Create FeedSlot
|
||
description: Requires admin.cms.write · ECR-042
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'400':
|
||
description: Invalid
|
||
'403':
|
||
description: Forbidden
|
||
'409':
|
||
description: Conflict
|
||
|
||
/api/v1/admin/cms/feed-slots/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get FeedSlot
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
put:
|
||
tags: [admin]
|
||
summary: Update FeedSlot
|
||
description: Requires admin.cms.write · deactivate via active=false
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'400':
|
||
description: Invalid
|
||
'403':
|
||
description: Forbidden
|
||
'404':
|
||
description: Not found
|
||
'409':
|
||
description: Conflict
|
||
|
||
/api/v1/admin/cms/publications:
|
||
get:
|
||
tags: [admin]
|
||
summary: List ScheduledPublication catalog
|
||
description: Requires admin.cms.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/cms/publications/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get ScheduledPublication
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/ai/knowledge-chunks:
|
||
get:
|
||
tags: [admin]
|
||
summary: List KnowledgeChunk catalog
|
||
description: Requires admin.ai_config.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/ai/knowledge-chunks/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get KnowledgeChunk
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/ai/tools:
|
||
get:
|
||
tags: [admin]
|
||
summary: List ToolDefinition catalog
|
||
description: Requires admin.ai_config.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/ai/tools/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get ToolDefinition
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/content-safety/block-policies:
|
||
get:
|
||
tags: [admin]
|
||
summary: List BlockPolicy catalog
|
||
description: Requires admin.content_safety.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/content-safety/block-policies/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get BlockPolicy
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/content-safety/cases:
|
||
get:
|
||
tags: [admin]
|
||
summary: List ModerationCase catalog
|
||
description: Requires admin.content_safety.read; TYPE CATALOG only — not live moderation tickets
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/content-safety/cases/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get ModerationCase
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/crisis/events:
|
||
get:
|
||
tags: [admin]
|
||
summary: List CrisisEvent catalog
|
||
description: Requires admin.crisis.read; TYPE CATALOG only — not user crisis cases
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/crisis/events/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get CrisisEvent
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/crisis/interventions:
|
||
get:
|
||
tags: [admin]
|
||
summary: List InterventionOutcome catalog
|
||
description: Requires admin.crisis.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/crisis/interventions/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get InterventionOutcome
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/ask/handoffs:
|
||
get:
|
||
tags: [admin]
|
||
summary: List HandoffCase catalog
|
||
description: Requires admin.ask.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/ask/handoffs/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get HandoffCase
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/privacy/requests:
|
||
get:
|
||
tags: [admin]
|
||
summary: List PrivacyRequest catalog
|
||
description: Requires admin.privacy.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/privacy/requests/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get PrivacyRequest
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/explore/scales:
|
||
get:
|
||
tags: [admin]
|
||
summary: List ScaleDefinition read projection
|
||
description: Requires admin.explore.read · reuses scales table
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/explore/scales/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get ScaleDefinition
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/explore/star-configs:
|
||
get:
|
||
tags: [admin]
|
||
summary: List StarConfig catalog
|
||
description: Requires admin.explore.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/explore/star-configs/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get StarConfig
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/explore/rhythm-configs:
|
||
get:
|
||
tags: [admin]
|
||
summary: List RhythmConfig catalog
|
||
description: Requires admin.explore.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/explore/rhythm-configs/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get RhythmConfig
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/explore/image-card-decks:
|
||
get:
|
||
tags: [admin]
|
||
summary: List ImageCardDeck catalog
|
||
description: Requires admin.explore.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/explore/image-card-decks/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get ImageCardDeck
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/growth/report-templates:
|
||
get:
|
||
tags: [admin]
|
||
summary: List ReportTemplate catalog
|
||
description: Requires admin.growth.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/growth/report-templates/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get ReportTemplate
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/analytics/funnel-definitions:
|
||
get:
|
||
tags: [admin]
|
||
summary: List FunnelDefinition catalog
|
||
description: Requires admin.growth.read; catalog only
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/analytics/funnel-definitions/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get FunnelDefinition
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/crisis/policies:
|
||
get:
|
||
tags: [admin]
|
||
summary: List CrisisPolicy
|
||
description: Requires admin.crisis.read
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'401':
|
||
description: Unauthorized
|
||
'403':
|
||
description: Forbidden
|
||
|
||
/api/v1/admin/crisis/policies/{id}:
|
||
get:
|
||
tags: [admin]
|
||
summary: Get CrisisPolicy
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: Not found
|
||
|
||
/api/v1/admin/crisis/evaluate:
|
||
post:
|
||
tags: [admin]
|
||
summary: Preview CrisisPolicy matches (no CrisisEvent write)
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [text]
|
||
properties:
|
||
text: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/orders:
|
||
get:
|
||
tags: [admin]
|
||
summary: List orders (Ops-D filters)
|
||
parameters:
|
||
- in: query
|
||
name: status
|
||
schema: { type: string }
|
||
- in: query
|
||
name: kind
|
||
schema: { type: string }
|
||
- in: query
|
||
name: from
|
||
schema: { type: string, format: date }
|
||
- in: query
|
||
name: to
|
||
schema: { type: string, format: date }
|
||
- in: query
|
||
name: user_id
|
||
schema: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: OK (includes refund_status)
|
||
|
||
/api/v1/admin/membership/plan-prices:
|
||
get:
|
||
tags: [admin]
|
||
summary: List membership display prices
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
put:
|
||
tags: [admin]
|
||
summary: Upsert membership display prices
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/audit-logs:
|
||
get:
|
||
tags: [admin]
|
||
summary: List audit logs
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/analytics/overview:
|
||
get:
|
||
tags: [admin]
|
||
summary: Behavior analytics overview (DAU / sessions)
|
||
parameters:
|
||
- in: query
|
||
name: from
|
||
schema: { type: string, format: date }
|
||
- in: query
|
||
name: to
|
||
schema: { type: string, format: date }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/analytics/pages:
|
||
get:
|
||
tags: [admin]
|
||
summary: Page PV/UV/dwell/exit aggregates
|
||
parameters:
|
||
- in: query
|
||
name: from
|
||
schema: { type: string, format: date }
|
||
- in: query
|
||
name: to
|
||
schema: { type: string, format: date }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/analytics/exits:
|
||
get:
|
||
tags: [admin]
|
||
summary: Exit page ranking
|
||
parameters:
|
||
- in: query
|
||
name: from
|
||
schema: { type: string, format: date }
|
||
- in: query
|
||
name: to
|
||
schema: { type: string, format: date }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/analytics/clicks:
|
||
get:
|
||
tags: [admin]
|
||
summary: UI click ranking
|
||
parameters:
|
||
- in: query
|
||
name: from
|
||
schema: { type: string, format: date }
|
||
- in: query
|
||
name: to
|
||
schema: { type: string, format: date }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/analytics/funnel:
|
||
get:
|
||
tags: [admin]
|
||
summary: Core funnel step counts
|
||
parameters:
|
||
- in: query
|
||
name: from
|
||
schema: { type: string, format: date }
|
||
- in: query
|
||
name: to
|
||
schema: { type: string, format: date }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/analytics/events:
|
||
post:
|
||
tags: [system]
|
||
summary: Batch ingest behavior events (DeviceAuth)
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items]
|
||
properties:
|
||
items:
|
||
type: array
|
||
maxItems: 100
|
||
items:
|
||
type: object
|
||
required: [name, session_id, client_ts]
|
||
properties:
|
||
name: { type: string }
|
||
session_id: { type: string }
|
||
page_path: { type: string }
|
||
client_ts: {}
|
||
props: { type: object }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Envelope'
|
||
|
||
/api/v1/home/tools:
|
||
get:
|
||
tags: [system]
|
||
summary: Homepage grid tools (enabled only)
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/home/banners:
|
||
get:
|
||
tags: [system]
|
||
summary: Homepage active banners (ECR-041)
|
||
parameters:
|
||
- in: query
|
||
name: placement
|
||
schema: { type: string, default: home, enum: [home, explore, ask] }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/home/feed-slots:
|
||
get:
|
||
tags: [system]
|
||
summary: Homepage active feed slots (ECR-042)
|
||
parameters:
|
||
- in: query
|
||
name: placement
|
||
schema: { type: string, default: home, enum: [home, explore, ask] }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/home/daily-tips:
|
||
get:
|
||
tags: [system]
|
||
summary: Homepage self-card tips by 时辰 (clothing / colors / wellness)
|
||
description: >
|
||
Cached per user × Chinese double-hour (时辰). Returns immediately from cache/fallback;
|
||
LLM warm runs in background. Includes shichen, shichen_name, valid_until for client refresh.
|
||
responses:
|
||
'200':
|
||
description: OK · tips + valid_until
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeHomeDailyTips'
|
||
|
||
/api/v1/admin/home/tools:
|
||
get:
|
||
tags: [admin]
|
||
summary: List all homepage tools
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
put:
|
||
tags: [admin]
|
||
summary: Replace homepage tools
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items]
|
||
properties:
|
||
items: { type: array }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/scales:
|
||
get:
|
||
tags: [admin]
|
||
summary: List all scales (incl. draft)
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/admin/scales/{id}:
|
||
patch:
|
||
tags: [admin]
|
||
summary: Set scale status published|draft
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [status]
|
||
properties:
|
||
status: { type: string, enum: [published, draft] }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/profiles:
|
||
get:
|
||
tags: [profile]
|
||
summary: 列出个人档案
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeProfileList'
|
||
post:
|
||
tags: [profile]
|
||
summary: 创建个人档案
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/CreateProfileRequest'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data: { $ref: '#/components/schemas/Profile' }
|
||
'409':
|
||
description: Second self profile (code 40902)
|
||
patch:
|
||
tags: [profile]
|
||
summary: 更新个人档案
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data: { $ref: '#/components/schemas/Profile' }
|
||
delete:
|
||
tags: [profile]
|
||
summary: 删除个人档案
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/reports/portrait:
|
||
post:
|
||
tags: [portrait]
|
||
summary: 生成个人画像成长报告
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [profile_id]
|
||
properties:
|
||
profile_id: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: 返回 GrowthReport(detail 按权益裁剪)
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeGrowthReport'
|
||
|
||
/api/v1/explore/catalog:
|
||
get:
|
||
tags: [explore]
|
||
summary: 探索三级目录(L1 分类 → L3 工具)
|
||
responses:
|
||
'200':
|
||
description: categories[]
|
||
|
||
/api/v1/explore/catalog/{key}:
|
||
get:
|
||
tags: [explore]
|
||
summary: 单个探索分类
|
||
parameters:
|
||
- name: key
|
||
in: path
|
||
required: true
|
||
schema: { type: string }
|
||
responses:
|
||
'200':
|
||
description: Category
|
||
'404':
|
||
description: not found
|
||
|
||
/api/v1/growth/plans:
|
||
get:
|
||
tags: [growth]
|
||
summary: 成长计划列表
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
post:
|
||
tags: [growth]
|
||
summary: 创建成长计划
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/growth/plans/{id}/checkin:
|
||
post:
|
||
tags: [growth]
|
||
summary: 今日打卡
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/growth/plans/{id}/checkins:
|
||
get:
|
||
tags: [growth]
|
||
summary: 打卡记录
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/moods/recent:
|
||
get:
|
||
tags: [companion]
|
||
summary: 近七日心情轨迹
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/reports/star:
|
||
post:
|
||
tags: [star]
|
||
summary: 生成本命排盘 / 运势成长报告
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [profile_id]
|
||
properties:
|
||
profile_id: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: GrowthReport type=star(含 chart/planets/aspects_preview/outlook/transits;summary.as_of/valid_until 按自然日;无 fortune/lucky 键;detail 按权益裁剪)
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeGrowthReport'
|
||
|
||
/api/v1/reports/synastry:
|
||
post:
|
||
tags: [star]
|
||
summary: 生成双人合盘报告(五主盘 + 次限推运)
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [profile_id_a, profile_id_b]
|
||
properties:
|
||
profile_id_a: { type: string, format: uuid }
|
||
profile_id_b: { type: string, format: uuid }
|
||
as_of: { type: string, format: date, description: 次限推运日期 YYYY-MM-DD }
|
||
responses:
|
||
'200':
|
||
description: GrowthReport type=synastry(三指数 + charts 五主盘/推运)
|
||
|
||
/api/v1/synastry/nearby:
|
||
get:
|
||
tags: [star]
|
||
summary: 附近可合盘档案
|
||
parameters:
|
||
- in: query
|
||
name: lat
|
||
required: true
|
||
schema: { type: number }
|
||
- in: query
|
||
name: lng
|
||
required: true
|
||
schema: { type: number }
|
||
- in: query
|
||
name: radius_km
|
||
schema: { type: number, default: 50 }
|
||
responses:
|
||
'200':
|
||
description: items[{profile, distance_km}]
|
||
|
||
/api/v1/synastry/invites:
|
||
post:
|
||
tags: [star]
|
||
summary: 创建合盘邀请
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [profile_id]
|
||
properties:
|
||
profile_id: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: token / path / expires_at
|
||
|
||
/api/v1/synastry/invites/{token}:
|
||
get:
|
||
tags: [star]
|
||
summary: 邀请元数据
|
||
parameters:
|
||
- in: path
|
||
name: token
|
||
required: true
|
||
schema: { type: string }
|
||
responses:
|
||
'200':
|
||
description: host_name / expires_at / already_accepted
|
||
|
||
/api/v1/synastry/invites/{token}/accept:
|
||
post:
|
||
tags: [star]
|
||
summary: 接受合盘邀请并生成报告
|
||
parameters:
|
||
- in: path
|
||
name: token
|
||
required: true
|
||
schema: { type: string }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [birth_date]
|
||
properties:
|
||
display_name: { type: string }
|
||
birth_date: { type: string, format: date }
|
||
birth_time: { type: string }
|
||
birth_place: { type: string }
|
||
responses:
|
||
'200':
|
||
description: GrowthReport type=synastry
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeGrowthReport'
|
||
|
||
/api/v1/reports/rhythm:
|
||
post:
|
||
tags: [rhythm]
|
||
summary: 生成身心节律成长报告
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [profile_id]
|
||
properties:
|
||
profile_id: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: GrowthReport type=rhythm(summary.as_of/valid_until 按自然日;GET 换日原地刷新今日/本周建议)
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeGrowthReport'
|
||
|
||
/api/v1/image-cards/scenes:
|
||
get:
|
||
tags: [image-card]
|
||
summary: 意象卡片场景列表
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeImageCardScenes'
|
||
|
||
/api/v1/image-cards/quota:
|
||
get:
|
||
tags: [image-card]
|
||
summary: 今日抽卡剩余次数
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeImageCardQuota'
|
||
|
||
/api/v1/image-cards/draw:
|
||
post:
|
||
tags: [image-card]
|
||
summary: 抽意象卡片(沉淀为 GrowthReport type=image_card)
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [profile_id]
|
||
properties:
|
||
profile_id: { type: string, format: uuid }
|
||
scene: { type: string }
|
||
depth: { type: boolean, description: 请求三卡组合(无权益则 detail 剥离) }
|
||
responses:
|
||
'200':
|
||
description: report + cards + quota_left
|
||
'402':
|
||
description: 免费次数用尽
|
||
|
||
/api/v1/relation/insight:
|
||
post:
|
||
tags: [relation]
|
||
summary: 生成关系理解
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [profile_a_id, profile_b_id]
|
||
properties:
|
||
profile_a_id: { type: string, format: uuid }
|
||
profile_b_id: { type: string, format: uuid }
|
||
responses:
|
||
'200':
|
||
description: RelationInsight + 可选 report_id
|
||
|
||
/api/v1/reports:
|
||
get:
|
||
tags: [report]
|
||
summary: 列出当前用户成长报告
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeReportList'
|
||
|
||
/api/v1/reports/{id}:
|
||
get:
|
||
tags: [report]
|
||
summary: 获取成长报告(权益裁剪)
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeGrowthReport'
|
||
|
||
/api/v1/scale-bank/catalog:
|
||
get:
|
||
tags: [scale]
|
||
summary: curated 题库精选宫格 + 分类卡片(A 集非临床)
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
code: { type: integer }
|
||
message: { type: string }
|
||
data:
|
||
type: object
|
||
properties:
|
||
featured:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
slug: { type: string }
|
||
title: { type: string }
|
||
description: { type: string }
|
||
category_key: { type: string }
|
||
icon: { type: string }
|
||
path: { type: string }
|
||
categories:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
key: { type: string }
|
||
title: { type: string }
|
||
description: { type: string }
|
||
icon: { type: string }
|
||
count: { type: integer }
|
||
path: { type: string }
|
||
|
||
/api/v1/scale-bank/categories/{key}:
|
||
get:
|
||
tags: [scale]
|
||
summary: curated 题库某一分类下的量表列表
|
||
parameters:
|
||
- name: key
|
||
in: path
|
||
required: true
|
||
schema: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'404':
|
||
description: category not found
|
||
|
||
/api/v1/scales:
|
||
get:
|
||
tags: [scale]
|
||
summary: 探索测试列表
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeScaleList'
|
||
|
||
/api/v1/scales/{slug}:
|
||
get:
|
||
tags: [scale]
|
||
summary: 探索测试题目
|
||
description: >
|
||
`mbti-full` 未开通成长会员时返回 access=membership · locked=true 且 questions 为空。
|
||
curated scalebank slug 从 embed 返回题目。
|
||
parameters:
|
||
- name: slug
|
||
in: path
|
||
required: true
|
||
schema: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeScaleDetail'
|
||
|
||
/api/v1/scales/{slug}/result:
|
||
get:
|
||
tags: [scale]
|
||
summary: 当前用户该量表最近一次结果
|
||
parameters:
|
||
- name: slug
|
||
in: path
|
||
required: true
|
||
schema: { type: string }
|
||
responses:
|
||
'200':
|
||
description: ScaleSubmitResult
|
||
'404':
|
||
description: 尚无结果
|
||
post:
|
||
tags: [scale]
|
||
summary: 提交探索测试并计分
|
||
parameters:
|
||
- name: slug
|
||
in: path
|
||
required: true
|
||
schema: { type: string }
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [profile_id, answers]
|
||
properties:
|
||
profile_id: { type: string, format: uuid }
|
||
answers: { type: object }
|
||
responses:
|
||
'200':
|
||
description: 探索结果
|
||
'403':
|
||
description: mbti-full 需要成长会员(code 40310)
|
||
get:
|
||
tags: [ask]
|
||
summary: 当前问答剩余次数(免费额度或成长会员)
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeAskQuota'
|
||
|
||
/api/v1/ask/threads:
|
||
post:
|
||
tags: [ask]
|
||
summary: 创建问答线程(须绑定 profile)
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [profile_id]
|
||
properties:
|
||
profile_id: { type: string, format: uuid }
|
||
scene: { type: string, description: self|relation|career|emotion|life }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/ask/threads/{id}/messages:
|
||
get:
|
||
tags: [ask]
|
||
summary: 拉取线程消息历史
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
post:
|
||
tags: [ask]
|
||
summary: 发送消息(扣额度;P1 规则引擎回复)
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [content]
|
||
properties:
|
||
content: { type: string }
|
||
responses:
|
||
'200':
|
||
description: user + assistant + quota
|
||
'402':
|
||
description: 额度用尽
|
||
|
||
/api/v1/solar-terms/today:
|
||
get:
|
||
tags: [companion]
|
||
summary: 今日节气生活建议
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/moods:
|
||
post:
|
||
tags: [companion]
|
||
summary: 记录/更新当日心情
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
score: { type: integer, minimum: 1, maximum: 5 }
|
||
note: { type: string, maxLength: 200 }
|
||
day: { type: string, format: date }
|
||
responses:
|
||
'200':
|
||
description: Mood
|
||
|
||
/api/v1/moods/today:
|
||
get:
|
||
tags: [companion]
|
||
summary: 今日心情
|
||
responses:
|
||
'200':
|
||
description: '{ mood: Mood | null }'
|
||
|
||
/api/v1/membership/me:
|
||
get:
|
||
tags: [commerce]
|
||
summary: 当前成长会员权益
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/EnvelopeMembershipMe'
|
||
|
||
/api/v1/membership/redeem:
|
||
post:
|
||
tags: [commerce]
|
||
summary: 兑换会员码
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [code]
|
||
properties:
|
||
code: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
'400':
|
||
description: Invalid or used code
|
||
'401':
|
||
description: Unauthorized
|
||
|
||
/api/v1/orders:
|
||
post:
|
||
tags: [commerce]
|
||
summary: 创建订单(membership | deep_access | ask_pack)
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/CreateOrderRequest'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/orders/{id}/pay-mock:
|
||
post:
|
||
tags: [commerce]
|
||
summary: 模拟支付成功
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
components:
|
||
parameters:
|
||
Id:
|
||
name: id
|
||
in: path
|
||
required: true
|
||
schema: { type: string, format: uuid }
|
||
|
||
schemas:
|
||
Envelope:
|
||
type: object
|
||
required: [code, message]
|
||
properties:
|
||
code: { type: integer, example: 0 }
|
||
message: { type: string, example: success }
|
||
data: { type: object }
|
||
|
||
AdminMe:
|
||
type: object
|
||
required: [id, username, permissions]
|
||
properties:
|
||
id: { type: string, format: uuid }
|
||
username: { type: string }
|
||
role: { type: string }
|
||
permissions:
|
||
type: array
|
||
items: { type: string }
|
||
|
||
CreateProfileRequest:
|
||
type: object
|
||
required: [relation, birth_date]
|
||
properties:
|
||
relation: { type: string, enum: [self, other] }
|
||
display_name: { type: string }
|
||
birth_date: { type: string, format: date }
|
||
birth_time: { type: string, nullable: true }
|
||
birth_place: { type: string, nullable: true }
|
||
gender: { type: string, nullable: true }
|
||
relation_type: { type: string, nullable: true }
|
||
|
||
CreateOrderRequest:
|
||
type: object
|
||
required: [kind]
|
||
properties:
|
||
kind: { type: string, enum: [membership, deep_access, ask_pack] }
|
||
plan: { type: string, description: month|quarter|year when membership; pack10|pack30|pack100 when ask_pack }
|
||
report_id: { type: string, format: uuid, description: when deep_access }
|
||
|
||
# --- Aligned with packages/types (ECR-004) ---
|
||
Profile:
|
||
type: object
|
||
required: [id, user_id, relation, display_name, birth_date, created_at]
|
||
properties:
|
||
id: { type: string, format: uuid }
|
||
user_id: { type: string, format: uuid }
|
||
relation: { type: string, enum: [self, other] }
|
||
display_name: { type: string }
|
||
birth_date: { type: string, format: date }
|
||
birth_time: { type: string, nullable: true }
|
||
birth_place: { type: string, nullable: true }
|
||
relation_type: { type: string, nullable: true }
|
||
geo_lat: { type: number, nullable: true }
|
||
geo_lng: { type: number, nullable: true }
|
||
geo_visible: { type: boolean }
|
||
created_at: { type: string, format: date-time }
|
||
|
||
GrowthReport:
|
||
type: object
|
||
required: [id, user_id, profile_id, type, summary, has_deep_access, created_at]
|
||
properties:
|
||
id: { type: string, format: uuid }
|
||
user_id: { type: string, format: uuid }
|
||
profile_id: { type: string, format: uuid }
|
||
type: { type: string }
|
||
summary:
|
||
type: object
|
||
additionalProperties: true
|
||
description: >
|
||
For type=star|rhythm include as_of (YYYY-MM-DD CST) and valid_until (next midnight CST RFC3339).
|
||
GET latest/id refreshes day-scoped outlook in place when as_of is stale; report id unchanged.
|
||
detail: { type: object, nullable: true, additionalProperties: true }
|
||
has_deep_access: { type: boolean }
|
||
created_at: { type: string, format: date-time }
|
||
|
||
HomeDailyTips:
|
||
type: object
|
||
required: [clothing_index, clothing, color_note, palette, wellness, source, shichen, shichen_name, valid_until]
|
||
properties:
|
||
clothing_index: { type: integer }
|
||
clothing: { type: string }
|
||
color_note: { type: string }
|
||
palette:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
name: { type: string }
|
||
hex: { type: string }
|
||
wellness: { type: string }
|
||
source: { type: string, description: llm | fallback }
|
||
gua_name: { type: string }
|
||
day_part: { type: string }
|
||
as_of: { type: string, format: date }
|
||
shichen: { type: integer, minimum: 0, maximum: 11 }
|
||
shichen_name: { type: string }
|
||
valid_until: { type: string, format: date-time, description: Next 时辰 start RFC3339 }
|
||
need_birth: { type: boolean }
|
||
|
||
MembershipMe:
|
||
type: object
|
||
required: [active, status]
|
||
properties:
|
||
active: { type: boolean }
|
||
plan: { type: string }
|
||
status: { type: string }
|
||
expires_at: { type: string, format: date-time, nullable: true }
|
||
ask_quota_left: { type: integer }
|
||
|
||
ScaleSummary:
|
||
type: object
|
||
required: [slug, title, description]
|
||
properties:
|
||
slug: { type: string }
|
||
title: { type: string }
|
||
description: { type: string }
|
||
|
||
ScaleQuestionOption:
|
||
type: object
|
||
required: [key, text]
|
||
properties:
|
||
key: { type: string }
|
||
text: { type: string }
|
||
|
||
ScaleQuestion:
|
||
type: object
|
||
required: [id, sort, body]
|
||
properties:
|
||
id: { type: string, format: uuid }
|
||
sort: { type: integer }
|
||
body:
|
||
type: object
|
||
required: [prompt, options]
|
||
properties:
|
||
prompt: { type: string }
|
||
options:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/ScaleQuestionOption' }
|
||
|
||
ScaleDetail:
|
||
type: object
|
||
required: [slug, title, description, questions]
|
||
properties:
|
||
slug: { type: string }
|
||
title: { type: string }
|
||
description: { type: string }
|
||
access: { type: string, description: free | membership }
|
||
locked: { type: boolean, description: true when membership required and inactive }
|
||
questions:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/ScaleQuestion' }
|
||
|
||
ScaleSubmitResult:
|
||
type: object
|
||
required: [id, result]
|
||
properties:
|
||
id: { type: string, format: uuid }
|
||
result: { type: object, additionalProperties: true }
|
||
|
||
AskQuota:
|
||
type: object
|
||
required: [active_membership, remaining, free_limit, source]
|
||
properties:
|
||
active_membership: { type: boolean }
|
||
remaining: { type: integer }
|
||
free_limit: { type: integer }
|
||
source: { type: string }
|
||
|
||
AskThread:
|
||
type: object
|
||
required: [id, user_id, profile_id, created_at]
|
||
properties:
|
||
id: { type: string, format: uuid }
|
||
user_id: { type: string, format: uuid }
|
||
profile_id: { type: string, format: uuid }
|
||
scene: { type: string, nullable: true }
|
||
created_at: { type: string, format: date-time }
|
||
|
||
AskMessage:
|
||
type: object
|
||
required: [id, thread_id, role, content, created_at]
|
||
properties:
|
||
id: { type: string, format: uuid }
|
||
thread_id: { type: string, format: uuid }
|
||
role: { type: string, enum: [user, assistant] }
|
||
content: { type: string }
|
||
created_at: { type: string, format: date-time }
|
||
|
||
ImageCardScene:
|
||
type: object
|
||
required: [key, label]
|
||
properties:
|
||
key: { type: string }
|
||
label: { type: string }
|
||
|
||
ImageCardQuota:
|
||
type: object
|
||
required: [remaining, daily_free, unlimited]
|
||
properties:
|
||
remaining: { type: integer }
|
||
daily_free: { type: integer }
|
||
unlimited: { type: boolean }
|
||
|
||
EnvelopeProfileList:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/Profile' }
|
||
|
||
EnvelopeGrowthReport:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data: { $ref: '#/components/schemas/GrowthReport' }
|
||
|
||
EnvelopeHomeDailyTips:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data: { $ref: '#/components/schemas/HomeDailyTips' }
|
||
|
||
EnvelopeMembershipMe:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data: { $ref: '#/components/schemas/MembershipMe' }
|
||
|
||
EnvelopeScaleList:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/ScaleSummary' }
|
||
|
||
EnvelopeAskQuota:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data: { $ref: '#/components/schemas/AskQuota' }
|
||
|
||
EnvelopeReportList:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/GrowthReport' }
|
||
|
||
EnvelopeScaleDetail:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data: { $ref: '#/components/schemas/ScaleDetail' }
|
||
|
||
EnvelopeImageCardScenes:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/ImageCardScene' }
|
||
|
||
EnvelopeImageCardQuota:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Envelope'
|
||
- type: object
|
||
properties:
|
||
data: { $ref: '#/components/schemas/ImageCardQuota' }
|