| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- """文档归属部门:doc_document 新增 organization_id/organization_name。
- Revision ID: 0006_document_organization
- Revises: 0005_category_flat_mode
- Create Date: 2026-08-14
- 变更要点:
- 1. doc_document 新增 organization_id(外键 → sys_organization.id,RESTRICT)
- 与 organization_name(冗余字符串,便于检索/展示)。
- 2. 适用于主案/子案/附件三类文档;与创建者字段独立维护——
- 用户调岗不影响历史文档归属。
- 3. 历史数据回填:按 created_by → sys_user.organization_id 关联填充;
- 无创建者或创建者无部门的记录保持 NULL。
- 4. 新增联合索引 ix_doc_document_organization_active(organization_id, is_deleted)
- 支撑按部门过滤的列表查询。
- 5. 机构改名时由 organization_service.update_organization 级联刷新
- organization_name(与现有 User 冗余字段策略一致)。
- """
- from __future__ import annotations
- from collections.abc import Sequence
- from alembic import op
- import sqlalchemy as sa
- revision: str = "0006_document_organization"
- down_revision: str | None = "0005_category_flat_mode"
- branch_labels: str | Sequence[str] | None = None
- depends_on: str | Sequence[str] | None = None
- _TABLE = "doc_document"
- _INDEX = "ix_doc_document_organization_active"
- def _existing_columns(table: str) -> set[str]:
- inspector = sa.inspect(op.get_bind())
- return {item["name"] for item in inspector.get_columns(table)}
- def _existing_index_names(table: str) -> set[str]:
- inspector = sa.inspect(op.get_bind())
- return {item["name"] for item in inspector.get_indexes(table)}
- def upgrade() -> None:
- columns = _existing_columns(_TABLE)
- if "organization_id" not in columns:
- op.add_column(
- _TABLE,
- sa.Column(
- "organization_id",
- sa.BigInteger(),
- nullable=True,
- ),
- )
- op.create_foreign_key(
- "fk_doc_document_organization_id_sys_organization",
- _TABLE,
- "sys_organization",
- ["organization_id"],
- ["id"],
- ondelete="RESTRICT",
- )
- if "organization_name" not in columns:
- op.add_column(
- _TABLE,
- sa.Column(
- "organization_name",
- sa.String(length=128),
- nullable=True,
- ),
- )
- # 历史回填:按创建者当前部门填充;剩余保持 NULL
- op.execute(
- sa.text(
- "UPDATE doc_document d "
- "JOIN sys_user u ON d.created_by = u.id "
- "SET d.organization_id = u.organization_id, "
- " d.organization_name = u.organization_name "
- "WHERE d.organization_id IS NULL "
- " AND u.organization_id IS NOT NULL"
- )
- )
- indexes = _existing_index_names(_TABLE)
- if _INDEX not in indexes:
- op.create_index(
- _INDEX,
- _TABLE,
- ["organization_id", "is_deleted"],
- unique=False,
- )
- def downgrade() -> None:
- # 外键存在性检测:MySQL 8 下 fk 名稳定,按名清理
- # MySQL要求先删外键再删其依赖的索引,顺序不可交换
- bind = op.get_bind()
- fk_rows = bind.execute(
- sa.text(
- "SELECT CONSTRAINT_NAME FROM information_schema.KEY_COLUMN_USAGE "
- "WHERE TABLE_SCHEMA = DATABASE() "
- " AND TABLE_NAME = 'doc_document' "
- " AND COLUMN_NAME = 'organization_id' "
- " AND REFERENCED_TABLE_NAME = 'sys_organization'"
- )
- ).fetchall()
- for row in fk_rows:
- op.drop_constraint(row[0], _TABLE, type_="foreignkey")
- indexes = _existing_index_names(_TABLE)
- if _INDEX in indexes:
- op.drop_index(_INDEX, table_name=_TABLE)
- columns = _existing_columns(_TABLE)
- if "organization_name" in columns:
- op.drop_column(_TABLE, "organization_name")
- if "organization_id" in columns:
- op.drop_column(_TABLE, "organization_id")
|