上线版: 产品表单统一+form嵌套修复+用户管理+部署+三套叙事

- 产品编辑入口统一走 ProductFormFull(卖点/风格/人群/品牌词全字段);
  修复开任务页 <form> 套 <form> 致"编辑产品"报错、改不了、跳回首个产品
- dashboard 入口卡片对齐实际路由: 系统管理(/config) 与 工作配置(/settings) 分开;
  settings ?tab=products 直达改用挂载后读 URL, 消除 hydration mismatch
- 新增用户管理(users API/admin service/改密页) + alembic 022/023/024
- 上线部署: Dockerfile / docker-compose.prod+https / nginx https / .env.example
- A8 三套正交叙事(痛点/场景/成分背书) + beige 调色去AI化 + 飞轮 text_import 高权重信号

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
yangqianqian
2026-06-30 18:08:13 +08:00
parent a77212781c
commit df1856d793
150 changed files with 8616 additions and 1765 deletions

View File

@@ -59,6 +59,14 @@ components:
email: { type: string }
current_workspace_id: { type: integer }
role: { type: string, enum: [admin, supervisor, operator] }
must_change_password: { type: boolean }
ChangePasswordRequest:
type: object
required: [current_password, new_password]
properties:
current_password: { type: string }
new_password: { type: string, minLength: 10 }
MeData:
type: object
@@ -68,6 +76,7 @@ components:
email: { type: string }
current_workspace_id: { type: integer }
role: { type: string }
must_change_password: { type: boolean }
workspace:
type: object
properties:
@@ -135,9 +144,11 @@ components:
# ── 标杆笔记 ──
BenchmarkCreateRequest:
type: object
required: [screenshot_url, highlights]
properties:
screenshot_url: { type: string }
screenshot_url:
type: string
nullable: true
description: 兼容旧字段;新前端不再把外部链接存到这里
highlights: { type: string }
link_url: { type: string, nullable: true }
@@ -146,9 +157,16 @@ components:
properties:
id: { type: integer }
product_id: { type: integer }
screenshot_url: { type: string }
screenshot_url: { type: string, nullable: true }
highlights: { type: string }
link_url: { type: string, nullable: true }
features:
type: object
nullable: true
additionalProperties: true
analyze_status: { type: string, enum: [pending, analyzing, done, failed] }
analysis_source: { type: string, nullable: true }
analysis_warning: { type: string, nullable: true }
created_at: { type: string, format: date-time }
# ── 违禁词 ──
@@ -191,10 +209,15 @@ components:
theme: { type: string }
status:
type: string
enum: [pending, generating, pending_selection, pending_review, approved, rejected, archived]
enum: [pending, generating, pending_selection, pending_review, approved, rejected, archived, failed]
created_at: { type: string, format: date-time }
text_count: { type: integer }
image_count: { type: integer }
track: { type: string, enum: [ai, import] }
need_product_image: { type: boolean }
review_status: { type: string, nullable: true }
reject_reason: { type: string, nullable: true }
reviewed_at: { type: string, format: date-time, nullable: true }
TextCandidateScore:
type: object
@@ -211,10 +234,19 @@ components:
properties:
candidate_id: { type: integer }
angle_label: { type: string }
strategy: { type: string, enum: [A, B, C], nullable: true }
title: { type: string }
content: { type: string }
tags: { type: array, items: { type: string } }
cover_title: { type: string }
image_brief: { type: string }
source: { type: string, enum: [ai, import] }
score: { $ref: '#/components/schemas/TextCandidateScore' }
banned_word_status: { type: string, enum: [pass, auto_fixed, soft_warn, hard_block] }
is_selected: { type: boolean }
verdict: { type: string }
summary: { type: string }
edited: { type: boolean }
ImageCandidateItem:
type: object
@@ -226,6 +258,11 @@ components:
type: string
enum: [hook, pain, proof, quality, credit, convert, main]
description: "storyboard角色,单张图用main"
seq: { type: integer }
is_selected: { type: boolean }
is_regen: { type: boolean }
ai_visual_score: { type: number, nullable: true }
ai_visual_note: { type: string, nullable: true }
SelectCandidateRequest:
type: object
@@ -262,17 +299,27 @@ components:
submitted_at: { type: string, format: date-time }
operator_name: { type: string }
selected_text:
type: object
properties:
candidate_id: { type: integer }
angle_label: { type: string }
content: { type: string }
allOf:
- $ref: '#/components/schemas/TextCandidateItem'
nullable: true
selected_image:
type: object
properties:
candidate_id: { type: integer }
strategy: { type: string, enum: [A, B, C] }
url: { type: string }
allOf:
- $ref: '#/components/schemas/ImageCandidateItem'
nullable: true
selected_sets:
type: array
items:
type: object
properties:
strategy: { type: string, enum: [A, B, C] }
selected_text:
allOf:
- $ref: '#/components/schemas/TextCandidateItem'
nullable: true
selected_images:
type: array
items:
$ref: '#/components/schemas/ImageCandidateItem'
PackageRequest:
type: object
@@ -309,6 +356,17 @@ paths:
- properties:
data: { $ref: '#/components/schemas/LoginData' }
/auth/change-password:
post:
summary: 修改当前登录用户密码(首登强制改密)
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/ChangePasswordRequest' }
responses:
"200": { description: OK }
/auth/me:
get:
summary: 当前用户信息
@@ -430,7 +488,7 @@ paths:
responses:
"200": { description: OK }
post:
summary: 新增标杆笔记(截图+手填亮点为主通道
summary: 新增标杆笔记(JSON兼容接口外部链接存 link_url不上传截图)
parameters:
- in: path
name: id
@@ -444,6 +502,54 @@ paths:
responses:
"200": { description: OK }
/products/{id}/benchmarks/upload:
post:
summary: 上传标杆笔记(原始链接 + 截图文件 + 手填亮点)
parameters:
- in: path
name: id
required: true
schema: { type: integer }
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
screenshot:
type: string
format: binary
description: JPEG/PNG/WebP最大 10MB用于视觉分析
highlights:
type: string
description: 手填亮点,可选
link_url:
type: string
description: 小红书或外部原始链接,可选,必须 http/https
responses:
"200": { description: OK }
/benchmarks/{id}/analyze:
post:
summary: 分析标杆笔记(截图视觉分析 + 原始链接/亮点上下文)
parameters:
- in: path
name: id
required: true
schema: { type: integer }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
code: { type: integer }
message: { type: string }
data: { $ref: '#/components/schemas/BenchmarkItem' }
# ── 违禁词 ──
/banned-words:
get:
@@ -665,7 +771,20 @@ paths:
get:
summary: 待审队列(组长视角)
responses:
"200": { description: OK }
"200":
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/SuccessEnvelope'
- properties:
data:
type: object
properties:
items:
type: array
items: { $ref: '#/components/schemas/ReviewQueueItem' }
/review/{task_id}/approve:
post:
@@ -712,7 +831,7 @@ paths:
/delivery-packages/{id}/download:
get:
summary: 下载交付包status:pending/ready/downloaded
summary: 查询交付包状态GET只读无副作用
parameters:
- in: path
name: id
@@ -729,6 +848,35 @@ paths:
- properties:
data: { $ref: '#/components/schemas/DeliveryPackageData' }
/delivery-packages/{id}/mark-downloaded:
post:
summary: 显式标记交付包已下载
parameters:
- in: path
name: id
required: true
schema: { type: integer }
responses:
"200":
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/SuccessEnvelope'
- properties:
data: { $ref: '#/components/schemas/DeliveryPackageData' }
/delivery-packages/{id}/download-file:
get:
summary: 下载交付包 zip 文件GET只读
parameters:
- in: path
name: id
required: true
schema: { type: integer }
responses:
"200": { description: ZIP 文件 }
# SSE事件约定见 启动包/API契约.md §2
# 二期端点(注册/矩阵号/管理后台)不在此文件中定义