v1.3.0: 每源独立采集周期 + 定制监控与新闻监控分离(独立配置/独立汇总) + 历史采样留档与提取API
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user