bea288e414
== 已部署上线 (9项) == - 代办事项真实数据源集成 (企微审批API 8bug修复链) - H5/坐席端 Logo样式统一+绿色背景 - 视频引导页修复 (localStorage key v2) - 坐席端 v9 Vue版本修复 (ElMessage._context) - 截图按钮 v10 修复 (getDisplayMedia user gesture) - 扫码样式恢复+H5扫码登录跳转修复 - H5截图快捷键提示 == 代码完成待部署 (3项) == - 知识迭代3Bug修复 (#8 POST端点/#7 MERGE幂等/#6 过期检查) - 会议室预定-小鱼易联终端 (40文件, 40/40测试通过) - IT资产升级审批推送 (asset_service.py) == 需求文档 (2项) == - 坐席端AI辅助消息框-PRD (4项新功能确认) - 坐席端布局优化建议 v2.0 (7天计划) == 新增文档 == - 日报-2026-07-11.md - 知识迭代Bug修复报告-20260711.md - 会议室预定-部署指南.md - CHANGELOG.md 更新 == 测试 == - test_todo_integration.py: 40/40 - test_meetingroom.py: 40/40 - test_bugfix_ki_suggestions.py: 21/21
195 lines
9.1 KiB
Python
195 lines
9.1 KiB
Python
"""add business_contacts and routing_events tables
|
||
|
||
新建 business_contacts 表(业务联系人)和 routing_events 表(路由命中统计),
|
||
并预置初始联系人数据(行政/HR/财务/法务/物业 各1-2人)。
|
||
|
||
关联设计文档:docs/03-技术架构/业务路由推荐-架构设计.md
|
||
|
||
Revision ID: 047_business_contacts
|
||
Revises: 046_add_login_logs
|
||
Create Date: 2026-07-15
|
||
|
||
"""
|
||
from typing import Sequence, Union
|
||
|
||
from alembic import op
|
||
import sqlalchemy as sa
|
||
|
||
|
||
# revision identifiers, used by Alembic.
|
||
revision: str = '047_business_contacts'
|
||
down_revision: Union[str, None] = '046_add_login_logs'
|
||
branch_labels: Union[str, Sequence[str], None] = None
|
||
depends_on: Union[str, Sequence[str], None] = None
|
||
|
||
|
||
def upgrade() -> None:
|
||
# --------------------------------------------------------------------------
|
||
# 新建 business_contacts 表
|
||
# --------------------------------------------------------------------------
|
||
op.create_table(
|
||
'business_contacts',
|
||
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True,
|
||
comment='主键'),
|
||
sa.Column('name', sa.String(50), nullable=False, comment='联系人姓名'),
|
||
sa.Column('gender', sa.String(10), nullable=False, server_default='male',
|
||
comment='性别(male/female)'),
|
||
sa.Column('department', sa.String(100), nullable=False, comment='部门名称'),
|
||
sa.Column('position', sa.String(100), nullable=False, comment='岗位'),
|
||
sa.Column('responsibility', sa.String(500), nullable=False, comment='负责业务描述'),
|
||
sa.Column('extension', sa.String(20), nullable=True, comment='分机号'),
|
||
sa.Column('service_area', sa.String(200), nullable=True, comment='服务区域/办公地点'),
|
||
sa.Column('wecom_userid', sa.String(100), nullable=False, comment='企微用户ID'),
|
||
sa.Column('avatar_url', sa.String(500), nullable=True, comment='头像URL'),
|
||
sa.Column('business_category', sa.String(50), nullable=False,
|
||
comment='业务类别(行政/人力资源/财务/法务/行政-物业)'),
|
||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default=sa.text('true'),
|
||
comment='是否启用'),
|
||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False,
|
||
server_default=sa.func.now(), comment='创建时间'),
|
||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False,
|
||
server_default=sa.func.now(), comment='更新时间'),
|
||
)
|
||
|
||
# 索引:按业务类别 + 是否启用查询
|
||
op.create_index(
|
||
'idx_business_contacts_category',
|
||
'business_contacts',
|
||
['business_category', 'is_active'],
|
||
)
|
||
|
||
# --------------------------------------------------------------------------
|
||
# 新建 routing_events 表(P1)
|
||
# --------------------------------------------------------------------------
|
||
op.create_table(
|
||
'routing_events',
|
||
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True,
|
||
comment='主键'),
|
||
sa.Column('conversation_id', sa.String(36),
|
||
sa.ForeignKey('conversations.id', ondelete='CASCADE'),
|
||
nullable=False, comment='会话ID'),
|
||
sa.Column('employee_id', sa.String(64), nullable=False, comment='员工ID'),
|
||
sa.Column('message_content', sa.String(500), nullable=False,
|
||
comment='触发路由的员工消息(截断)'),
|
||
sa.Column('business_category', sa.String(50), nullable=False,
|
||
comment='识别的业务类别'),
|
||
sa.Column('routing_confidence', sa.Float(), nullable=False, comment='路由置信度'),
|
||
sa.Column('contact_id', sa.Integer(),
|
||
sa.ForeignKey('business_contacts.id', ondelete='SET NULL'),
|
||
nullable=True, comment='推荐的联系人ID'),
|
||
sa.Column('contact_name', sa.String(50), nullable=False,
|
||
comment='联系人姓名(冗余)'),
|
||
sa.Column('is_clicked', sa.Boolean(), nullable=False, server_default=sa.text('false'),
|
||
comment='是否点击了联系TA'),
|
||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False,
|
||
server_default=sa.func.now(), comment='创建时间'),
|
||
)
|
||
|
||
# 索引
|
||
op.create_index('idx_routing_events_category', 'routing_events', ['business_category'])
|
||
op.create_index('idx_routing_events_conv', 'routing_events', ['conversation_id'])
|
||
|
||
# --------------------------------------------------------------------------
|
||
# 预置初始联系人数据
|
||
# --------------------------------------------------------------------------
|
||
# 说明:wecom_userid 使用占位符,上线前需替换为真实的企微通讯录 userid
|
||
# --------------------------------------------------------------------------
|
||
contacts_data = [
|
||
# 行政(2人)
|
||
{
|
||
'name': '王芳', 'gender': 'female', 'department': '行政部',
|
||
'position': '设备管理岗', 'responsibility': '打印机/复印机/扫描仪',
|
||
'extension': '8002', 'service_area': '滨江园区 3-5楼',
|
||
'wecom_userid': 'WangFang', 'avatar_url': '',
|
||
'business_category': '行政', 'is_active': True,
|
||
},
|
||
{
|
||
'name': '陈伟', 'gender': 'male', 'department': '行政部',
|
||
'position': '行政事务岗', 'responsibility': '办公用品/名片印刷/保洁服务',
|
||
'extension': '8003', 'service_area': '滨江园区 1-2楼',
|
||
'wecom_userid': 'ChenWei', 'avatar_url': '',
|
||
'business_category': '行政', 'is_active': True,
|
||
},
|
||
# 人力资源(2人)
|
||
{
|
||
'name': '李娜', 'gender': 'female', 'department': '人力资源部',
|
||
'position': '员工服务岗', 'responsibility': '工牌补办/考勤异常/入职手续',
|
||
'extension': '8005', 'service_area': '滨江园区 A栋3楼',
|
||
'wecom_userid': 'LiNa', 'avatar_url': '',
|
||
'business_category': '人力资源', 'is_active': True,
|
||
},
|
||
{
|
||
'name': '张磊', 'gender': 'male', 'department': '人力资源部',
|
||
'position': '薪酬福利岗', 'responsibility': '社保/公积金/离职手续',
|
||
'extension': '8006', 'service_area': '滨江园区 A栋3楼',
|
||
'wecom_userid': 'ZhangLei', 'avatar_url': '',
|
||
'business_category': '人力资源', 'is_active': True,
|
||
},
|
||
# 财务(1人)
|
||
{
|
||
'name': '刘洋', 'gender': 'male', 'department': '财务部',
|
||
'position': '费用报销岗', 'responsibility': '报销/发票/借款/工资条',
|
||
'extension': '8010', 'service_area': '滨江园区 B栋4楼',
|
||
'wecom_userid': 'LiuYang', 'avatar_url': '',
|
||
'business_category': '财务', 'is_active': True,
|
||
},
|
||
# 法务(1人)
|
||
{
|
||
'name': '赵敏', 'gender': 'female', 'department': '法务部',
|
||
'position': '法务顾问岗', 'responsibility': '合同/法律咨询/知识产权',
|
||
'extension': '8015', 'service_area': '滨江园区 C栋5楼',
|
||
'wecom_userid': 'ZhaoMin', 'avatar_url': '',
|
||
'business_category': '法务', 'is_active': True,
|
||
},
|
||
# 行政-物业(1人)
|
||
{
|
||
'name': '孙强', 'gender': 'male', 'department': '行政部',
|
||
'position': '物业管理岗', 'responsibility': '空调/电梯/门禁/停车',
|
||
'extension': '8008', 'service_area': '滨江园区 全园区',
|
||
'wecom_userid': 'SunQiang', 'avatar_url': '',
|
||
'business_category': '行政-物业', 'is_active': True,
|
||
},
|
||
]
|
||
|
||
# 批量插入初始数据
|
||
op.bulk_insert(
|
||
sa.table(
|
||
'business_contacts',
|
||
sa.column('name', sa.String),
|
||
sa.column('gender', sa.String),
|
||
sa.column('department', sa.String),
|
||
sa.column('position', sa.String),
|
||
sa.column('responsibility', sa.String),
|
||
sa.column('extension', sa.String),
|
||
sa.column('service_area', sa.String),
|
||
sa.column('wecom_userid', sa.String),
|
||
sa.column('avatar_url', sa.String),
|
||
sa.column('business_category', sa.String),
|
||
sa.column('is_active', sa.Boolean),
|
||
sa.column('created_at', sa.DateTime),
|
||
sa.column('updated_at', sa.DateTime),
|
||
),
|
||
[
|
||
{
|
||
**c,
|
||
'avatar_url': c['avatar_url'] or None,
|
||
'extension': c.get('extension') or None,
|
||
'service_area': c.get('service_area') or None,
|
||
'created_at': sa.func.now(),
|
||
'updated_at': sa.func.now(),
|
||
}
|
||
for c in contacts_data
|
||
],
|
||
)
|
||
|
||
|
||
def downgrade() -> None:
|
||
# 删除索引
|
||
op.drop_index('idx_routing_events_conv', table_name='routing_events')
|
||
op.drop_index('idx_routing_events_category', table_name='routing_events')
|
||
op.drop_index('idx_business_contacts_category', table_name='business_contacts')
|
||
|
||
# 删除表
|
||
op.drop_table('routing_events')
|
||
op.drop_table('business_contacts')
|