v1.3.0: 每源独立采集周期 + 定制监控与新闻监控分离(独立配置/独立汇总) + 历史采样留档与提取API

This commit is contained in:
2026-08-29 19:29:28 +08:00
parent 2cad7a2976
commit de08530958
12 changed files with 489 additions and 88 deletions
+71 -3
View File
@@ -117,7 +117,8 @@ def api_sources():
sid = db.add_source(data.get("name", ""), data.get("type", ""), data.get("url", ""),
data.get("description", ""), float(data.get("weight", 1.0)),
kind=data.get("kind", "normal"),
monitor_standard=data.get("monitor_standard", ""))
monitor_standard=data.get("monitor_standard", ""),
scan_interval_min=int(data.get("scan_interval_min", 0) or 0))
return jsonify({"ok": True, "id": sid})
if action == "update":
db.update_source(data["id"], name=data.get("name"), type=data.get("type"),
@@ -125,6 +126,7 @@ def api_sources():
weight=float(data.get("weight", 1.0)),
kind=data.get("kind", "normal"),
monitor_standard=data.get("monitor_standard", ""),
scan_interval_min=int(data.get("scan_interval_min", 0) or 0),
enabled=1 if data.get("enabled") else 0)
return jsonify({"ok": True})
if action == "delete":
@@ -158,6 +160,60 @@ def api_sources():
return jsonify({"ok": False, "error": "unknown action"})
@app.route("/api/sources/history")
def api_source_history():
"""数据源历史采样记录(供页面查看 / 自动流程提取)
GET /api/sources/history?source_id=1&limit=50
返回: {source: {...}, snapshots: [{id,fetched_at,count,status,detail}], total, limit}
"""
source_id = request.args.get("source_id", type=int)
if not source_id:
return jsonify({"ok": False, "error": "source_id 必填"})
s = db.get_source(source_id)
if not s:
return jsonify({"ok": False, "error": "数据源不存在"})
limit = min(500, request.args.get("limit", 50, type=int))
snaps = db.list_source_snapshots(source_id, limit=limit)
return jsonify({"ok": True, "source": s, "snapshots": snaps, "total": len(snaps), "limit": limit})
@app.route("/api/sources/articles")
def api_source_articles():
"""数据源历史采集到的资讯(自动流程提取用)
GET /api/sources/articles?source_id=1&page=1&page_size=20&q=关键词
返回: {ok, source, articles: [...], page, page_size, total}
"""
source_id = request.args.get("source_id", type=int)
if not source_id:
return jsonify({"ok": False, "error": "source_id 必填"})
s = db.get_source(source_id)
if not s:
return jsonify({"ok": False, "error": "数据源不存在"})
page = max(1, request.args.get("page", 1, type=int))
page_size = min(100, max(1, request.args.get("page_size", 20, type=int)))
q = request.args.get("q", "")
conn = db.get_conn()
where, args = "a.source_id=?", [source_id]
if q:
where += " AND (a.title LIKE ? OR a.content LIKE ? OR a.summary LIKE ?)"
args += [f"%{q}%", f"%{q}%", f"%{q}%"]
total = conn.execute(f"SELECT COUNT(*) c FROM articles a WHERE {where}", args).fetchone()["c"]
rows = conn.execute(
f"SELECT a.* FROM articles a WHERE {where} ORDER BY a.collected_at DESC, a.id DESC "
f"LIMIT ? OFFSET ?", args + [page_size, (page - 1) * page_size]).fetchall()
conn.close()
out = []
for r in rows:
d = dict(r)
try:
d["entities"] = json.loads(d["entities"] or "[]")
except Exception:
d["entities"] = []
out.append(d)
return jsonify({"ok": True, "source": s, "articles": out, "page": page,
"page_size": page_size, "total": total})
@app.route("/api/profile", methods=["POST"])
def api_profile():
data = request.get_json(force=True) or {}
@@ -197,6 +253,10 @@ def api_settings():
cur = db.get_all_settings().get("mail", {})
cur.update(data["mail"])
db.set_setting("mail", cur)
if "custom" in data and isinstance(data["custom"], dict):
cur = db.get_all_settings().get("custom", {})
cur.update(data["custom"])
db.set_setting("custom", cur)
return jsonify({"ok": True})
@@ -268,7 +328,7 @@ def api_actions():
data = request.get_json(force=True) or {}
action = data.get("action")
if action == "collect":
n = scheduler.collect_once()
n = scheduler.collect_once(force=True)
return jsonify({"ok": True, "added": n})
if action == "llm":
r = analysis.batch_llm_analyze(limit=int(data.get("limit", 10)))
@@ -276,6 +336,9 @@ def api_actions():
if action == "summary":
n = scheduler.send_daily_summary()
return jsonify({"ok": True, "sent": n})
if action == "custom_summary":
n = scheduler.send_custom_summary()
return jsonify({"ok": True, "sent": n})
if action == "seed":
n = simulate.seed_all()
return jsonify({"ok": True, "added": n})
@@ -306,12 +369,16 @@ def not_found(e):
# ---------------- 启动 ----------------
def main():
db.init_db()
# 确保定制监控机制配置存在(老库升级)
if db.get_setting("custom") is None:
db.set_setting("custom", dict(config.CUSTOM_DEFAULTS))
# 首次初始化:写入默认数据源 / 兴趣画像 / 默认设置 / 模拟数据
if db.get_setting("initialized") != 1:
for s in config.DEFAULT_SOURCES:
db.add_source(s["name"], s["type"], s["url"], s["description"], s["weight"],
kind=s.get("kind", "normal"),
monitor_standard=s.get("monitor_standard", ""))
monitor_standard=s.get("monitor_standard", ""),
scan_interval_min=s.get("scan_interval_min", 0))
for kw, w in config.DEFAULT_KEYWORDS:
db.add_keyword(kw, w)
for d, w in config.DEFAULT_DOMAINS:
@@ -327,6 +394,7 @@ def main():
db.set_active_provider(pid)
db.set_setting("initialized", 1)
db.set_setting("auto", dict(config.AUTO_DEFAULTS))
db.set_setting("custom", dict(config.CUSTOM_DEFAULTS))
db.set_setting("mail", dict(config.MAIL_DEFAULTS))
simulate.seed_all()
analysis.run_llm_background()