Fix reminder resend, settings validation, and notice navigation.
Reuse resolved reminder rows instead of inserting duplicates, skip failed keys in batch send, reject invalid scan settings with 400, and bind 去处理 via event delegation after async render. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
f17636183d
commit
212b1f9b9b
@@ -73,26 +73,68 @@ def get_settings(connection: sqlite3.Connection) -> dict[str, str]:
|
||||
return settings
|
||||
|
||||
|
||||
def _valid_scan_time(value: str) -> bool:
|
||||
parts = value.strip().split(":")
|
||||
if len(parts) != 2 or not parts[0].isdigit() or not parts[1].isdigit():
|
||||
return False
|
||||
if len(parts[1]) != 2:
|
||||
return False
|
||||
hour, minute = int(parts[0]), int(parts[1])
|
||||
return 0 <= hour <= 23 and 0 <= minute <= 59
|
||||
|
||||
|
||||
def validate_setting(key: str, value: str) -> str:
|
||||
raw = str(value).strip()
|
||||
if key == SETTING_MONTHLY_START_DAY:
|
||||
try:
|
||||
day = int(raw)
|
||||
except ValueError as exc:
|
||||
raise ValueError("每月起始日须为 1 到 28 的整数。") from exc
|
||||
if day < 1 or day > 28:
|
||||
raise ValueError("每月起始日须为 1 到 28 的整数。")
|
||||
return str(day)
|
||||
if key == SETTING_GAP_DAYS:
|
||||
try:
|
||||
days = int(raw)
|
||||
except ValueError as exc:
|
||||
raise ValueError("断档天数须为大于等于 1 的整数。") from exc
|
||||
if days < 1:
|
||||
raise ValueError("断档天数须为大于等于 1 的整数。")
|
||||
return str(days)
|
||||
if key == SETTING_SCAN_TIME:
|
||||
if not _valid_scan_time(raw):
|
||||
raise ValueError("扫描时间须为合法的 HH:MM。")
|
||||
hour, minute = raw.split(":")
|
||||
return f"{int(hour):02d}:{minute}"
|
||||
raise ValueError(f"unknown setting: {key}")
|
||||
|
||||
|
||||
def update_settings(connection: sqlite3.Connection, updates: dict[str, str]) -> dict[str, str]:
|
||||
allowed = set(DEFAULT_SETTINGS)
|
||||
cleaned: dict[str, str] = {}
|
||||
for key, value in updates.items():
|
||||
if key not in allowed:
|
||||
raise ValueError(f"unknown setting: {key}")
|
||||
cleaned[key] = validate_setting(key, value)
|
||||
now = utc_now()
|
||||
with connection:
|
||||
for key, value in updates.items():
|
||||
if key not in allowed:
|
||||
raise ValueError(f"unknown setting: {key}")
|
||||
for key, value in cleaned.items():
|
||||
connection.execute(
|
||||
"""
|
||||
INSERT INTO reminder_settings (key, value, updated_at)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
|
||||
""",
|
||||
(key, str(value), now),
|
||||
(key, value, now),
|
||||
)
|
||||
return get_settings(connection)
|
||||
|
||||
|
||||
def _setting_int(settings: dict[str, str], key: str) -> int:
|
||||
return int(settings.get(key, DEFAULT_SETTINGS[key]))
|
||||
try:
|
||||
return int(validate_setting(key, settings.get(key, DEFAULT_SETTINGS[key])))
|
||||
except (TypeError, ValueError):
|
||||
return int(DEFAULT_SETTINGS[key])
|
||||
|
||||
|
||||
def _month_period(day: date | None = None) -> str:
|
||||
@@ -400,21 +442,33 @@ def _deliver(
|
||||
).fetchone()
|
||||
|
||||
with connection:
|
||||
if existing is not None and existing["status"] != "resolved":
|
||||
if existing is not None:
|
||||
reminder_id = int(existing["id"])
|
||||
send_count = int(existing["send_count"]) + 1
|
||||
connection.execute(
|
||||
"""
|
||||
UPDATE reminders
|
||||
SET send_count = ?, last_sent_at = ?, status = 'open'
|
||||
SET send_count = ?, last_sent_at = ?, status = 'open',
|
||||
title = ?, content = ?, rule_params = ?, action_link = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(send_count, now, reminder_id),
|
||||
(
|
||||
send_count,
|
||||
now,
|
||||
finding.title,
|
||||
finding.content,
|
||||
json.dumps(finding.rule_params, ensure_ascii=False),
|
||||
finding.action_link,
|
||||
reminder_id,
|
||||
),
|
||||
)
|
||||
event_type = "sent" if existing["status"] == "resolved" else (
|
||||
"escalated" if send_count > 1 else "sent"
|
||||
)
|
||||
_append_event(
|
||||
connection,
|
||||
reminder_id,
|
||||
"escalated" if send_count > 1 else "sent",
|
||||
event_type,
|
||||
actor_ref,
|
||||
f"第 {send_count} 次催办",
|
||||
)
|
||||
@@ -465,7 +519,10 @@ def deliver_many(
|
||||
) -> list[int]:
|
||||
sent: list[int] = []
|
||||
for key in dedupe_keys:
|
||||
reminder_id = deliver_finding(connection, key, actor=actor, ip=ip)
|
||||
try:
|
||||
reminder_id = deliver_finding(connection, key, actor=actor, ip=ip)
|
||||
except Exception:
|
||||
continue
|
||||
if reminder_id is not None:
|
||||
sent.append(reminder_id)
|
||||
return sent
|
||||
|
||||
Reference in New Issue
Block a user