| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- """主案、子方案和共享附件统一文档模型。"""
- from __future__ import annotations
- from datetime import datetime
- from sqlalchemy import (
- BigInteger,
- Boolean,
- CheckConstraint,
- ForeignKey,
- Index,
- Integer,
- JSON,
- String,
- Text,
- text,
- )
- from sqlalchemy.orm import Mapped, mapped_column
- from sqlalchemy.dialects import mysql
- from dms.database.base import BusinessTableMixin
- from dms.extensions import db
- class Document(BusinessTableMixin, db.Model):
- __tablename__ = "doc_document"
- __table_args__ = (
- CheckConstraint(
- "document_type IN ('MAIN', 'SUB_PLAN', 'ATTACHMENT')",
- name="document_type",
- ),
- CheckConstraint(
- "document_status IN ('DRAFT', 'PUBLISHED', 'ARCHIVED')",
- name="document_status",
- ),
- CheckConstraint(
- "security_level IN "
- "('PUBLIC', 'INTERNAL', 'SECRET', 'CONFIDENTIAL', 'TOP_SECRET')",
- name="document_security_level",
- ),
- CheckConstraint(
- "visibility_type IN ('ALL_AUTHENTICATED', 'ORGANIZATION', 'CUSTOM')",
- name="document_visibility_type",
- ),
- CheckConstraint(
- "attachment_type IS NULL OR attachment_type IN "
- "('SUPPORT_ACHIEVEMENT', 'TRAINING_MATERIAL', 'TYPICAL_CASE')",
- name="document_attachment_type",
- ),
- CheckConstraint(
- "content_extract_status IN "
- "('SUCCESS', 'EMPTY', 'UNSUPPORTED', 'FAILED')",
- name="document_content_extract_status",
- ),
- # 方案密码锁:仅主案可设密(password_hash 非空时其余类型被拒绝)
- CheckConstraint(
- "(password_hash IS NULL) OR (document_type = 'MAIN')",
- name="document_password_main_only",
- ),
- CheckConstraint(
- "("
- "document_type = 'MAIN' "
- "AND parent_document_id IS NULL "
- "AND category_id IS NOT NULL "
- "AND attachment_type IS NULL"
- ") OR ("
- "document_type = 'SUB_PLAN' "
- "AND parent_document_id IS NOT NULL "
- "AND root_document_id IS NOT NULL "
- "AND category_id IS NOT NULL "
- "AND attachment_type IS NULL"
- ") OR ("
- "document_type = 'ATTACHMENT' "
- "AND parent_document_id IS NULL "
- "AND root_document_id IS NULL "
- "AND category_id IS NULL "
- "AND attachment_type IS NOT NULL "
- "AND security_level = 'PUBLIC' "
- "AND visibility_type = 'ALL_AUTHENTICATED'"
- ")",
- name="document_type_shape",
- ),
- Index(
- "ix_doc_document_type_active_updated",
- "document_type",
- "is_deleted",
- "updated_at",
- ),
- Index(
- "ix_doc_document_parent_type_active",
- "parent_document_id",
- "document_type",
- "is_deleted",
- ),
- Index(
- "ix_doc_document_root_active",
- "root_document_id",
- "is_deleted",
- ),
- Index(
- "ix_doc_document_category_active",
- "category_id",
- "is_deleted",
- ),
- Index(
- "ix_doc_document_organization_active",
- "organization_id",
- "is_deleted",
- ),
- Index(
- "ix_doc_document_status_active",
- "document_status",
- "is_deleted",
- ),
- Index(
- "ix_doc_document_security_active",
- "security_level",
- "is_deleted",
- ),
- Index("ix_doc_document_file_hash", "file_hash"),
- Index(
- "ix_doc_document_creator_active",
- "created_by",
- "is_deleted",
- ),
- Index(
- "ix_doc_document_attachment_type_active_updated",
- "attachment_type",
- "is_deleted",
- "updated_at",
- ),
- Index(
- "ix_doc_document_file_extension_active_updated",
- "file_extension",
- "is_deleted",
- "updated_at",
- ),
- Index(
- "ix_doc_document_recycle_deleted",
- "is_deleted",
- "deleted_at",
- "id",
- ),
- )
- document_name: Mapped[str] = mapped_column(String(255), nullable=False)
- summary: Mapped[str | None] = mapped_column(Text, nullable=True)
- # 详细说明:管理员在"查看"对话框维护,供普通用户阅读;不参与全文检索
- description: Mapped[str | None] = mapped_column(Text, nullable=True)
- document_type: Mapped[str] = mapped_column(String(32), nullable=False)
- document_status: Mapped[str] = mapped_column(
- String(32),
- nullable=False,
- server_default=text("'DRAFT'"),
- )
- security_level: Mapped[str] = mapped_column(
- String(32),
- nullable=False,
- server_default=text("'INTERNAL'"),
- )
- visibility_type: Mapped[str] = mapped_column(
- String(32),
- nullable=False,
- server_default=text("'CUSTOM'"),
- )
- attachment_type: Mapped[str | None] = mapped_column(String(32), nullable=True)
- category_id: Mapped[int | None] = mapped_column(
- BigInteger,
- ForeignKey("doc_category.id", ondelete="RESTRICT"),
- nullable=True,
- )
- category_name: Mapped[str | None] = mapped_column(String(128), nullable=True)
- category_path: Mapped[str | None] = mapped_column(String(1000), nullable=True)
- organization_id: Mapped[int | None] = mapped_column(
- BigInteger,
- ForeignKey("sys_organization.id", ondelete="RESTRICT"),
- nullable=True,
- )
- organization_name: Mapped[str | None] = mapped_column(String(128), nullable=True)
- parent_document_id: Mapped[int | None] = mapped_column(
- BigInteger,
- ForeignKey("doc_document.id", ondelete="RESTRICT"),
- nullable=True,
- )
- root_document_id: Mapped[int | None] = mapped_column(
- BigInteger,
- ForeignKey("doc_document.id", ondelete="RESTRICT"),
- nullable=True,
- )
- tags: Mapped[list[str] | None] = mapped_column(JSON, nullable=True)
- original_file_name: Mapped[str] = mapped_column(String(255), nullable=False)
- file_relative_path: Mapped[str] = mapped_column(String(1000), nullable=False)
- file_extension: Mapped[str] = mapped_column(String(20), nullable=False)
- mime_type: Mapped[str | None] = mapped_column(String(128), nullable=True)
- file_size: Mapped[int] = mapped_column(
- BigInteger,
- nullable=False,
- server_default=text("0"),
- )
- file_hash: Mapped[str] = mapped_column(String(64), nullable=False)
- # 管理员拖动排序:主案范围内全局排序、子方案在所属主案内排序;步长10便于插入。
- sort_no: Mapped[int] = mapped_column(
- Integer,
- nullable=False,
- server_default=text("0"),
- )
- child_count: Mapped[int] = mapped_column(
- Integer,
- nullable=False,
- server_default=text("0"),
- )
- attachment_count: Mapped[int] = mapped_column(
- Integer,
- nullable=False,
- server_default=text("0"),
- )
- view_count: Mapped[int] = mapped_column(
- BigInteger,
- nullable=False,
- server_default=text("0"),
- )
- download_count: Mapped[int] = mapped_column(
- BigInteger,
- nullable=False,
- server_default=text("0"),
- )
- search_text: Mapped[str | None] = mapped_column(mysql.LONGTEXT, nullable=True)
- content_text: Mapped[str | None] = mapped_column(mysql.LONGTEXT, nullable=True)
- content_extract_status: Mapped[str] = mapped_column(
- String(32),
- nullable=False,
- server_default=text("'UNSUPPORTED'"),
- )
- content_extracted_at: Mapped[datetime | None] = mapped_column(
- mysql.DATETIME(fsp=3),
- nullable=True,
- )
- created_by_name: Mapped[str | None] = mapped_column(String(64), nullable=True)
- updated_by_name: Mapped[str | None] = mapped_column(String(64), nullable=True)
- # 方案密码锁:Argon2id 哈希(NULL=未设密);保护位对普通用户生效,
- # 管理员对设密主案的除删除外全部操作均需密码(服务层判定)。
- password_hash: Mapped[str | None] = mapped_column(String(255), nullable=True)
- protect_view: Mapped[bool] = mapped_column(
- Boolean,
- nullable=False,
- server_default=text("0"),
- )
- protect_download: Mapped[bool] = mapped_column(
- Boolean,
- nullable=False,
- server_default=text("0"),
- )
- protect_edit: Mapped[bool] = mapped_column(
- Boolean,
- nullable=False,
- server_default=text("0"),
- )
- protect_delete: Mapped[bool] = mapped_column(
- Boolean,
- nullable=False,
- server_default=text("0"),
- )
- protect_config_permission: Mapped[bool] = mapped_column(
- Boolean,
- nullable=False,
- server_default=text("0"),
- )
- protect_bind_attachment: Mapped[bool] = mapped_column(
- Boolean,
- nullable=False,
- server_default=text("0"),
- )
- protect_unbind_attachment: Mapped[bool] = mapped_column(
- Boolean,
- nullable=False,
- server_default=text("0"),
- )
|