微信小程序页面迁移校验之前 P5任务处理之前

This commit is contained in:
Neo
2026-03-09 01:19:21 +08:00
parent 263bf96035
commit 6e20987d2f
1112 changed files with 153824 additions and 219694 deletions

View File

@@ -0,0 +1,217 @@
# -*- coding: utf-8 -*-
"""
备注回溯重分类器Note Reclassifier
召回完成后,回溯检查是否有普通备注需重分类为回访备注。
查找 service_time 之后的第一条 normal 备注 → 更新为 follow_up →
触发 AI 应用 6 接口(占位)→ 根据 ai_score 生成 follow_up_visit 任务。
由 trigger_jobs 中的 note_reclassify_backfill 配置驱动event: recall_completed
"""
import json
import logging
logger = logging.getLogger(__name__)
def _get_connection():
"""延迟导入 get_connection避免模块级导入失败。"""
from app.database import get_connection
return get_connection()
def _insert_history(
cur,
task_id: int,
action: str,
old_status: str | None = None,
new_status: str | None = None,
old_task_type: str | None = None,
new_task_type: str | None = None,
detail: dict | None = None,
) -> None:
"""在 coach_task_history 中记录变更。"""
cur.execute(
"""
INSERT INTO biz.coach_task_history
(task_id, action, old_status, new_status,
old_task_type, new_task_type, detail)
VALUES (%s, %s, %s, %s, %s, %s, %s)
""",
(
task_id,
action,
old_status,
new_status,
old_task_type,
new_task_type,
json.dumps(detail) if detail else None,
),
)
def ai_analyze_note(note_id: int) -> int | None:
"""
AI 应用 6 备注分析接口(占位)。
P5 AI 集成层实现后替换此占位函数。
当前返回 None 表示 AI 未就绪,跳过评分逻辑。
"""
return None
def run(payload: dict | None = None) -> dict:
"""
备注回溯主流程。
payload 包含: {site_id, assistant_id, member_id, service_time}
1. 查找 biz.notes 中该 (site_id, target_type='member', target_id=member_id)
在 service_time 之后提交的第一条 type='normal' 的备注
2. 将该备注 type 从 'normal' 更新为 'follow_up'
3. 触发 AI 应用 6 接口P5 实现,本 SPEC 仅定义触发接口):
- 调用 ai_analyze_note(note_id) → 返回 ai_score
4. 若 ai_score >= 6
- 生成 follow_up_visit 任务status='completed'(回溯完成)
5. 若 ai_score < 6
- 生成 follow_up_visit 任务status='active'(需助教重新备注)
返回: {"reclassified_count": int, "tasks_created": int}
"""
if not payload:
return {"reclassified_count": 0, "tasks_created": 0}
site_id = payload.get("site_id")
assistant_id = payload.get("assistant_id")
member_id = payload.get("member_id")
service_time = payload.get("service_time")
if not all([site_id, assistant_id, member_id, service_time]):
logger.warning("备注回溯缺少必要参数: %s", payload)
return {"reclassified_count": 0, "tasks_created": 0}
reclassified_count = 0
tasks_created = 0
conn = _get_connection()
try:
# ── 1. 查找 service_time 之后的第一条 normal 备注 ──
note_id = None
with conn.cursor() as cur:
cur.execute(
"""
SELECT id
FROM biz.notes
WHERE site_id = %s
AND target_type = 'member'
AND target_id = %s
AND type = 'normal'
AND created_at > %s
ORDER BY created_at ASC
LIMIT 1
""",
(site_id, member_id, service_time),
)
row = cur.fetchone()
if row:
note_id = row[0]
conn.commit()
if note_id is None:
logger.info(
"未找到符合条件的 normal 备注: site_id=%s, member_id=%s",
site_id, member_id,
)
return {"reclassified_count": 0, "tasks_created": 0}
# ── 2. 将备注 type 从 'normal' 更新为 'follow_up' ──
with conn.cursor() as cur:
cur.execute("BEGIN")
cur.execute(
"""
UPDATE biz.notes
SET type = 'follow_up', updated_at = NOW()
WHERE id = %s AND type = 'normal'
""",
(note_id,),
)
conn.commit()
reclassified_count = 1
# ── 3. 触发 AI 应用 6 接口(占位,当前返回 None ──
ai_score = ai_analyze_note(note_id)
# ── 4/5. 根据 ai_score 生成 follow_up_visit 任务 ──
if ai_score is not None:
if ai_score >= 6:
# 回溯完成:生成 completed 任务
task_status = "completed"
else:
# 需助教重新备注:生成 active 任务
task_status = "active"
with conn.cursor() as cur:
cur.execute("BEGIN")
cur.execute(
"""
INSERT INTO biz.coach_tasks
(site_id, assistant_id, member_id, task_type,
status, completed_at, completed_task_type)
VALUES (
%s, %s, %s, 'follow_up_visit',
%s,
CASE WHEN %s = 'completed' THEN NOW() ELSE NULL END,
CASE WHEN %s = 'completed' THEN 'follow_up_visit' ELSE NULL END
)
RETURNING id
""",
(
site_id, assistant_id, member_id,
task_status, task_status, task_status,
),
)
new_task_row = cur.fetchone()
new_task_id = new_task_row[0]
# 记录任务创建历史
_insert_history(
cur,
new_task_id,
action="created_by_reclassify",
old_status=None,
new_status=task_status,
old_task_type=None,
new_task_type="follow_up_visit",
detail={
"note_id": note_id,
"ai_score": ai_score,
"source": "note_reclassifier",
},
)
conn.commit()
tasks_created = 1
else:
# AI 未就绪,跳过任务创建
logger.info(
"AI 接口未就绪,跳过任务创建: note_id=%s", note_id
)
except Exception:
logger.exception(
"备注回溯失败: site_id=%s, member_id=%s",
site_id, member_id,
)
conn.rollback()
finally:
conn.close()
logger.info(
"备注回溯完成: reclassified_count=%d, tasks_created=%d",
reclassified_count, tasks_created,
)
return {
"reclassified_count": reclassified_count,
"tasks_created": tasks_created,
}