装闭 RenoPit 源码解析(02):AI装修闭坑系统数据库与模型设计

1785881979716

装闭 RenoPit 的项目、装修素材、AI 分析结果和 PDF 报告都需要跨请求保存。本篇结合开源仓库 fthux/RenoPit 中的 SQLAlchemy 模型,分析六张核心数据表如何组织一份完整的装修闭坑项目。

一、六张表围绕 Project 展开

后端模型位于 backend/app/models,全部继承 core/database.py 中的 Base。六张表的关系如下:

erDiagram PROJECT ||--o{ PROJECT_IMAGE : contains PROJECT ||--o{ PROJECT_FILE : contains PROJECT ||--o{ ANALYSIS : produces PROJECT ||--o{ DOCUMENT_ANALYSIS : produces PROJECT ||--o{ REPORT : owns ANALYSIS ||--o| REPORT : generates PROJECT { uuid id PK string name string status text input_text } ANALYSIS { uuid id PK uuid project_id FK json raw_result_json string status } DOCUMENT_ANALYSIS { uuid id PK uuid project_id FK uuid project_file_id FK json risks_json }

Project 是整个数据模型的入口。图片、普通文件、设计分析和报告都通过 project_id 回到项目,因此 API 只要拿到项目 UUID,就能继续查询全部业务数据。

二、Project 保存业务状态

Project 的核心字段如下:

class Project(Base):
    id = Column(Uuid(as_uuid=False), primary_key=True)
    name = Column(String(255), nullable=False)
    access_token = Column(String(64), nullable=False)
    status = Column(String(20), default="pending")
    input_text = Column(Text, nullable=True)

namedescription 描述项目,input_text 保存用户补充的装修需求。status 是前后端共同使用的状态字段,主要在 pendinganalyzingcompletedfailed 之间变化。

模型还为 access_tokenstatuscreated_at 建立索引。列表接口按创建时间倒序查询,状态则会被项目页和 SSE 接口反复读取。

imagesfilesanalysisreport 都设置了 cascade="all, delete-orphan"。删除项目 ORM 对象时,关联的数据行会一起进入删除流程。数据库外键同时使用 ondelete="CASCADE",在数据库层继续维护父子关系。

三、图片和文档为什么分表

ProjectImage 保存原始文件名、磁盘路径、文件大小、宽度和高度。图片会进入多模态 LLM,因此后端需要尺寸信息,并在分析前转换成 Base64。

ProjectFile 面向 TXT、Markdown、DOCX 和 PDF 等文档,除路径和文件类型外,还包含 extracted_text

class ProjectFile(Base):
    original_filename = Column(String(500), nullable=False)
    storage_path = Column(String(500), nullable=False)
    file_type = Column(String(20), nullable=False)
    extracted_text = Column(Text, nullable=True)

上传阶段提取出的文本会直接写入该字段。后续设计分析、合同审查和跨文档核查可以复用它,不必每次重新读取 PDF 或 DOCX。

两个表都在 project_id 上建立索引,并通过 back_populates 回到 Project.imagesProject.files

四、两类 AI 分析使用不同模型

Analysis 保存设计图和综合装修闭坑分析。大模型返回的完整结构放在 raw_result_json,失败原因放在 error_messageproject_id 没有唯一约束,一次重新分析会创建新的 Analysis 记录,结果接口再按创建时间取最近一条。虽然 Project.analysis 在 ORM 中配置为标量关系,业务接口读取分析历史时使用的是显式查询和排序。

DocumentAnalysis 则保存单份合同或报价单的分析。它包含:

  • doc_type:合同、报价单或未知类型;
  • confidence:文档分类置信度;
  • classifications_json:分类依据和关键片段;
  • risks_json:风险项及增项预测;
  • summaryrisks_count:供列表和报告快速读取。

project_file_id 使用 ondelete="SET NULL"。当原始文件记录被删除时,文档分析记录仍可保留,只是失去文件关联;project_id 则继续使用级联删除。

五、Report 连接分析结果和 PDF

Report 同时引用 ProjectAnalysisanalysis_id 上还有唯一约束:

UniqueConstraint("analysis_id", name="uq_reports_analysis_id")

这表示一条分析记录最多对应一条报告记录;project_id 本身没有唯一约束。当前下载接口会优先复用项目已有的第一条 Report 记录。file_path 用来记录生成文件的位置,而实际下载接口也可以现场调用 ReportLab 生成 PDF 字节。

六、模型如何变成 API 数据

数据库模型不会直接返回给浏览器。项目接口使用 Pydantic 的 ProjectCreateRequestProjectUpdateRequestProjectResponse 约束输入输出。例如创建项目时限制名称 255 字、描述 500 字、补充说明 2000 字。

_project_to_response() 还会根据 ORM 关系计算 image_countfile_count。前端 types/index.ts 中的 ProjectProjectFileProjectImageAnalysisResult 等接口再接住这些 JSON 字段,使页面状态与后端响应保持同一套结构。

七、数据模型调用链小结

RenoPit 的数据层可以分为三组:ProjectProjectImageProjectFile 保存用户输入;AnalysisDocumentAnalysis 保存 AI 结构化结果;Report 保存最终输出。所有后续调用链都围绕 project_id 展开。

完整模型和 Schema 可以在 fthux/RenoPit 中继续对照查看。下一篇将沿着这些模型进入项目 API,分析一个装修项目从创建、编辑到复制和删除的完整生命周期。

posted on 2026-08-09 14:14  fthux  阅读(14)  评论(0)    收藏  举报