Files
hankin 815cbf9d8c v0.2.0: CRM/ERP 系统升级 - 清理 .gitignore 并移除误提交的 venv/env/db 文件
- 更新 .gitignore:全面覆盖环境变量、数据库、日志、缓存、上传文件
- 移除误跟踪的 server/venv/、crm_data.db、.env 文件
- 新增 server/.env.example 模板
- 新增合同管理、利润核算、AI教练等功能模块
- 新增 Playwright e2e 测试套件
- 前后端多项功能升级和 bug 修复
2026-05-11 07:24:19 +00:00

81 lines
4.1 KiB
Python

"""
新增 ORM 模型 — ai_chat_sessions / sales_logs / ai_report_drafts
"""
from __future__ import annotations
import uuid
from datetime import date, datetime
from sqlalchemy import Boolean, Date, DateTime, ForeignKey, SmallInteger, String, Text, func
from sqlalchemy.dialects.postgresql import UUID, JSONB, ARRAY
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base
class AiChatSession(Base):
__tablename__ = "ai_chat_sessions"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("sys_users.id"), nullable=False)
role: Mapped[str] = mapped_column(String(10), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
msg_type: Mapped[str] = mapped_column(String(20), default="text")
metadata_: Mapped[dict | None] = mapped_column("metadata", JSONB, default=dict)
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
class SalesLog(Base):
__tablename__ = "sales_logs"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
salesperson_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("sys_users.id"), nullable=False)
involved_company_ids: Mapped[list] = mapped_column(
ARRAY(UUID(as_uuid=True)), nullable=False, default=list,
comment="该篇日志涉及的公司ID列表"
)
customer_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("crm_customers.id"), nullable=True)
content: Mapped[str] = mapped_column(Text, nullable=False)
log_date: Mapped[date] = mapped_column(Date, default=date.today)
contact_ids: Mapped[list | None] = mapped_column(JSONB, default=list, nullable=True)
ai_processed: Mapped[bool] = mapped_column(Boolean, default=False)
ai_coaching_feedback: Mapped[dict | None] = mapped_column(
JSONB, default=dict, nullable=True,
comment="AI 教练引擎回写的指导反馈"
)
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now())
is_deleted: Mapped[bool] = mapped_column(Boolean, default=False)
class AiReportDraft(Base):
__tablename__ = "ai_report_drafts"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
author_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("sys_users.id"), nullable=False)
report_type: Mapped[str] = mapped_column(String(20), nullable=False)
period_start: Mapped[date] = mapped_column(Date, nullable=False)
period_end: Mapped[date] = mapped_column(Date, nullable=False)
content_md: Mapped[str] = mapped_column(Text, nullable=False)
status: Mapped[str] = mapped_column(String(20), default="draft")
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now())
is_deleted: Mapped[bool] = mapped_column(Boolean, default=False)
class KbObsidianVector(Base):
"""知识库向量表 —— pgvector 存储 Obsidian 文档分块向量"""
__tablename__ = "kb_obsidian_vectors"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
company_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("sys_companies.id"), nullable=False, index=True
)
source_path: Mapped[str] = mapped_column(String(500), nullable=False, comment="源文件路径")
chunk_index: Mapped[int] = mapped_column(SmallInteger, default=0)
content: Mapped[str] = mapped_column(Text, nullable=False)
metadata_: Mapped[dict | None] = mapped_column("metadata", JSONB, default=dict)
# 向量字段使用 raw SQL 创建(vector(1536))因 SQLAlchemy 无原生 pgvector 类型
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
is_deleted: Mapped[bool] = mapped_column(Boolean, default=False)