fix: 修复时间戳继承、补分DB检查、OCR急停、SequenceMatcher作用域问题 (Task #1661-1663)

- Task #1661: OCR时间戳丢失时无时间戳消息继承前驱时间戳新鲜度
- Task #1662: 补分逻辑三重DB检查,DB不存在时跳过补分
- Task #1663: OCR readline循环添加_stop_event检查,支持急停
- 修复SequenceMatcher局部导入导致的UnboundLocalError
This commit is contained in:
youpaishidifu
2026-09-01 22:06:22 +08:00
parent eccc1a4247
commit 93a7d0be6c
+49 -9
View File
@@ -848,6 +848,20 @@ class MessageHistory:
finally:
conn.close()
def has_any_records_for_platform(self, platform: str) -> bool:
"""检查该平台在DB中是否有任何消息记录(用于补分逻辑判断)"""
self._ensure_ocr_staging_table()
conn = self._get_conn()
try:
cursor = conn.execute(
"SELECT COUNT(*) FROM ocr_staging WHERE platform=?",
(platform,)
)
count = cursor.fetchone()[0]
return count > 0
finally:
conn.close()
class MessageMonitor:
"""多线程消息监控器"""
@@ -2934,17 +2948,17 @@ class MessageMonitor:
pass
_t = threading.Thread(target=_read_worker, daemon=True)
_t.start()
# ⭐ 急停检查:轮询 _running,急停后立即放弃等待(丢弃未出结果的 OCR)
# ⭐ 急停检查:轮询 _running 和 _stop_event,急停后立即放弃等待(丢弃未出结果的 OCR)
_deadline = time.time() + timeout
line = None
while self._running and time.time() < _deadline:
while self._running and not self._stop_event.is_set() and time.time() < _deadline:
try:
line = _q.get(timeout=0.05)
break
except _QueueEmpty:
continue
if line is None:
if not self._running:
if not self._running or self._stop_event.is_set():
logger.debug("[ocr] EasyOCR 等待期间急停,丢弃未出结果的 OCR 请求")
else:
self._easyocr_consecutive_failures += 1
@@ -7379,14 +7393,17 @@ class MessageMonitor:
# 从文本中提取时间戳
timestamps = re.findall(r'\d{1,2}:\d{1,2}(?::\d{1,2})?', text)
if not timestamps:
return 0.0
# ⭐ Task #1661: 无时间戳返回特殊值-999.0,区分"无时间戳"与"有时间戳但较旧"
# 调用方据此判断:无时间戳→不补分、视为陈旧消息
return -999.0
# 取最后一个时间戳(通常是最新消息的时间戳)
ts_str = timestamps[-1]
age_seconds = self._timestamp_age_seconds(ts_str)
if age_seconds is None:
return 0.0
# 时间戳解析失败也视为无有效时间戳
return -999.0
# 根据时间差给分(越接近得分越高)
if age_seconds <= 300: # 5分钟内
@@ -9836,12 +9853,28 @@ class MessageMonitor:
new_items = []
# ⭐ 时间戳新鲜度评分:在关键词打分和查重之前计算,作为加分项
freshness_scores = {} # 存储每条消息的时间戳新鲜度分数
# ⭐ Task #1661: 维护上一条带合法时间戳消息的新鲜度,供无时间戳消息继承
# QQ/微信中用户连发多条消息时,仅首条带时间戳,后续紧邻消息无时间戳
# 无时间戳消息继承前驱时间戳新鲜度 → 同批次消息共享时间上下文,避免误处理旧消息
_last_ts_freshness = None # 上一条带合法时间戳消息的新鲜度分数
_last_ts_text = None # 上一条带合法时间戳消息的文本(仅日志用)
for t in texts:
ts = t.strip()
if not ts:
continue
# 计算时间戳新鲜度分数(作为后续综合得分的加分项)
freshness_score = self._timestamp_freshness_score(ts)
# ⭐ Task #1661: 无时间戳(-999.0) → 继承同批次紧邻前驱消息的时间戳新鲜度
if freshness_score == -999.0:
if _last_ts_freshness is not None:
logger.debug(f"[{platform}] 无时间戳消息继承前驱时间戳新鲜度 "
f"({_last_ts_text[:20]!r}→{ts[:20]!r}): {_last_ts_freshness:.1f}")
freshness_score = _last_ts_freshness
# else: 整批都无时间戳,保持-999.0(保守,不补分、扣10分过滤)
else:
# 有合法时间戳:更新前驱状态,供后续无时间戳消息继承
_last_ts_freshness = freshness_score
_last_ts_text = ts
# 存储原始文本和新鲜度分数
freshness_scores[ts] = freshness_score
# === URL-only 兜底:无中文且含 URL → 碎片 ===
@@ -10531,6 +10564,11 @@ class MessageMonitor:
old_score = score
score = max(score + 5.0, 5.0)
logger.info(f"[{platform}] 时间戳新鲜度≥5直接放行(+5): {old_score:.1f}→{score:.1f}: {item[:50]}")
elif freshness_score == -999.0:
# ⭐ Task #1661: 无有效时间戳(OCR丢失或解析失败)
# 不补分、直接扣分为负,避免旧消息因"DB无记录"误获补分
score -= 10.0
logger.info(f"[{platform}] 无有效时间戳(OCR丢失?)扣10分: {score:.1f}: {item[:50]}")
elif freshness_score < 0:
# 负分(超过1小时):扣10分
score += freshness_score # 加上负分 = 扣分
@@ -10540,11 +10578,14 @@ class MessageMonitor:
if self._msg_history and self._msg_history.has_ocr_message_record(platform, cleaned_item):
# DB有记录:不加分
logger.info(f"[{platform}] 时间戳新鲜度<5且DB有记录,不加分: {item[:50]}")
else:
# DB无记录:补分到5(确保最终分数至少为5,不因负基础分而被丢弃)
elif self._msg_history and self._msg_history.has_any_records_for_platform(platform):
# DB存在但该消息无记录:补分到5(确保最终分数至少为5,不因负基础分而被丢弃)
old_score = score
score = max(score + 5.0, 5.0)
logger.info(f"[{platform}] 时间戳新鲜度<5且DB无记录,补分到5: {old_score:.1f}→{score:.1f}: {item[:50]}")
else:
# ⭐ Task #1662: DB不存在或无记录时跳过,不加分(避免旧消息因补分逻辑被误处理)
logger.info(f"[{platform}] 时间戳新鲜度<5且DB无记录/不存在,跳过补分: {item[:50]}")
# ⭐ 检查分数是否>0:分数<=0的消息不应被处理
if score <= 0:
@@ -10601,8 +10642,7 @@ class MessageMonitor:
)
_is_same_round_dup = True
break
# 高相似度匹配
from difflib import SequenceMatcher
# 高相似度匹配(SequenceMatcher 已在文件头全局导入)
sim = SequenceMatcher(None, sk_cur, sk_prev).ratio()
if sim >= 0.7:
logger.info(