152 lines
9.6 KiB
Python
152 lines
9.6 KiB
Python
"""预存「力量态势」演示任务详情(force-situation,新类型任务 002)。
|
||
|
||
一个任务一份自由 JSON 文档(force_situation_details 表,结构不定死字段):
|
||
- 时间轴(importantTimeNodes 重要时间节点)+ 当前研判 + 重要目标 + 洞察区块;
|
||
- 任务衔接(taskConnections):任务字段与任务列表页对齐
|
||
(taskName/taskDirection/taskLevel/taskStatus/taskContent/sourceType/createTime),
|
||
另带 connectionType(进攻型/防御型/正面塑势)作为泳道分组。
|
||
|
||
用法(backend 目录):PYTHONPATH=. uv run python scripts/seed_force_situation_demo.py [--dry-run]
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import asyncio
|
||
import json
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
TASK_ID = "002"
|
||
_LOCATION_SCRIPTS = Path(__file__).resolve().parents[1] / "skills" / "custom" / "fs-location-info" / "scripts"
|
||
sys.path.insert(0, str(_LOCATION_SCRIPTS))
|
||
from fs_location_info import attach_coordinates # noqa: E402
|
||
|
||
def _connection_rows() -> list[dict]:
|
||
"""任务衔接演示行(字段与任务列表页对齐)。"""
|
||
rows = [
|
||
("T01", "进攻型", "核心客户竞对反制续约", "W方向", 1, 20, "针对竞对报价渗透,输出两家战略客户反制口径并推动续签。", 0, "2026-09-03 20:08:35"),
|
||
("T02", "进攻型", "华东核心商圈抢点铺货", "W方向", 2, 10, "江苏、浙江核心商圈新增网点 40 个,抢在竞对促销前完成布点。", 0, "2026-09-05 10:12:00"),
|
||
("T03", "进攻型", "话题热度集中投放", "E方向", 2, 25, "账号编组与媒资就绪后 48 小时内完成话题集中投放。", 1, "2026-09-06 15:40:11"),
|
||
("T04", "进攻型", "重点群体定向触达", "E方向", 2, 20, "面向关键决策人群体输出定向内容并跟踪触达效果。", 1, "2026-09-08 09:20:45"),
|
||
("T05", "防御型", "竞对挖角渠道防护", "W方向", 1, 20, "梳理核心经销商合作风险清单,逐户明确稳控责任人与话术。", 0, "2026-09-04 11:30:00"),
|
||
("T06", "防御型", "低效网点流失止损", "W方向", 3, 10, "对 30 家低效网点分批整改,明确止损退出标准。", 0, "2026-09-09 14:05:22"),
|
||
("T07", "防御型", "负面舆情稳态控制", "N方向", 1, 25, "7×24 监测渠道与社群负面声量,触发阈值即启动应急预案。", 1, "2026-09-02 08:00:00"),
|
||
("T08", "正面塑势", "品牌口碑正向传播", "E方向", 2, 25, "围绕客户成功案例组织系列正面传播,扩大口碑基本盘。", 1, "2026-09-07 16:44:30"),
|
||
("T09", "正面塑势", "战略客户成功案例放大", "E方向", 2, 20, "提炼 3 个战略客户标杆案例,形成多语种传播素材。", 1, "2026-09-10 10:55:00"),
|
||
("T10", "正面塑势", "经销商信心公开塑造", "W方向", 3, 10, "年中大会前发布渠道政策预告,稳定经销商预期。", 0, "2026-09-11 09:15:40"),
|
||
]
|
||
return [
|
||
{
|
||
"id": row[0],
|
||
"connectionType": row[1],
|
||
"taskName": row[2],
|
||
"taskDirection": row[3],
|
||
"taskLevel": row[4],
|
||
"taskStatus": row[5],
|
||
"taskContent": row[6],
|
||
"sourceType": row[7],
|
||
"createTime": row[8],
|
||
}
|
||
for row in rows
|
||
]
|
||
|
||
|
||
DETAIL: dict = {
|
||
# 当前判断(研判卡)
|
||
"currentJudgments": [
|
||
{"id": "J01", "content": "核心客户续约窗口正在收窄,竞对报价已进入两家战略客户决策层,若不在本周完成口径对齐,续约率可能跌破 90%。", "confidence": 86, "conclusion": "风险上升"},
|
||
{"id": "J02", "content": "华东铺货节奏总体符合计划,江苏、浙江核心商圈网点拓展仍可按既定节点推进,需盯紧安徽渠道落地质量。", "confidence": 78, "conclusion": "持续关注"},
|
||
{"id": "J03", "content": "经销商活跃度回升至近三个月高位,可借势优化低效网点并扩大覆盖,形成巩固渠道的窗口。", "confidence": 74, "conclusion": "存在战机"},
|
||
{"id": "J04", "content": "海外发布通道尚未就绪,重点信息触达存在 3–7 天滞后,可能拖慢对目标群体的及时影响。", "confidence": 81, "conclusion": "风险上升"},
|
||
{"id": "J05", "content": "账号编组与媒体生产能力已就绪,具备本周集中投放条件,可支撑话题热度与公开发布动作。", "confidence": 88, "conclusion": "存在战机"},
|
||
{"id": "J06", "content": "效果评估数据闭环仍不完整,重点群体沟通成效难以量化,暂不影响当前动作,但需持续补齐监测口径。", "confidence": 69, "conclusion": "持续关注"},
|
||
],
|
||
# 重要目标
|
||
"importantTargets": [
|
||
{"id": "G01", "name": "G01 核心客户续约目标", "description": "Q3 完成 15 家战略客户合同续签,续约率不低于 90%,锁定年度营收基本盘。"},
|
||
{"id": "G02", "name": "G02 华东区域铺货目标", "description": "新增终端网点 120 个,覆盖江苏、浙江、安徽核心商圈,Q3 末铺货完成率达到 70% 以上。"},
|
||
{"id": "G03", "name": "G03 经销商网络巩固", "description": "提升经销商活跃度至 75%,优化低效网点 30 家,防范竞对挖角导致核心渠道松动。"},
|
||
],
|
||
# 时间轴:重要时间节点
|
||
"importantTimeNodes": [
|
||
{"title": "G01 战略客户续约窗口开启", "content": "15 家战略客户进入合同窗口期,需在两周内完成商务条款对齐,锁定年度营收基本盘。", "topic": "核心续约", "date": "09-18"},
|
||
{"title": "华东铺货完成率中期考核", "content": "江苏、浙江核心商圈铺货进度复核,目标完成率 70%,安徽渠道落地质量同步盯紧。", "topic": "区域铺货", "date": "09-22"},
|
||
{"title": "全国经销商年中大会", "content": "集中通报渠道政策与返点节奏,稳定核心经销商合作意愿,防范竞对挖角。", "topic": "渠道巩固", "date": "09-28"},
|
||
{"title": "海外发布通道开通截止", "content": "重点信息触达通道需在此节点前就绪,避免 3–7 天滞后拖慢对目标群体的影响。", "topic": "发布通道", "date": "10-05"},
|
||
],
|
||
# 洞察区块
|
||
"insightTopics": [
|
||
{"title": "核心客户续约窗口收窄", "heat": 96},
|
||
{"title": "华东渠道竞对促销加码", "heat": 88},
|
||
{"title": "经销商利润承压讨论升温", "heat": 76},
|
||
{"title": "品牌口碑正面案例扩散", "heat": 64},
|
||
],
|
||
"insightRisks": [
|
||
{"description": "外部扩散风险", "severity": "高", "urgency": "高"},
|
||
{"description": "群体情绪升温", "severity": "高", "urgency": "中"},
|
||
{"description": "负面叙事形成", "severity": "中", "urgency": "高"},
|
||
{"description": "敌对势力借题发挥", "severity": "中", "urgency": "中"},
|
||
],
|
||
"insightOpportunities": [
|
||
{"description": "权威发声窗口", "value": "高", "timing": "紧迫"},
|
||
{"description": "议题澄清窗口", "value": "高", "timing": "中"},
|
||
{"description": "正面议题引导", "value": "中", "timing": "中"},
|
||
],
|
||
# 任务衔接:任务字段与任务列表页(/situation-overview/action/task)对齐;
|
||
# connectionType 为衔接类型(进攻型/防御型/正面塑势),taskStatus 用任务列表状态码
|
||
# (10=未开始 20=进行中 25=已完成分析 30=已结束)。不带坐标。
|
||
"taskConnections": _connection_rows(),
|
||
}
|
||
|
||
|
||
def _stamp_locations(detail: dict) -> dict:
|
||
"""除任务衔接外,按 fs-location-info 补 latitude/longitude。"""
|
||
for item in detail.get("currentJudgments") or []:
|
||
attach_coordinates(item, query=item.get("content", ""), item_id=item.get("id", ""))
|
||
for item in detail.get("importantTargets") or []:
|
||
attach_coordinates(item, query=item.get("name", ""), item_id=item.get("id", ""))
|
||
for index, item in enumerate(detail.get("importantTimeNodes") or [], start=1):
|
||
attach_coordinates(item, query=item.get("title", ""), item_id=f"node-{index}-{item.get('title', '')}")
|
||
for index, item in enumerate(detail.get("insightTopics") or [], start=1):
|
||
attach_coordinates(item, query=item.get("title", ""), item_id=f"topic-{index}")
|
||
for index, item in enumerate(detail.get("insightRisks") or [], start=1):
|
||
attach_coordinates(item, query=item.get("description", ""), item_id=f"risk-{index}")
|
||
for index, item in enumerate(detail.get("insightOpportunities") or [], start=1):
|
||
attach_coordinates(item, query=item.get("description", ""), item_id=f"opp-{index}")
|
||
return detail
|
||
|
||
|
||
DETAIL = _stamp_locations(DETAIL)
|
||
|
||
|
||
|
||
async def _run(dry_run: bool) -> int:
|
||
from deerflow.config import get_app_config
|
||
from deerflow.persistence.engine import close_engine, get_session_factory, init_engine_from_config
|
||
from deerflow.persistence.force_situation import make_force_situation_store
|
||
|
||
if dry_run:
|
||
print(json.dumps(DETAIL, ensure_ascii=False, indent=2)[:2000])
|
||
print("…(dry-run,未写库)")
|
||
return 0
|
||
|
||
config = get_app_config()
|
||
await init_engine_from_config(config.database)
|
||
sf = get_session_factory()
|
||
store = make_force_situation_store(sf)
|
||
try:
|
||
record = await store.upsert_detail(TASK_ID, DETAIL, updated_by="seed")
|
||
print(f"已预存任务 {TASK_ID} 详情:keys={sorted(record['detail'].keys())}")
|
||
print(f"updated_at={record.get('updated_at')}")
|
||
finally:
|
||
await close_engine()
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
parser = argparse.ArgumentParser()
|
||
parser.add_argument("--dry-run", action="store_true")
|
||
args = parser.parse_args()
|
||
sys.exit(asyncio.run(_run(args.dry_run)))
|