feat: expose acceptance Link AI aggregate data
This commit is contained in:
@@ -22,6 +22,7 @@ from crawler_client import (
|
|||||||
collect_keyword_sources,
|
collect_keyword_sources,
|
||||||
fetch_content_detail,
|
fetch_content_detail,
|
||||||
get_wen_resources,
|
get_wen_resources,
|
||||||
|
match_feishu_notes,
|
||||||
query_collection,
|
query_collection,
|
||||||
schedule_xapi_screenshots,
|
schedule_xapi_screenshots,
|
||||||
)
|
)
|
||||||
@@ -965,3 +966,61 @@ def list_acceptance_notes(public_id: str) -> dict[str, Any]:
|
|||||||
item["parsed_data"] = json.loads(item.pop("raw_json")) if item.get("raw_json") else None
|
item["parsed_data"] = json.loads(item.pop("raw_json")) if item.get("raw_json") else None
|
||||||
items.append(item)
|
items.append(item)
|
||||||
return {"items": items, "total": len(items)}
|
return {"items": items, "total": len(items)}
|
||||||
|
|
||||||
|
|
||||||
|
def get_acceptance_link_ai_data(public_id: str) -> dict[str, Any]:
|
||||||
|
"""返回 Link AI 所需的笔记、AI 采集结果和逐关键词比对结果。"""
|
||||||
|
with connection() as conn:
|
||||||
|
task = conn.execute(
|
||||||
|
"SELECT * FROM acceptance_tasks WHERE public_task_id=?", (public_id,),
|
||||||
|
).fetchone()
|
||||||
|
if not task:
|
||||||
|
raise TaskError("ACCEPTANCE_TASK_NOT_FOUND", "验收任务不存在", 404)
|
||||||
|
task_data = _task_dict(conn, task)
|
||||||
|
rows = conn.execute(
|
||||||
|
"""SELECT a.keyword,a.credential_id,a.attempt_number,a.crawler_task_id,
|
||||||
|
a.result_json,a.completed_at
|
||||||
|
FROM acceptance_cookie_attempts a
|
||||||
|
WHERE a.task_id=? AND a.status='completed' AND a.result_json IS NOT NULL
|
||||||
|
ORDER BY a.keyword,a.id""",
|
||||||
|
(task["id"],),
|
||||||
|
).fetchall()
|
||||||
|
|
||||||
|
ai_results = []
|
||||||
|
for row in rows:
|
||||||
|
try:
|
||||||
|
result = json.loads(row["result_json"])
|
||||||
|
except (TypeError, json.JSONDecodeError):
|
||||||
|
continue
|
||||||
|
ai_results.append({
|
||||||
|
"keyword": row["keyword"],
|
||||||
|
"credential_id": row["credential_id"],
|
||||||
|
"attempt_number": row["attempt_number"],
|
||||||
|
"crawler_task_id": row["crawler_task_id"],
|
||||||
|
"completed_at": row["completed_at"],
|
||||||
|
"result": result,
|
||||||
|
})
|
||||||
|
|
||||||
|
comparisons = []
|
||||||
|
for keyword in task_data["keywords"]:
|
||||||
|
try:
|
||||||
|
comparisons.append({
|
||||||
|
"keyword": keyword,
|
||||||
|
"status": "completed",
|
||||||
|
"data": match_feishu_notes(keyword),
|
||||||
|
})
|
||||||
|
except Exception as exc:
|
||||||
|
comparisons.append({
|
||||||
|
"keyword": keyword,
|
||||||
|
"status": "failed",
|
||||||
|
"error": str(exc),
|
||||||
|
"data": None,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"task": task_data,
|
||||||
|
"notes": list_acceptance_notes(public_id),
|
||||||
|
"ai_results": {"items": ai_results, "total": len(ai_results)},
|
||||||
|
"note_matches": {"items": comparisons, "total": len(comparisons)},
|
||||||
|
"data_scope": "仅包含当前已完成并已入库的数据",
|
||||||
|
}
|
||||||
|
|||||||
8
app.py
8
app.py
@@ -18,6 +18,7 @@ from database import init_database
|
|||||||
from acceptance_service import (
|
from acceptance_service import (
|
||||||
create_acceptance_task,
|
create_acceptance_task,
|
||||||
get_acceptance_resources,
|
get_acceptance_resources,
|
||||||
|
get_acceptance_link_ai_data,
|
||||||
get_acceptance_task,
|
get_acceptance_task,
|
||||||
list_acceptance_notes,
|
list_acceptance_notes,
|
||||||
list_acceptance_tasks,
|
list_acceptance_tasks,
|
||||||
@@ -286,6 +287,13 @@ def create_app() -> Flask:
|
|||||||
def get_project_acceptance_notes(task_id: str):
|
def get_project_acceptance_notes(task_id: str):
|
||||||
return jsonify({"data": list_acceptance_notes(task_id), "request_id": g.request_id})
|
return jsonify({"data": list_acceptance_notes(task_id), "request_id": g.request_id})
|
||||||
|
|
||||||
|
@app.route("/api/v1/acceptance-tasks/<task_id>/link-ai-data", methods=["GET"])
|
||||||
|
def get_project_acceptance_link_ai_data(task_id: str):
|
||||||
|
return jsonify({
|
||||||
|
"data": get_acceptance_link_ai_data(task_id),
|
||||||
|
"request_id": g.request_id,
|
||||||
|
})
|
||||||
|
|
||||||
@app.route("/api/v1/acceptance-tasks/<task_id>/resume", methods=["POST"])
|
@app.route("/api/v1/acceptance-tasks/<task_id>/resume", methods=["POST"])
|
||||||
def resume_project_acceptance_task(task_id: str):
|
def resume_project_acceptance_task(task_id: str):
|
||||||
return jsonify({"data": resume_acceptance_task(task_id), "request_id": g.request_id})
|
return jsonify({"data": resume_acceptance_task(task_id), "request_id": g.request_id})
|
||||||
|
|||||||
@@ -338,6 +338,15 @@ def collect_keyword_sources(keyword: str, credential_id: str | None = None) -> d
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def match_feishu_notes(keyword: str, task_limit: int = 100) -> dict[str, Any]:
|
||||||
|
"""调用爬虫服务,比对飞书笔记和问一问采集来源。"""
|
||||||
|
return _post(
|
||||||
|
"/api/v1/xhs/feishu-notes/matches",
|
||||||
|
{"keyword": keyword, "task_limit": task_limit},
|
||||||
|
retry_transient=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def resume_collection(crawler_task_id: str) -> dict[str, Any]:
|
def resume_collection(crawler_task_id: str) -> dict[str, Any]:
|
||||||
"""沿用原爬虫任务 ID,从 framework 保存的检查点继续采集。"""
|
"""沿用原爬虫任务 ID,从 framework 保存的检查点继续采集。"""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user