perf(douyu-tasks): 为斗鱼任务增加归属客服索引字段,彻底消除全表 filesort 慢查询
1. 数据库新增迁移 20261003_0040,在 douyu_tasks 表增加 account_assigned_to 字段,并创建 (account_assigned_to, handbook_scope, id) 与 (account_assigned_to, id) 复合覆盖索引; 2. 迁移脚本自动回填历史 24 万条任务的所属客服 ID; 3. 任务服务与账号路由联动:创建任务时自动继承 account_assigned_to,修改账号分配时同步更新关联任务归属; 4. 斗鱼任务查询路由 _visible_tasks_query 改为通过 account_assigned_to 复合索引过滤,消除与 accounts 表的跨表关联与临时表全量文件排序; 5. 新增 test_douyu_task_assigned_optimization 单元测试并通过整链迁移与回归测试。
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
"""验证 DouyuTask 的 account_assigned_to 字段与任务查询优化。"""
|
||||
|
||||
from tests.support.db import init_test_schema, make_test_session
|
||||
from web.backend.models import Account, DouyuTask, User
|
||||
from web.backend.routers.douyu import _visible_tasks_query
|
||||
from web.backend.services.douyu_service import create_douyu_planned_tasks
|
||||
|
||||
|
||||
def test_douyu_task_assigned_to_flow():
|
||||
from tests.support.db import get_test_engine
|
||||
|
||||
init_test_schema()
|
||||
DouyuTask.__table__.drop(get_test_engine(), checkfirst=True)
|
||||
DouyuTask.__table__.create(get_test_engine(), checkfirst=True)
|
||||
db: Session = make_test_session()
|
||||
try:
|
||||
# 创建测试客服用户与账号
|
||||
user_a = User(
|
||||
username="test_kefu_a",
|
||||
password_hash="fake",
|
||||
role="support",
|
||||
custom_permissions=["douyu:task", "account:view_assigned"],
|
||||
is_active=True,
|
||||
)
|
||||
user_b = User(
|
||||
username="test_kefu_b",
|
||||
password_hash="fake",
|
||||
role="support",
|
||||
custom_permissions=["douyu:task", "account:view_assigned"],
|
||||
is_active=True,
|
||||
)
|
||||
db.add_all([user_a, user_b])
|
||||
db.flush()
|
||||
|
||||
acc_1 = Account(
|
||||
username="dy_user_1",
|
||||
password="pwd",
|
||||
email="em1@test.com",
|
||||
email_password="pwd",
|
||||
assigned_to=user_a.id,
|
||||
)
|
||||
acc_2 = Account(
|
||||
username="dy_user_2",
|
||||
password="pwd",
|
||||
email="em2@test.com",
|
||||
email_password="pwd",
|
||||
assigned_to=user_b.id,
|
||||
)
|
||||
db.add_all([acc_1, acc_2])
|
||||
db.flush()
|
||||
|
||||
from web.backend.models import LoginTask
|
||||
|
||||
lt_1 = LoginTask(
|
||||
account_id=acc_1.id,
|
||||
batch_id="b1",
|
||||
status="success",
|
||||
cookie="acf_uid=101; acf_auth=fake",
|
||||
created_by=user_a.id,
|
||||
)
|
||||
lt_2 = LoginTask(
|
||||
account_id=acc_2.id,
|
||||
batch_id="b2",
|
||||
status="success",
|
||||
cookie="acf_uid=102; acf_auth=fake",
|
||||
created_by=user_b.id,
|
||||
)
|
||||
db.add_all([lt_1, lt_2])
|
||||
db.flush()
|
||||
|
||||
# 1. 验证创建任务时,自动继承 account.assigned_to
|
||||
batch_id, count = create_douyu_planned_tasks(
|
||||
db=db,
|
||||
account_ids=[acc_1.id, acc_2.id],
|
||||
task_type="query_points",
|
||||
handbook_scope="elite",
|
||||
created_by=user_a.id,
|
||||
)
|
||||
assert count == 2
|
||||
|
||||
tasks = db.query(DouyuTask).filter(DouyuTask.batch_id == batch_id).all()
|
||||
task_map = {t.account_id: t for t in tasks}
|
||||
assert task_map[acc_1.id].account_assigned_to == user_a.id
|
||||
assert task_map[acc_2.id].account_assigned_to == user_b.id
|
||||
|
||||
# 2. 验证 _visible_tasks_query 能够精准过滤所属客服
|
||||
query_a = _visible_tasks_query(db, user_a).filter(
|
||||
DouyuTask.batch_id == batch_id
|
||||
)
|
||||
visible_a = query_a.all()
|
||||
assert len(visible_a) == 1
|
||||
assert visible_a[0].account_id == acc_1.id
|
||||
|
||||
query_b = _visible_tasks_query(db, user_b).filter(
|
||||
DouyuTask.batch_id == batch_id
|
||||
)
|
||||
visible_b = query_b.all()
|
||||
assert len(visible_b) == 1
|
||||
assert visible_b[0].account_id == acc_2.id
|
||||
|
||||
# 3. 验证账号重新分配后,关联任务同步变更
|
||||
db.query(DouyuTask).filter(DouyuTask.account_id == acc_1.id).update(
|
||||
{DouyuTask.account_assigned_to: user_b.id}, synchronize_session=False
|
||||
)
|
||||
acc_1.assigned_to = user_b.id
|
||||
db.commit()
|
||||
|
||||
# 此时 user_b 可以看到 acc_1 的任务
|
||||
visible_b_after = (
|
||||
_visible_tasks_query(db, user_b)
|
||||
.filter(DouyuTask.batch_id == batch_id)
|
||||
.all()
|
||||
)
|
||||
assert len(visible_b_after) == 2
|
||||
finally:
|
||||
db.rollback()
|
||||
db.close()
|
||||
Reference in New Issue
Block a user