merge: integrate ZSY customer service and profile capabilities

This commit is contained in:
张胜宇
2026-09-11 22:31:51 +08:00
94 changed files with 7933 additions and 77 deletions
+4
View File
@@ -54,3 +54,7 @@ class FaqSynonym(Base):
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
# 知识生命周期代码沿用该名称;保留别名避免重复声明同一张表。
FinKnowledgeMeta = KnowledgeMeta
+6
View File
@@ -5,6 +5,12 @@ from sqlalchemy import JSON, BigInteger, DateTime, Integer, Numeric, String, Tex
from sqlalchemy.orm import Mapped, mapped_column
from app.model.base import Base
from app.model.profile import ProfileSnapshot
__all__ = [
"MemoryUnit", "MemoryEvidence", "MemorySyncOutbox", "MemoryConflict",
"ProfileSnapshot",
]
class MemoryUnit(Base):
+6 -3
View File
@@ -6,13 +6,13 @@
1. `user_facts.id` 在库里**没有 auto_increment**,插入时必须由应用显式提供主键;
2. `profile_snapshots.current_customer_id` 是**生成列**(`IF(is_current=1, customer_id, NULL)`),
与唯一键 `uk_profile_snapshot_current` 共同保证「每个客户最多一条当前快照」。生成列由数据库
维护,因此这里**故意不映射**——映射了反而会在写入时与之冲突。
维护,因此这里只映射为只读计算列,写入时不会提供该字段。
"""
from datetime import datetime
from typing import Any
from sqlalchemy import CHAR, JSON, BigInteger, Boolean, DateTime, Float, String
from sqlalchemy import CHAR, JSON, BigInteger, Boolean, Computed, DateTime, Float, String
from sqlalchemy.orm import Mapped, mapped_column
from app.model.base import Base
@@ -59,4 +59,7 @@ class ProfileSnapshot(Base):
generated_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
# current_customer_id 为生成列,由数据库维护,故意不映射(见模块 docstring)
# 生成列由数据库维护;映射为只读计算列,便于按当前快照查询,不参与 INSERT/UPDATE。
current_customer_id: Mapped[int | None] = mapped_column(
BigInteger, Computed("IF(is_current = 1, customer_id, NULL)")
)
+6 -25
View File
@@ -1,30 +1,11 @@
"""Additive projection model for opening-risk questionnaire profiles."""
"""Compatibility exports for opening-risk questionnaire profiles.
from datetime import datetime
from typing import Any
The canonical ``profile_snapshots`` mapping lives in ``app.model.profile``. Keeping
two declarative classes for the same table makes SQLAlchemy reject model imports,
so this module re-exports the canonical class for existing callers.
"""
from sqlalchemy import JSON, BigInteger, DateTime, String
from sqlalchemy.orm import Mapped, mapped_column
from app.model.base import Base
from app.model.fund import FundRiskAssessment as RiskAssessment
class ProfileSnapshot(Base):
__tablename__ = "profile_snapshots"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
profile_uuid: Mapped[str | None] = mapped_column(String(36), unique=True)
customer_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
version: Mapped[int] = mapped_column(BigInteger, nullable=False)
snapshot: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
generation_basis: Mapped[dict[str, Any] | None] = mapped_column(JSON)
snapshot_hash: Mapped[str | None] = mapped_column(String(64))
is_current: Mapped[bool] = mapped_column(nullable=False, default=False)
current_customer_id: Mapped[int | None] = mapped_column(BigInteger)
generated_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
from app.model.profile import ProfileSnapshot
__all__ = ["ProfileSnapshot", "RiskAssessment"]