Files

152 lines
9.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""预存「力量态势」演示任务详情(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)))