Technical Architecture · Source Analysis

PackHorizon AI

面向"包装升级决策"的 SaaS。用户递交项目 → AI 顾问引导确认 → 10 阶段流水线生成 19 章报告 + 6 个方向配图 → 会员/积分/付费解锁交付。 本文件基于对 gitee packhorizon/pack-horizon@master 的全量代码通读,还原当前生产架构、数据模型、业务流、部署形态与在建改造。

gitee packhorizon/pack-horizon HEAD aea1413b · 增加退款功能 分析日 2026-07-06 源材料 479 行 · 33 KB · 9 维度
Backend Services
7个 · docker-compose
FastAPI · 2 队列 worker · Postgres · Redis · Flower · Nginx
Pipeline Stages
10阶段 → 19 章
source-intake → …19 章报告 + 6 方向配图
AI Providers
5适配
OpenAI · DeepSeek · 小米 MiMo · Compatible · Custom
Alembic Revisions
33迁移
SQLAlchemy 2.0 · psycopg V3 · schema 灰度独立于 prompt SQL
01 · Overall Layering

两服务单体 + Celery 拆载耗时 AI 工作

前端 SPA 与后端 FastAPI 是唯二"服务",PostgreSQL 是唯一事实源,Redis 同时充当 Celery broker、分布式锁与反暴力计数。 报告和图片两条独立队列各自跑 Celery worker(--concurrency 3 · --concurrency 9), 两个 15 秒 poller(scheduler)内嵌在 API 进程里,从 DB 抓 pending 抢 Redis lock 后向 Celery 投递 —— 不是 Celery beat。

