Files
digital-psychology/proto/openapi.yaml
T
jackyu66gitandCursor d33c8fdfe9 feat(ECR-012): 星座对齐收口,并 Closed ECR-007/008
对齐 outlook/分享 type=star/报告页运势面板与测试;流程上关闭 Ops-B/C 两张 ECR。真支付仍后置。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 15:48:19 +08:00

1125 lines
29 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
openapi: 3.0.3
info:
title: YuXinGu API
version: 0.2.0
description: |
愈心谷 P1 API 契约(冻结草案)。
用户可见文案遵循 .ai/product/lexicon.md。
统一信封:{ code, message, data }code=0 成功。
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/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
responses:
'200':
description: OK
/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
/api/v1/admin/orders:
get:
tags: [admin]
summary: List orders
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/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' }
/api/v1/profiles/{id}:
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: 返回 GrowthReportdetail 按权益裁剪)
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=starsummary 含 chart/planets/aspects_preview/outlook(日周月年一生)/transits;无 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
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/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: 探索测试题目
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:
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: 探索结果
/api/v1/ask/quota:
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/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 }
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 }
detail: { type: object, nullable: true, additionalProperties: true }
has_deep_access: { type: boolean }
created_at: { type: string, format: date-time }
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 }
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' }
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' }