交付出件箱

← 返回出件箱

批次四 83.26/83.27 开工前 verify 原始输出

# 83.26/83.27 开工前 verify · 基线 origin/test 81962b75 · 2026-09-28 22:13:53
## V1 基线漂移(81962b75→origin/test 提交数)
0
## V2 本地库迁移状态
phase83_002
-- 对照:本地库 org_parent_seat_counter / seat_open_batches / users.seat_batch_id 是否存在
org_parent_seat_counter||1
## V3 仓库里 phase83_002 的子迁移(应只有 phase85_001)
migrations/versions/phase85_001_wecom_agent.py
## V4 user.py 插入点上下文
150:     #    传了二维码的门店席位"兜底(app/api/parent_account.py 取码逻辑)。
151:     # FK 加 ondelete="SET NULL"(镜像本文件 linked_org_id 写法):这里不是跨库/跨服务
152:     # 引用(不同于结算侧 MR #419 "结算账号对 users 无 REFERENCES 权限"那种跨账号场景)——
153:     # 本列与 users.id 同表同库同迁移账号,该账号本就是建 users 表的账号,天然有权限;
154:     # 且 internal_settlement.py:393 有真实的批量硬删除孤儿席位路径,若不设 ON DELETE
155:     # SET NULL,删除一个"曾开通过子席位"的席位会被 FK 拦下导致清理事务整体失败。
156:     created_by_seat_id = Column(
157:         Integer,
158:         ForeignKey("users.id", ondelete="SET NULL"),
159:         nullable=True,
160:         comment="开通该席位的门店席位 id(自引用 users.id)· NULL=平台开通或老数据 · Phase 74",
161:     )
162:     # 二期批3(方案总纲五.3 拍板)· 席位备注名:门店开子席位时顺手起的好认名字
163:     # (如"梓豪妈妈"),子席位列表 + 绑定家长选择器都展示它 · 选填 · 无 backfill,
164:     # 存量席位天然 NULL(前端约定"备注为空的行不显示该段文字")。
165:     seat_remark_name = Column(
166:         String(50),
167:         nullable=True,
168:         comment="席位备注名(门店起的好认名·选填)· 二期批3",
169:     )
170:     # 【已废弃 2026-09-04】Phase 68 初始密码 7 天有效期机制整体拆除(生产大量客户超 7 天
171:     # 才首登被集体拒登 · 长官拍板不设限)。列保留不删(免 DB 变更)· 业务代码不再读写 ·
172:     # 存量值一律忽略。
## V5 models/__init__ __all__ 末尾
106:__all__ = [
160:]
154:     "BrandCode",
155:     "BrandCodeActivation",
156:     "WecomServiceGroup",
157:     "WecomOrgSettings",
158:     "WecomEvent",
159:     "WecomAgentHeartbeat",
160: ]
161: 
## V6 模型里部分索引写法先例
app/models/brand_code.py:113:            postgresql_where=text("wechat_unionid IS NULL"),
app/models/user.py:208:            postgresql_where=text("role = 'channel' AND channel_id IS NOT NULL"),
## V7 依赖版本(venv 实装)
sqlalchemy 2.0.35 | cryptography 50.0.0 | openpyxl 3.1.5
7:sqlalchemy==2.0.35
8:alembic==1.18.4
9:asyncpg==0.31.0
## V8 pre-commit 守卫
-rwxrwxr-x 1 dev2 dev2 5492 Aug 11 13:05 /home/dev2/doctor-ai-brain/.git/hooks/pre-commit
## V9 alembic 配置
8:script_location = %(here)s/migrations
45:# to <script_location>/versions.  When using multiple version
89:sqlalchemy.url = postgresql://casdoor:casdoor_password@localhost:5432/doctor_ai_brain?options=-csearch_path%%3Dpublic
15:# 从环境变量读取 DATABASE_URL(优先级高于 alembic.ini)
16:database_url = os.getenv("DATABASE_URL")
## V10 迁移建表先例(phase83_002 全文 upgrade/downgrade 段)
60:         "ck_organizations_subseat_prefix_format",
61:         "organizations",
62:         "subseat_prefix ~ '^[A-Z]{1,6}$'",
63:     )
64: 
65:     # --- d) 新表 org_parent_seat_counter ------------------------------------
66:     op.create_table(
67:         "org_parent_seat_counter",
68:         sa.Column(
69:             "org_id",
70:             sa.Integer,
71:             sa.ForeignKey("organizations.id", ondelete="CASCADE"),
72:             primary_key=True,
73:         ),
74:         sa.Column(
75:             "last_seq",
76:             sa.Integer,
77:             nullable=False,
78:             server_default=sa.text("100000"),
79:             comment="该机构已分配到的最大家长号序号(六位数字,100001 起用)",
80:         ),
81:         sa.Column(
82:             "updated_at",
83:             sa.TIMESTAMP(timezone=True),
84:             server_default=sa.text("now()"),
85:         ),
86:         sa.CheckConstraint(
87:             "last_seq BETWEEN 100000 AND 999999",
88:             name="ck_org_parent_seat_counter_range",
89:         ),
90:     )
91:     op.execute(
92:         "COMMENT ON TABLE org_parent_seat_counter IS "
93:         "'按机构原子计数家长号序号(Phase 83.13)· 取代 seat_seq_counter 的按月计数'"
94:     )
95: 
96:     # --- e) users.phone_login_enabled ---------------------------------------
97:     op.add_column(
98:         "users",
99:         sa.Column(
100:             "phone_login_enabled",
101:             sa.Boolean,
102:             nullable=False,
103:             server_default=sa.text("false"),
104:             comment="新席位开 · 设好密码后只认手机号登录 · 老席位 false 零感知 · "
105:             "Phase 83.13",
106:         ),
107:     )
108: 
109: 
110: def downgrade() -> None:
111:     # 严格逆序:e → d → c → b → a
112:     op.drop_column("users", "phone_login_enabled")
113: 
114:     op.execute("DROP TABLE IF EXISTS org_parent_seat_counter")
115: 
116:     op.drop_constraint(
117:         "ck_organizations_subseat_prefix_format", "organizations", type_="check"
118:     )
119:     op.drop_index("uq_organizations_subseat_prefix", table_name="organizations")
120:     op.drop_column("organizations", "subseat_prefix")