flowchart LR
    subgraph Client["浏览器"]
        UI["React SPA
Workspace / Admin / Payment"] end subgraph Edge["frontend 容器 · Nginx"] NG["nginx.conf
SPA fallback + /api 反代"] end subgraph API["backend · FastAPI 进程"] FA["create_app()
main.py:26"] SCH1["ReportScheduler
report_scheduler.py:313"] SCH2["ImageScheduler
image_scheduler.py:188"] SVC["services/*
ai_workflow · intake_consultant
commerce · credit"] end subgraph Queue["Redis 7"] RED[("Broker · Lock · AuthAbuse")] end subgraph Workers["Celery workers · 独立容器"] RW["report-worker
run_report_workflow"] IW["image-worker
run_image_generation_job"] end subgraph DB["PostgreSQL 16"] PG[("workflow_runs · stage_artifacts
packaging_reports · image_generation_jobs
prompt_versions · users · orders · credit_grants")] end subgraph AI["外部 AI"] P1["OpenAI /v1"] P2["DeepSeek /v1"] P3["小米 MiMo /v1"] P4["OpenAI Compatible"] end subgraph Pay["外部支付"] WX["微信支付 V3"] end UI -- fetch --> NG NG -- /api --> FA FA <---> PG FA -- publish --> RED SCH1 -- claim/publish --> RED SCH2 -- claim/publish --> RED RED --> RW RED --> IW RW -- HTTPS --> P1 & P2 & P3 & P4 IW -- HTTPS --> P1 & P3 RW <---> PG IW <---> PG SVC <---> PG FA -. WeChat Pay V3 .-> WX
Frontend

React 18 + Vite 8 SPA

frontend/src/main.tsx → App.tsx

API

FastAPI · 17 router

backend/app/main.py:26-95 create_app()

Workers

Celery 5.4 · 2 队列

report_generation · image_generation

Storage

Postgres 16 · 本地文件系统

3 bind mount 卷 · 无 OSS/MinIO

02 · Backend Stack

FastAPI · SQLAlchemy 2.0 · Celery · Redis

Python 后端选型偏"标准 boring stack"。ORM 走 SQLAlchemy 2.0 声明式,DB 驱动锁到 psycopg V3(_normalize_database_url 强制 postgresql+psycopg:// 方言), 迁移工具 Alembic 现有 33 个 revision。异步任务是 Celery 5.4 + Redis broker,result_backend 关掉了 —— 业务态由 DB 承担。 Scheduler 是自研 15 秒 poller,不是 Celery beat。认证走签名 Cookie session,反暴力靠 Redis 计数 + Cloudflare Turnstile。

维度选型关键位置
Web 框架FastAPI 0.115+backend/app/main.py:26-95 · create_app() 挂 17 router
ORMSQLAlchemy 2.0 · Mapped/DeclarativeBasebackend/app/db/session.py:1-22 · engine/SessionLocal, 池参从 settings.database_*
DB 驱动psycopg V3backend/app/core/config.py:11-43 · _normalize_database_url 强制方言
迁移工具Alembic 1.13 · 33 revisionbackend/alembic/versions/6749f7a6d790_initial_core_schema.py
异步任务Celery 5.4 + Redis broker · 2 queuebackend/app/tasks/celery_app.py:8-29 · task_routes 分流 · result_backend 关闭
Scheduler自研 poller · 15 秒report_scheduler.py:313 · image_scheduler.py:188 · 内嵌 API 进程
缓存 / 锁Redis 7 · packhorizon:*report_queue.py:60-138 · ReportExecutionLock.claim/heartbeat/release
认证Cookie session (签名 token) + 微信auth_service.py:30-53 · create_signed_token · scrypt 校验
反暴力 / 验证码Redis 计数 + Cloudflare Turnstileconfig.py:82-86 · 用户 5/10 次触发 · 管理员 3/8 次
密码算法scryptmodels/access.py:73 · User.password_algorithm 默认值
03 · Frontend Stack

React 18 + Vite 8 · shadcn/Radix + Tailwind 4

纯 SPA,无 SSR/SSG。无第三方 state store,由 30+ 个 useWorkspace*.ts custom hook 按域拆分状态。UI 是 shadcn 风格 —— Radix primitives + Tailwind 4 + CVA。 业务 API 客户端按域拆到 frontend/src/lib/aiClient*.ts(Auth · Workspace · AdminModels · Analytics)。 部署时 Vite 打 dist/,拷进 nginx:alpine 容器 —— 对 /api/ 反代到 backend:8000,其余路径 fallback 到 index.html

维度选型
框架 / 语言React 18.3.1 + TS 5.8 (strict)
构建Vite 8.0.16 · @vitejs/plugin-react · 别名 @ → ./src
路由react-router-dom 6.30.4 · 二级用 URL search param
状态管理纯 React Hooks + Context · 无第三方 store
UI / 样式Radix UI + TailwindCSS 4 + CVA · shadcn 风格
图标 / 字体lucide-react + @fontsource-variable/geist
HTTP 客户端原生 fetch · apiClient.ts · credentials: "include"
部署nginx:alpine + dist/ · /api 反代 · 其余 fallback index.html
渲染模式纯 SPA · 无 SSR/SSG

路由总览 frontend/src/App.tsx

/login /register AuthPage.tsx
/wechat/invite WechatInvitePage.tsx
/app WorkspacePage · tab={chat, records, membership, feedback, profile}
/payment/:orderNo PaymentPage.tsx
/admin/* AdminPage · 18 sub-section · prompts · models · orders · credits · trace · evidence
/terms /privacy LegalPage.tsx
fallback HomePage.tsx
04 · Data Model

5 个域 · 24 张核心表

核心模型按业务域拆到 backend/app/models/{access, workflow, intake, commerce, system}.py。 设计上把 项目 → workflow_run → stage_artifacts → packaging_reports 建成不可变链,历史 run 永远绑定当时的 prompt_version_id —— 版本切换不会追溯影响已排队的报告。 商业化侧订单双状态机(payment_status · entitlement_status),积分走 credit_grants → credit_ledger_entries 账本模式。

访问与用户

models/access.py
roles系统角色,code 唯一
registration_invites邀请码 · users.registration_invite_id 一对多
wechat_pending_registrations微信扫码到绑定完成之间的暂存
users主账号 · 内嵌 login_failed_count/locked_until · openid/unionid/jsapi_openid 独立列

项目 & 工作流

models/workflow.py
projects项目主表 · has_legacy_packaging · current_workflow_run_id 反向指针
project_assets项目文件资产(旧包装 · logo · 参考)
ai_provider_catalog供应商目录 · capability_profile_json 存能力画像
ai_models模型配置 · provider_id → catalog(保留 provider_name 兼容)
stage_definitions阶段元数据 · system/AI 阶段区分
prompt_versions整套版本 · is_current 单例 · based_on_version_id 自引用版本链
prompt_definitions版本内每阶段三段 prompt · prompt_hash 用于比对
workflow_runs报告生成运行 · 绑定 prompt_version_id · pending → queued → running → completed/failed
workflow_stage_runs每阶段快照 · gate_status 存质量门
image_generation_jobs6 方向配图 · direction_id · charge_mode · celery_task_id · worker_heartbeat_at
stage_artifacts阶段中间产物 · is_selected / is_final
packaging_reports最终报告快照 · schema_version · content_json
model_call_logs模型调用审计 · latency · tokens · 错误

项目 Intake

models/intake.py
intake_sessionsAI 顾问会话 · kind ∈ {new-packaging, upgrade} · confirmation_json
intake_messages顾问对话消息 · role ∈ {user, assistant}
intake_assets会话内上传素材 · asset_role: old-packaging / logo / reference / competitor

商业化

models/commerce.py
plans订阅计划 · 月/年价 · 赠积分 · 日限报
credit_packs积分包
usage_rules消耗规则 · 报告 200 · 付费图 20
user_subscriptions订阅明细 · plan_code · starts_at / expires_at
credit_grants积分授权 · source_type ∈ {subscription, credit_pack}
orders订单 · payment_status · entitlement_status 双状态机
order_items / order_payments订单明细与支付流水
entitlement_deliveries权益投递记录
credit_ledger_entries积分账本 · delta · balance_after
refunds退款记录 · 最新提交 aea1413b

系统

models/system.py
system_configs后台可修改的运行参数键值对
audit_events审计事件流
analytics_events增长/漏斗埋点
feedback_entries内测反馈 · survey_json · feedback_attachments/

主要外键关系(文字 ER)

users ─┬─(1..n)→ projects ─(1..n)→ project_assets
       ├─(1..n)→ intake_sessions ─(1..n)→ intake_messages / intake_assets
       ├─(1..n)→ orders ─(1..n)→ order_items / order_payments / entitlement_deliveries
       ├─(1..n)→ user_subscriptions ─→ plans
       ├─(1..n)→ credit_grants ─(1..n)→ credit_ledger_entries
       └─(1..1)→ file_assets (avatar_file_id)

projects ─┬─(1..n)→ workflow_runs ─┬─(1..n)→ workflow_stage_runs ─(1..n)→ stage_artifacts
          │                        ├─(1..n)→ image_generation_jobs ─(0..1)→ generated_files
          │                        └─(0..1)→ packaging_reports  (result_payload 快照)
          └─(1..n)→ project_assets

prompt_versions ─(1..n)→ prompt_definitions ─(n..1)→ stage_definitions  (by stage_code)
workflow_runs.prompt_version_id ─→ prompt_versions.id
ai_models.provider_id ─→ ai_provider_catalog.id
05 · Core Business Flow

Intake → 10 阶段流水线 → 19 章报告 · 6 方向图

端到端主流程分四段:项目 intake(AI 顾问对话)→ 报告 workflow(10 阶段 · Celery 拉起)→ 6 方向配图(独立队列)→ 交付/解锁(订单 · 积分 · 微信支付)。 两个 scheduler 都是"每 15 秒抢 Redis owner lock,把 DB pending 转 queued 后 publish 到 Celery",worker 消费时再抢一次 ReportExecutionLock 防重跑。

10 阶段固定顺序 seed_defaults.py:820-1020 · build_packaging_stage_definitions

01 · SYS
source-
intake
02 · AI
packaging-
intelligence
03 · AI
brand-
strategy
04 · AI
copy-
compliance
05 · AI
creative-
exploration
06 · AI
director-
selection
07 · AI
designer-
directions
08 · AI
image-
brief
09 · OPT
difference-
review
10 · AI
report-
presentation

SYS 系统阶段 · AI AI 阶段(prompt_definitions 版本化) · OPT 仅升级项目跑 · "19 章"是 report-presentation 输出 JSON 的顶层数组结构,由 prompt 约束,版本记录在 packaging_reports.schema_version

5.1 · Intake / AI 顾问

项目 intake 到 project 转换

前端页面 WorkspacePage.tsx (tab=chat),后端路由 backend/app/api/intake.py:

  • POST /api/intake/sessionscreate_intake_session (:202)
  • POST /api/intake/sessions/{id}/messagesappend_intake_message (:219)
  • POST /api/intake/sessions/{id}/confirmconfirm_intake_session (:248)
  • POST /api/intake/sessions/{id}/assetsupload_intake_asset (:271)

顾问 turn 当作一次 AI 阶段调用,prompt 用 stage_code = intake-dialogue,走 ModelRuntimeService.invoke_stage_json 输出结构化 JSON:assistantReply / summary / confirmation / missingFields / shouldShowConfirmationconfirm_intake_session 落库时把 confirmation_json 拷进 projects 完成转换。

5.2 · Report 19 章生成

enqueue → schedule → worker → 10 阶段

前端触发 requestUserReportEnqueuePOST /api/ai/report-runs/enqueue:

  1. AiWorkflowService.enqueue_report_run (ai_workflow_service.py:195) 建 workflow_runs · 状态 pending · 绑定当前 prompt_version_id
  2. ReportScheduler(:313)每 15 秒 publish_pending_report_runs_once(:168)抢 Redis lock, pending→queued 后 publish 到 report_generation 队列
  3. report-worker 消费 run_report_workflow(report_tasks.py:31),在 claimed_report_slot() 上下文里抢 Redis 分布式锁,调 process_report_run(ai_workflow_service.py:1048)
  4. 遍历 10 阶段调 _execute_stage(:1308),取 prompt · 拿 provider profile · 走 ModelRuntimeService.invoke_stage_json(:36)· 结果写 workflow_stage_runs + stage_artifacts
  5. 末阶段落入 workflow_runs.result_payload["report"] + 生成 packaging_reports 行 · 状态 completed
5.3 · 6 方向配图

与报告同款队列模式

designer-directions 阶段完成后,_execute_stage 为每个方向建 image_generation_jobs(ai_workflow_service.py:660),charge_mode 依方向序号决定:前 N 属报告包含(included-auto),其余走付费解锁(paid-auto)。

ImageScheduler(image_scheduler.py:188)与报告队列同款 —— 每 15 秒 publish_pending_image_jobs_once(:108),image-worker 消费 run_image_generation_job(image_tasks.py:9),内部同样调 ModelRuntimeService.generate_image(:151),图片落 generated_files + 本地存储卷,回写 image_url / revised_prompt

5.4 · 交付 & 解锁

订单 · 权益 · 积分账本

报告付费:会员免费 · 超额或非会员下单 · commerce_service.py 建 orders,微信支付回调 wechat_payment_notify(commerce.py:112)→ entitlement_deliveries 记录 → 更新 orders.entitlement_status = fulfilled

图片解锁:报告页点解锁 → POST /api/commerce/checkoutcreate_checkout_order(commerce.py:37)→ 支付成功后 CreditService.consume_credits 扣 20 积分, image_generation_jobs.charge_modepaid-auto → paid-unlocked

积分账本:每次消耗/授权在 credit_ledger_entries 落一行,balance_after 便于对账。

06 · AI Provider Adapter

5 供应商 · 能力画像驱动 · Prompt 版本人工灰度

ProviderCapabilityServicecapability profile(结构化输出模式 / developer role 支持 / 视觉能力)统一封装差异 —— 只有 OpenAI 官方支持 developer role,DeepSeek/MiMo 必须把 developer_prompt 合并进 system,否则报 unknown variant developer。 Prompt 侧 schema 由 Alembic 管理,内容用 DbSql 手工 SQL 补丁走灰度 —— schema 与内容变更解耦。

OpenAI
https://api.openai.com/v1
chatchat_completions
结构化json_schema · 强 schema
工具调用native
视觉✓ 多模态
DeepSeek
https://api.deepseek.com/v1
chatchat_completions
结构化prompt_and_repair
工具调用
视觉
小米 MiMo
token-plan-cn.xiaomimimo.com/v1
chatchat_completions
结构化prompt_and_repair
工具调用
视觉✓ 多模态
OpenAI Compatible + Custom
自定义 base URL · 兜底 profile
chat按 OpenAI 兼容
结构化fallback profile
路由_infer_provider_key 关键字回退
用途接第三方兼容层 · 兜底
6.2 · 适配机制

ProviderCapabilityService

resolve_provider_profile(model) · provider_capability_service.py:79 · 先按 model.provider_id 查表命中直接返回,未命中调 _infer_provider_key(:118) 按名字/URL 关键字(api.openai.com · deepseek · mimo · lconai.com)回退到内置 profile。

运行时消费点在 ModelRuntimeService._merge_system_and_developer_prompt(model_runtime_service.py:505)—— 根据 profile 决定 developer_prompt 合并方式,直接对应 docs/AI供应商适配改造任务清单.md §1 描述的原始 bug。

_structured_output_mode(:514)区分 json_schema(OpenAI)与 prompt_and_repair(先请求 → 失败调 repair_stage_json(:80)修复,给 DeepSeek/MiMo 兜底)。

6.3 · Prompt 版本管理

Alembic 管 schema · SQL 补丁管内容

schema 由 3 个迁移落成:a1b2c3d4e5f6_add_prompt_versions · a7b8c9d0e1f2_simplify_prompt_versions_current_only · b8c9d0e1f2a3_globalize_report_stage_and_prompt_versions

版本切换 API 在 prompt_version_service.py:35-341 · ensure_initial_versions() 启动兜底,resolve_version(workflow_run) 保证已排队报告不被切换影响。

灰度靠 DbSql/ 手工 SQL:

  • export_stage_..._from_local_db_*.sql — 从本地 DB 导出快照
  • create_prompt_version_v36_*.sql — 手动 INSERT 新版本
  • set_prompt_version_v36_current_*.sql — 切换 is_current
  • prompt_backups/stage_prompts_backup_*_before-*.sql — 大动作前整套备份

⚠ 同一时刻只有一条 is_current=true,由服务代码保证 —— 不是 DB unique index(见迁移 a7b8c9d0e1f2 的简化)。

07 · Deployment

docker-compose · 7 服务 · workers profile 隔离

docker-compose.yml 定义 7 服务,3 个 worker/flower 挂 profiles: [workers] —— 需 --profile workers 才启。全部 restart: unless-stopped,端口全绑 127.0.0.1,生产暴露靠外层反向代理(ECS + Nginx)。

服务 Image / 命令 宿主端口 depends_on Volume · 备注
postgres postgres:16-alpine 127.0.0.1:55432 → 5432 packhorizon-postgres
redis redis:7-alpine · append-only 127.0.0.1:56379 → 6379 packhorizon-redis
backend 本地 build · ./backend/Dockerfile expose 8000(不映射) postgres:healthy · redis:healthy 3 卷:storage · avatars · feedback-attachments
report-worker profile 同 backend · celery -Q report_generation --concurrency ${CELERY_REPORT_WORKER_CONCURRENCY:-3} postgres:healthy · redis:healthy 与 backend 同 3 卷
image-worker profile 同 backend · celery -Q image_generation --concurrency ${CELERY_IMAGE_WORKER_CONCURRENCY:-9} postgres:healthy · redis:healthy 同上
flower profile celery -A app.tasks.celery_app flower --port=5555 127.0.0.1:5555 → 5555 redis · report-worker · image-worker BASIC_AUTH from FLOWER_BASIC_AUTH
frontend 本地 build · nginx:alpine + dist/ · ARG VITE_API_BASE_URL=/ 127.0.0.1:8088 → 80 backend:healthy
flowchart TB
    Internet(("外部流量")) --> Reverse["外层 Nginx / CDN"]
    subgraph Host["ECS 主机"]
      subgraph Compose["docker-compose"]
        direction TB
        FE["frontend
nginx:alpine + dist/
127.0.0.1:8088→80"] BE["backend
FastAPI + uvicorn
expose 8000"] RW["report-worker
celery -Q report_generation
--concurrency 3"] IW["image-worker
celery -Q image_generation
--concurrency 9"] FL["flower
127.0.0.1:5555→5555"] PG[("postgres:16-alpine
127.0.0.1:55432→5432
vol packhorizon-postgres")] RD[("redis:7-alpine
127.0.0.1:56379→6379
vol packhorizon-redis")] subgraph Vols["共享 bind mounts"] V1["/opt/packhorizon-storage → /app/storage"] V2["/opt/packhorizon-avatars → /app/avatar-storage"] V3["/opt/packhorizon-feedback-attachments"] end end end Reverse --> FE FE -- /api /health /version --> BE FE -- /auth/wechat/callback --> BE BE --> PG BE --> RD BE -. writes .-> V1 BE -. writes .-> V2 BE -. writes .-> V3 RW --> PG RW --> RD RW -. writes .-> V1 IW --> PG IW --> RD IW -. writes .-> V1 FL --> RD BE -- HTTPS --> OpenAI["OpenAI"] RW -- HTTPS --> OpenAI RW -- HTTPS --> DeepSeek["DeepSeek"] RW -- HTTPS --> MiMo["小米 MiMo"] IW -- HTTPS --> OpenAI IW -- HTTPS --> MiMo BE -. WeChat Pay V3 .-> WX["mp.weixin.qq.com"]
Healthcheck

postgres · pg_isready · 20s

redis · redis-cli ping · 20s

backend · HTTP /health · 30s

Worker 健康

report-worker · celery inspect ping · 60s

image-worker · celery inspect ping · 60s

broker 探活,非业务探活

监控入口

flower · TCP 5555 探活

frontend · wget / · 30s

部署形态

ECS + 外层 Nginx 反代 · 单机 docker-compose

文档:docs/ops/FRONTEND_BACKEND_DEPLOYMENT.md

08 · Middleware & Third-Party

Redis 一实例三用 · 本地文件系统 · 无 SMTP/短信

Redis 一个实例同时做 Celery broker/backend + 分布式锁 + 反暴力计数,visibility_timeout=21600(6h)与长报告 job 匹配。 对象存储走本地文件系统,3 个 bind mount 卷分别接报告图、头像、反馈附件 —— 无 OSS/MinIO。 邮件、短信均未接。验证码用 Cloudflare Turnstile,微信登录 + 支付 V3 走官方 OAuth。

用途选型关键位置 / 注解
消息队列Redis(同一实例做 broker + 锁)celery_app.py:14-23 · visibility_timeout=21600
分布式锁Redis SET NX + TTLreport_queue.py:79-138 · ReportExecutionLock · report_lock_ttl_seconds=21600
缓存Redis(主要给 auth-abuse 计数)auth_abuse_service.py · 前缀 packhorizon:auth-abuse:*
对象存储本地文件系统(无 OSS/MinIO)3 bind mount:/app/storage · /app/avatar-storage · /app/feedback-attachment-storage · 元数据落 generated_files / file_assets
邮件— 无仓库无 SMTP 相关配置
短信— 无config/env 未发现供应商 key
验证码Cloudflare Turnstilecaptcha_provider.py · env CAPTCHA_PROVIDER=turnstile + TURNSTILE_SITE_KEY/SECRET_KEY
微信登录官方 OAuth(扫码 + JSAPI)wechat_auth_service.py · 双 openid(PC 扫码 openid + JSAPI openid)
微信支付官方 V3(JSAPI + Native)wechat_pay_service.py · 回调 /api/payments/wechat/notify(:111)· /api/payments/wechat/refund-notify(:140)
联网搜索可选 · DuckDuckGo / Tavily / Bingenv PACKHORIZON_SEARCH_PROVIDER · 默认关(RESEARCH_ENABLED=0packaging_research_service.py
任务监控Celery Flower127.0.0.1:5555 · FLOWER_BASIC_AUTH
分析埋点自建 analytics_eventsaiClientAnalytics.ts 上报 · analytics_service.py 存表
IP 地理本地 CSV 库(可自动刷新)env PACKHORIZON_IP_GEO_PROVIDER=local · IP_GEO_DB_PATH
09 · Technical Debt & In-flight

11 个在建/待清 · 从 docs 反推现状

docs/ 中"改造方案 / 待办清单 / 重构计划"类文档 + git log + Alembic 迁移记录反推正在填的坑。 标签:P0 阻塞内测 · 进行中 · 基本完成 · 运维/流程

9.1 · 报告/图片队列改造 进行中

image_scheduler + image_generation_jobs 刚落成(3 个迁移 merge head),但 REPORT_LEGACY_WORKER_ENABLED 开关仍留 —— 旧 report_worker.py 未删,双轨过渡。

方案要求"API 可多副本 + scheduler 多副本抢锁",锁机制已上,但未见 K8s / systemd 多副本部署证据。

docs/报告生成Celery队列改造方案.md
docs/报告生图独立队列改造任务清单.md
9.2 · 报告 stub / 假数据下线 P0

当前 10 阶段"大部分实现仍是本地模板/本地占位逻辑",report_snapshot 直写通道未移除 —— 导致"点进入研究后瞬间出报告"。

状态:待彻底移除 stub,阻塞内测转正。

docs/报告管理与真实工作流改造待办清单.md §2.2
9.3 · AI 供应商适配 基本完成

已完成:ai_provider_catalog(迁移 ab12cd34ef56ProviderCapabilityService · developer_prompt 分离合并。

遗留:后台"新增模型"仍支持自由输入 provider_name 兜底;profile_overrides_json 明确列为"第一版不做"。

docs/AI供应商适配改造任务清单.md
docs/AI供应商适配开发执行清单.md
9.4 · Prompt 版本管理 在用 · 流程手工

schema 已就位(3 迁移),但版本变更靠人肉 SQL(DbSql/create_prompt_version_v36_*.sql + set_..._current_*.sql)。后台 /admin/prompts 只做只读比对,无写入路径。

潜在债:生产版本切换未走审计,依赖运维手工执行 SQL,备份靠 DbSql/prompt_backups/ 目录约定。

docs/报告提示词版本管理实施方案.md
docs/报告流程阶段与提示词版本全局化实施方案.md
9.5 · 会员订阅 & 积分规则 刚完成

迁移 f7a8b9c0d1e2_membership_subscriptions_and_credit_grants · 4 档订阅 + 2 种积分包 + 报告 200 积分 / 付费图 20 积分。

失败不退款,积分按订阅到期一起过期(credit_grants.expires_atuser_subscriptions.expires_at 对齐)。

docs/会员订阅与积分规则改造实施方案.md
docs/图片解锁积分规则统一改造方案.md
9.6 · 退款 最新提交

git log:aea1413b 增加退款功能 · 18806e3e 增加退款功能

表:refunds(commerce.py:183)。状态已上,未见对应"退款方案 md",属于运维闭环补齐。

git log · aea1413b · 18806e3e
9.7 · 登录反暴力 & 验证码 最近落地

auth_abuse_service.py + Turnstile 集成完毕 · 阈值可通过 env 调优。

遗留:AUTH_ABUSE_FAIL_OPEN=1 默认放行 Redis 故障,生产需按运维策略决定。

docs/登录注册防暴力破解与验证码改造方案.md
9.8 · 前端本地存储清理 进行中

意图:消除 localStorage 里的业务缓存,强制"后端为唯一数据源"。

现状:30+ useWorkspace*.ts hook 都是"每次进入 tab 重新拉",与规范一致;仍存在少量前端过滤/排序逻辑未逐一核查。

docs/前端本地存储清理计划.md
docs/前端开发规范.md
9.9 · 后台管理页重构 进行中

AdminPage.tsx(62 KB)已经拆到 18 个 section,但单文件仍偏大 —— 关键子模块 admin/* 组件下沉到 frontend/src/components/admin/

docs/后台管理页面重构执行清单.md
docs/后台管理-生成记录优化方案.md
9.10 · 内测评测阻塞点 P0 / P1

P0: /api/projects 旧路由未鉴权(需核实是否已修)。

P1: "DB queued 但 Celery 无任务"的假排队(Redis lock 释放时机与 scheduler 抢锁时序问题,与 9.1 强相关)。

其它:多语言混语、图片计数不一致、上游模型额度不足。

docs/PackHorizon AI 内测前上线评测报告 - 2026-07-01.md
9.11 · 历史包袱 流程

重构计划.md(Windows/CRLF 环境编辑过)保留意义在于说明"两服务架构"分家计划 —— 已基本落地。

旧后端参考清单.md 承认"旧后端未删除,作为迁移参考保留" —— backend/ 里已是新代码,旧路径以本文档索引。需要一次性 sweep。

docs/重构计划.md
docs/旧后端参考清单.md