2026-10-12 21:49:05 -04:00
|
|
|
|
"""基础数据管理六表:人员/辐射区域/拓展计划/产品/客户/归属变更留痕(M2)
|
|
|
|
|
|
|
|
|
|
|
|
- personnel / personnel_regions / personnel_plans:人员档案及其 1:N 子表
|
|
|
|
|
|
- products:产品主数据(类别锚定折算阈值参数)
|
|
|
|
|
|
- customers:客户档案(公司名称+负责人为业务唯一标识)
|
|
|
|
|
|
- ownership_change_logs:归属变更留痕(append-only,不回溯历史)
|
|
|
|
|
|
- 六张表的 T+ 同步预留列(source_no 等)全部可空、不设唯一键(对照表 #26)
|
|
|
|
|
|
|
|
|
|
|
|
Revision ID: e1133a7c670a
|
|
|
|
|
|
Revises: bfbda9c3191d
|
|
|
|
|
|
Create Date: 2026-10-13 09:36:02.219248
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
2026-10-12 23:22:34 -04:00
|
|
|
|
|
2026-10-12 21:49:05 -04:00
|
|
|
|
from collections.abc import Sequence
|
|
|
|
|
|
|
|
|
|
|
|
from alembic import op
|
|
|
|
|
|
import sqlalchemy as sa
|
|
|
|
|
|
from sqlalchemy.dialects import mysql
|
|
|
|
|
|
|
|
|
|
|
|
# revision identifiers, used by Alembic.
|
2026-10-12 23:22:34 -04:00
|
|
|
|
revision: str = "e1133a7c670a"
|
|
|
|
|
|
down_revision: str | Sequence[str] | None = "bfbda9c3191d"
|
2026-10-12 21:49:05 -04:00
|
|
|
|
branch_labels: str | Sequence[str] | None = None
|
|
|
|
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def upgrade() -> None:
|
|
|
|
|
|
"""应用迁移。"""
|
|
|
|
|
|
# ### commands auto generated by Alembic - please adjust! ###
|
2026-10-12 23:22:34 -04:00
|
|
|
|
op.create_table(
|
|
|
|
|
|
"products",
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"id", mysql.BIGINT(unsigned=True), autoincrement=True, nullable=False, comment="主键ID"
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("product_name", sa.String(length=128), nullable=False, comment="产品名称"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"spec_model",
|
|
|
|
|
|
sa.String(length=128),
|
|
|
|
|
|
server_default="",
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="规格型号(空串而非 NULL:参与组合唯一键,MySQL 组合索引对 NULL 不去重)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("unit", sa.String(length=16), nullable=True, comment="计量单位"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"stock_quantity",
|
|
|
|
|
|
sa.Numeric(precision=14, scale=2),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="数量(T+ 同步原样存储,语义待 IT 确认)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"sale_price",
|
|
|
|
|
|
sa.Numeric(precision=14, scale=4),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="销售单价(元,T+ 同步)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"product_category",
|
|
|
|
|
|
sa.String(length=32),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="产品类别:municipal_water市政水利 rail_transit轨道交通(决定折算阈值 30%/20%;T+同步不覆盖)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"status",
|
|
|
|
|
|
mysql.TINYINT(),
|
|
|
|
|
|
server_default="1",
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="状态:1启用 0停用",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("remark", sa.String(length=255), nullable=True, comment="备注"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"create_time",
|
|
|
|
|
|
sa.DateTime(),
|
|
|
|
|
|
server_default=sa.text("CURRENT_TIMESTAMP"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="创建时间",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"update_time",
|
|
|
|
|
|
sa.DateTime(),
|
|
|
|
|
|
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="更新时间",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"source_no",
|
|
|
|
|
|
sa.String(length=64),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="T+ 原始单据号(同步幂等去重锚点;唯一键待 IT 定键后升级)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("t_plus_id", sa.String(length=64), nullable=True, comment="T+ 系统内部标识"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"sync_batch_no",
|
|
|
|
|
|
sa.String(length=32),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="同步批次号(同步模块写入)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("synced_at", sa.DateTime(), nullable=True, comment="最近同步时间"),
|
|
|
|
|
|
sa.PrimaryKeyConstraint("id", name=op.f("pk_products")),
|
|
|
|
|
|
sa.UniqueConstraint("product_name", "spec_model", name="uk_products_name_spec"),
|
|
|
|
|
|
comment="产品表(类别锚定折算阈值参数;T+同步不覆盖产品类别)",
|
|
|
|
|
|
mysql_charset="utf8mb4",
|
|
|
|
|
|
mysql_collate="utf8mb4_general_ci",
|
|
|
|
|
|
mysql_engine="InnoDB",
|
|
|
|
|
|
)
|
|
|
|
|
|
op.create_index(op.f("idx_products_source_no"), "products", ["source_no"], unique=False)
|
|
|
|
|
|
op.create_table(
|
|
|
|
|
|
"ownership_change_logs",
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"id", mysql.BIGINT(unsigned=True), autoincrement=True, nullable=False, comment="主键ID"
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"owner_type", mysql.TINYINT(), nullable=False, comment="归属类型:1客户归属 2业务归属"
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"customer_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="客户ID(固化,owner_type=1 时非空)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"business_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="业务ID(固化,owner_type=2 时非空)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"old_owner_personnel_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="变更前负责营销人员ID(固化)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"new_owner_personnel_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="变更后负责营销人员ID(固化)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"old_assistant_personnel_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="变更前协助营销人员ID(固化,仅业务归属使用)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"new_assistant_personnel_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="变更后协助营销人员ID(固化,仅业务归属使用)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("change_reason", sa.String(length=255), nullable=True, comment="变更原因"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"operator_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="操作人ID(账号删除后置空)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"operator_name",
|
|
|
|
|
|
sa.String(length=64),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="操作人用户名(冗余固化)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"create_time",
|
|
|
|
|
|
sa.DateTime(),
|
|
|
|
|
|
server_default=sa.text("CURRENT_TIMESTAMP"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="创建时间",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.ForeignKeyConstraint(
|
|
|
|
|
|
["operator_id"],
|
|
|
|
|
|
["users.id"],
|
|
|
|
|
|
name=op.f("fk_ownership_change_logs_operator_id"),
|
|
|
|
|
|
ondelete="SET NULL",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.PrimaryKeyConstraint("id", name=op.f("pk_ownership_change_logs")),
|
|
|
|
|
|
comment="归属变更记录表(append-only;客户/业务归属变更留痕,不回溯历史)",
|
|
|
|
|
|
mysql_charset="utf8mb4",
|
|
|
|
|
|
mysql_collate="utf8mb4_general_ci",
|
|
|
|
|
|
mysql_engine="InnoDB",
|
|
|
|
|
|
)
|
|
|
|
|
|
op.create_index(
|
|
|
|
|
|
op.f("idx_ownership_change_logs_customer_id"),
|
|
|
|
|
|
"ownership_change_logs",
|
|
|
|
|
|
["customer_id"],
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
)
|
|
|
|
|
|
op.create_index(
|
|
|
|
|
|
op.f("idx_ownership_change_logs_business_id"),
|
|
|
|
|
|
"ownership_change_logs",
|
|
|
|
|
|
["business_id"],
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
)
|
|
|
|
|
|
op.create_index(
|
|
|
|
|
|
op.f("idx_ownership_change_logs_create_time"),
|
|
|
|
|
|
"ownership_change_logs",
|
|
|
|
|
|
["create_time"],
|
|
|
|
|
|
unique=False,
|
|
|
|
|
|
)
|
|
|
|
|
|
op.create_table(
|
|
|
|
|
|
"personnel",
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"id", mysql.BIGINT(unsigned=True), autoincrement=True, nullable=False, comment="主键ID"
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("personnel_name", sa.String(length=64), nullable=False, comment="姓名"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"business_role_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="岗位角色ID(决定计提公式模板;T+同步不覆盖)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"manager_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="直属营销经理ID(NULL=经理本人或未分配;团队成员关系锚点)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("phone", sa.String(length=20), nullable=True, comment="联系电话"),
|
|
|
|
|
|
sa.Column("hire_date", sa.Date(), nullable=True, comment="入职日期"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"is_probation",
|
|
|
|
|
|
mysql.TINYINT(),
|
|
|
|
|
|
server_default="0",
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="是否试用期:1是 0否(试用期绩效照算不发放)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"salary_standard",
|
|
|
|
|
|
sa.Numeric(precision=12, scale=2),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="月工资标准(元;T+同步不覆盖)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"status",
|
|
|
|
|
|
mysql.TINYINT(),
|
|
|
|
|
|
server_default="1",
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="状态:1在职 0离职(离职人员不可再被业务/客户归属引用)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("remark", sa.String(length=255), nullable=True, comment="备注"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"create_time",
|
|
|
|
|
|
sa.DateTime(),
|
|
|
|
|
|
server_default=sa.text("CURRENT_TIMESTAMP"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="创建时间",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"update_time",
|
|
|
|
|
|
sa.DateTime(),
|
|
|
|
|
|
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="更新时间",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"source_no",
|
|
|
|
|
|
sa.String(length=64),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="T+ 原始单据号(同步幂等去重锚点;唯一键待 IT 定键后升级)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("t_plus_id", sa.String(length=64), nullable=True, comment="T+ 系统内部标识"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"sync_batch_no",
|
|
|
|
|
|
sa.String(length=32),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="同步批次号(同步模块写入)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("synced_at", sa.DateTime(), nullable=True, comment="最近同步时间"),
|
|
|
|
|
|
sa.ForeignKeyConstraint(
|
|
|
|
|
|
["business_role_id"],
|
|
|
|
|
|
["business_roles.id"],
|
|
|
|
|
|
name=op.f("fk_personnel_business_role_id"),
|
|
|
|
|
|
ondelete="RESTRICT",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.ForeignKeyConstraint(
|
|
|
|
|
|
["manager_id"],
|
|
|
|
|
|
["personnel.id"],
|
|
|
|
|
|
name=op.f("fk_personnel_manager_id"),
|
|
|
|
|
|
ondelete="RESTRICT",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.PrimaryKeyConstraint("id", name=op.f("pk_personnel")),
|
|
|
|
|
|
comment="营销人员表(T+同步不覆盖岗位与工资标准)",
|
|
|
|
|
|
mysql_charset="utf8mb4",
|
|
|
|
|
|
mysql_collate="utf8mb4_general_ci",
|
|
|
|
|
|
mysql_engine="InnoDB",
|
|
|
|
|
|
)
|
|
|
|
|
|
op.create_index(
|
|
|
|
|
|
op.f("idx_personnel_business_role_id"), "personnel", ["business_role_id"], unique=False
|
|
|
|
|
|
)
|
|
|
|
|
|
op.create_index(op.f("idx_personnel_manager_id"), "personnel", ["manager_id"], unique=False)
|
|
|
|
|
|
op.create_index(op.f("idx_personnel_source_no"), "personnel", ["source_no"], unique=False)
|
|
|
|
|
|
op.create_table(
|
|
|
|
|
|
"customers",
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"id", mysql.BIGINT(unsigned=True), autoincrement=True, nullable=False, comment="主键ID"
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("company_name", sa.String(length=128), nullable=False, comment="公司名称"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"owner_name",
|
|
|
|
|
|
sa.String(length=64),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="客户负责人姓名(客户公司负责人的人名,非系统人员)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("contact_person", sa.String(length=64), nullable=True, comment="联系人"),
|
|
|
|
|
|
sa.Column("contact_phone", sa.String(length=20), nullable=True, comment="联系电话"),
|
|
|
|
|
|
sa.Column("address", sa.String(length=255), nullable=True, comment="地址"),
|
|
|
|
|
|
sa.Column("industry", sa.String(length=64), nullable=True, comment="所属行业(自由文本)"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"customer_type",
|
|
|
|
|
|
sa.String(length=32),
|
|
|
|
|
|
server_default="personal",
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="客户分类:personal个人户 manager_personal经理个人户 enterprise企业户(计提规则匹配锚点;T+同步不覆盖)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"owner_personnel_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="归属负责营销人员ID(手动修改不回溯历史,变更留痕见 ownership_change_logs)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("remark", sa.String(length=255), nullable=True, comment="备注"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"create_time",
|
|
|
|
|
|
sa.DateTime(),
|
|
|
|
|
|
server_default=sa.text("CURRENT_TIMESTAMP"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="创建时间",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"update_time",
|
|
|
|
|
|
sa.DateTime(),
|
|
|
|
|
|
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="更新时间",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"source_no",
|
|
|
|
|
|
sa.String(length=64),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="T+ 原始单据号(同步幂等去重锚点;唯一键待 IT 定键后升级)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("t_plus_id", sa.String(length=64), nullable=True, comment="T+ 系统内部标识"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"sync_batch_no",
|
|
|
|
|
|
sa.String(length=32),
|
|
|
|
|
|
nullable=True,
|
|
|
|
|
|
comment="同步批次号(同步模块写入)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("synced_at", sa.DateTime(), nullable=True, comment="最近同步时间"),
|
|
|
|
|
|
sa.ForeignKeyConstraint(
|
|
|
|
|
|
["owner_personnel_id"],
|
|
|
|
|
|
["personnel.id"],
|
|
|
|
|
|
name=op.f("fk_customers_owner_personnel_id"),
|
|
|
|
|
|
ondelete="RESTRICT",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.PrimaryKeyConstraint("id", name=op.f("pk_customers")),
|
|
|
|
|
|
sa.UniqueConstraint("company_name", "owner_name", name="uk_customers_company_owner"),
|
|
|
|
|
|
comment="客户表(公司名称+负责人为业务唯一标识;T+同步不覆盖客户分类)",
|
|
|
|
|
|
mysql_charset="utf8mb4",
|
|
|
|
|
|
mysql_collate="utf8mb4_general_ci",
|
|
|
|
|
|
mysql_engine="InnoDB",
|
|
|
|
|
|
)
|
|
|
|
|
|
op.create_index(
|
|
|
|
|
|
op.f("idx_customers_customer_type"), "customers", ["customer_type"], unique=False
|
2026-10-12 21:49:05 -04:00
|
|
|
|
)
|
2026-10-12 23:22:34 -04:00
|
|
|
|
op.create_index(
|
|
|
|
|
|
op.f("idx_customers_owner_personnel_id"), "customers", ["owner_personnel_id"], unique=False
|
2026-10-12 21:49:05 -04:00
|
|
|
|
)
|
2026-10-12 23:22:34 -04:00
|
|
|
|
op.create_index(op.f("idx_customers_source_no"), "customers", ["source_no"], unique=False)
|
|
|
|
|
|
op.create_table(
|
|
|
|
|
|
"personnel_plans",
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"id", mysql.BIGINT(unsigned=True), autoincrement=True, nullable=False, comment="主键ID"
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"personnel_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="人员ID(personnel.id)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"plan_period",
|
|
|
|
|
|
sa.String(length=32),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="计划期间(如 2026Q1、2026-05)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("plan_content", sa.String(length=500), nullable=False, comment="计划内容"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"target_desc", sa.String(length=255), nullable=True, comment="指标说明(文字描述)"
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"source_no", sa.String(length=64), nullable=True, comment="T+ 原始条目号(预留)"
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"create_time",
|
|
|
|
|
|
sa.DateTime(),
|
|
|
|
|
|
server_default=sa.text("CURRENT_TIMESTAMP"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="创建时间",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"update_time",
|
|
|
|
|
|
sa.DateTime(),
|
|
|
|
|
|
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="更新时间",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.ForeignKeyConstraint(
|
|
|
|
|
|
["personnel_id"],
|
|
|
|
|
|
["personnel.id"],
|
|
|
|
|
|
name=op.f("fk_personnel_plans_personnel_id"),
|
|
|
|
|
|
ondelete="RESTRICT",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.PrimaryKeyConstraint("id", name=op.f("pk_personnel_plans")),
|
|
|
|
|
|
comment="市场拓展计划表",
|
|
|
|
|
|
mysql_charset="utf8mb4",
|
|
|
|
|
|
mysql_collate="utf8mb4_general_ci",
|
|
|
|
|
|
mysql_engine="InnoDB",
|
2026-10-12 21:49:05 -04:00
|
|
|
|
)
|
2026-10-12 23:22:34 -04:00
|
|
|
|
op.create_index(
|
|
|
|
|
|
op.f("idx_personnel_plans_personnel_id"), "personnel_plans", ["personnel_id"], unique=False
|
2026-10-12 21:49:05 -04:00
|
|
|
|
)
|
2026-10-12 23:22:34 -04:00
|
|
|
|
op.create_table(
|
|
|
|
|
|
"personnel_regions",
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"id", mysql.BIGINT(unsigned=True), autoincrement=True, nullable=False, comment="主键ID"
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"personnel_id",
|
|
|
|
|
|
mysql.BIGINT(unsigned=True),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="人员ID(personnel.id)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"region_name",
|
|
|
|
|
|
sa.String(length=128),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="区域名称(产品辐射地理范围)",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column("remark", sa.String(length=255), nullable=True, comment="备注"),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"create_time",
|
|
|
|
|
|
sa.DateTime(),
|
|
|
|
|
|
server_default=sa.text("CURRENT_TIMESTAMP"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="创建时间",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.Column(
|
|
|
|
|
|
"update_time",
|
|
|
|
|
|
sa.DateTime(),
|
|
|
|
|
|
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
comment="更新时间",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.ForeignKeyConstraint(
|
|
|
|
|
|
["personnel_id"],
|
|
|
|
|
|
["personnel.id"],
|
|
|
|
|
|
name=op.f("fk_personnel_regions_personnel_id"),
|
|
|
|
|
|
ondelete="RESTRICT",
|
|
|
|
|
|
),
|
|
|
|
|
|
sa.PrimaryKeyConstraint("id", name=op.f("pk_personnel_regions")),
|
|
|
|
|
|
comment="产品辐射区域表",
|
|
|
|
|
|
mysql_charset="utf8mb4",
|
|
|
|
|
|
mysql_collate="utf8mb4_general_ci",
|
|
|
|
|
|
mysql_engine="InnoDB",
|
2026-10-12 21:49:05 -04:00
|
|
|
|
)
|
2026-10-12 23:22:34 -04:00
|
|
|
|
op.create_index(
|
|
|
|
|
|
op.f("idx_personnel_regions_personnel_id"),
|
|
|
|
|
|
"personnel_regions",
|
|
|
|
|
|
["personnel_id"],
|
|
|
|
|
|
unique=False,
|
2026-10-12 21:49:05 -04:00
|
|
|
|
)
|
|
|
|
|
|
# ### end Alembic commands ###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def downgrade() -> None:
|
|
|
|
|
|
"""回滚迁移。
|
|
|
|
|
|
|
|
|
|
|
|
**注意(autogenerate 不会替你处理)**:生成的 `op.drop_index` 与
|
|
|
|
|
|
`op.drop_table` 顺序需人工检查 —— 若某索引是外键的唯一支撑索引,
|
|
|
|
|
|
单独 drop_index 会报 MySQL 1553(needed in a foreign key constraint)。
|
|
|
|
|
|
**整表回滚应只保留 `op.drop_table`**,DROP TABLE 会连带删除索引与外键。
|
|
|
|
|
|
"""
|
2026-10-12 23:22:34 -04:00
|
|
|
|
op.drop_table("personnel_regions")
|
|
|
|
|
|
op.drop_table("personnel_plans")
|
|
|
|
|
|
op.drop_table("customers")
|
|
|
|
|
|
op.drop_table("personnel")
|
|
|
|
|
|
op.drop_table("ownership_change_logs")
|
|
|
|
|
|
op.drop_table("products")
|