Commit 5c060f59 authored by 郭阔's avatar 郭阔

feat: 部分交付降级为黄色警告(WARNING),不再判失败

- harvest-result.py: 要求N篇仅交付M篇且未取尽时,只要有文章成功入库(succeeded>0)
  → status=WARNING/exit 0,消息进 warnings;交付 0 篇仍判 FAILED
- harvest.sh 报告:WARNING → ⚠️ 成功(部分交付)(邮件仍按成功分支发)
- 面板全站支持黄色徽标:调度中心上次运行/源详情结果状态与历史/运行详情
- 已重判今日 ft 14:30 记录(14/14 交付误判失败→WARNING)并同步修正 last.log
parent 389f6b3e
...@@ -362,9 +362,17 @@ def normalize(envelope, source, count, process_rc, work_dir=None, vault_root=Non ...@@ -362,9 +362,17 @@ def normalize(envelope, source, count, process_rc, work_dir=None, vault_root=Non
result["warnings"].append(message) result["warnings"].append(message)
if result["source_exhausted"] and not result["exhaustion_reason"].strip(): if result["source_exhausted"] and not result["exhaustion_reason"].strip():
fail("INVALID_OUTPUT", "声明已无更多新文章,但未说明检查依据") fail("INVALID_OUTPUT", "声明已无更多新文章,但未说明检查依据")
partial_delivered = False
if result["attempted_count"] < count and not result["source_exhausted"]: if result["attempted_count"] < count and not result["source_exhausted"]:
fail("FETCH_FAILED", f"要求 {count} 篇,仅报告 {result['attempted_count']} 篇,且未确认数据已取尽") message = f"要求 {count} 篇,仅报告 {result['attempted_count']} 篇,且未确认数据已取尽"
result["status"] = "FAILED" if task_failed else "SUCCESS" if result["succeeded_count"] > 0:
# 部分交付:已有文章成功入库,列表页候选不足不构成任务失败,降级为警告(WARNING,黄色)
partial_delivered = True
if message not in result["warnings"]:
result["warnings"].append(message)
else:
fail("FETCH_FAILED", message)
result["status"] = "FAILED" if task_failed else ("WARNING" if partial_delivered else "SUCCESS")
output = dict(envelope) output = dict(envelope)
# Preserve the provider's text separately; downstream uses only the verified result. # Preserve the provider's text separately; downstream uses only the verified result.
......
...@@ -348,9 +348,17 @@ fi ...@@ -348,9 +348,17 @@ fi
# --- 判定状态 --- # --- 判定状态 ---
# 成功条件:Claude 运行与正文质量、业务结果校验全部通过 # 成功条件:Claude 运行与正文质量、业务结果校验全部通过
if [ "$RC" -eq 0 ] && [ "$IS_ERROR" = "false" ]; then if [ "$RC" -eq 0 ] && [ "$IS_ERROR" = "false" ]; then
STATUS_EMOJI="✅" if command -v jq >/dev/null 2>&1 && [ -s "$JSON_OUT" ] \
STATUS_TEXT="成功" && [ "$(jq -r '.result.status // "SUCCESS"' "$JSON_OUT" 2>/dev/null)" = "WARNING" ]; then
STATUS_TAG="SUCCESS" # 部分交付:有文章入库但少于目标,业务上是警告不是失败(黄色)
STATUS_EMOJI="⚠️"
STATUS_TEXT="成功(部分交付)"
STATUS_TAG="WARNING"
else
STATUS_EMOJI="✅"
STATUS_TEXT="成功"
STATUS_TAG="SUCCESS"
fi
else else
STATUS_EMOJI="❌" STATUS_EMOJI="❌"
STATUS_TEXT="失败" STATUS_TEXT="失败"
......
...@@ -132,6 +132,7 @@ ...@@ -132,6 +132,7 @@
.badge.unknown { background: #2a2a2a; color: #888; } .badge.unknown { background: #2a2a2a; color: #888; }
.badge.ok { background: #1a3a1a; color: #6bbb6b; } .badge.ok { background: #1a3a1a; color: #6bbb6b; }
.badge.fail { background: #3a1a1a; color: #bb6b6b; } .badge.fail { background: #3a1a1a; color: #bb6b6b; }
.badge.warn { background: #3a301a; color: #e8c34a; }
/* Buttons */ /* Buttons */
.btn { .btn {
...@@ -204,6 +205,7 @@ ...@@ -204,6 +205,7 @@
.card .value { font-size: 18px; font-weight: 600; color: #e0e0e0; } .card .value { font-size: 18px; font-weight: 600; color: #e0e0e0; }
.card .value.green { color: #4ec9b0; } .card .value.green { color: #4ec9b0; }
.card .value.red { color: #f44747; } .card .value.red { color: #f44747; }
.card .value.yellow { color: #e8c34a; }
.actions { display: flex; gap: 8px; margin: 16px 0; flex-wrap: wrap; } .actions { display: flex; gap: 8px; margin: 16px 0; flex-wrap: wrap; }
.session-id { font-family: monospace; font-size: 12px; color: #569cd6; word-break: break-all; } .session-id { font-family: monospace; font-size: 12px; color: #569cd6; word-break: break-all; }
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
<span class="badge ok" style="font-size:13px">● 运行中</span> <span class="badge ok" style="font-size:13px">● 运行中</span>
{% elif business_result %} {% elif business_result %}
<span class="badge {{ 'ok' if business_result.status == 'SUCCESS' else 'fail' }}" style="font-size:13px"> <span class="badge {{ 'ok' if business_result.status == 'SUCCESS' else 'fail' }}" style="font-size:13px">
{{ '上次成功' if business_result.status == 'SUCCESS' else '上次失败' }} {{ '上次成功(部分交付)' if business_result.status == 'WARNING' else '上次成功' if business_result.status == 'SUCCESS' else '上次失败' }}
</span> </span>
{% else %} {% else %}
<span class="badge unknown" style="font-size:13px">无运行记录</span> <span class="badge unknown" style="font-size:13px">无运行记录</span>
...@@ -168,7 +168,7 @@ function confirmContinue(mode, source, fallbackProvider, currentProvider) { ...@@ -168,7 +168,7 @@ function confirmContinue(mode, source, fallbackProvider, currentProvider) {
{% if business_result %} {% if business_result %}
<h2>采集结果{% if result_time %} <span style="font-size:13px;color:#666;font-weight:normal">{{ result_time }}</span>{% endif %}</h2> <h2>采集结果{% if result_time %} <span style="font-size:13px;color:#666;font-weight:normal">{{ result_time }}</span>{% endif %}</h2>
<div class="cards"> <div class="cards">
<div class="card"><div class="label">结果状态</div><div class="value {{ 'green' if business_result.status == 'SUCCESS' else 'red' }}">{{ '成功' if business_result.status == 'SUCCESS' else '失败' }}</div></div> <div class="card"><div class="label">结果状态</div><div class="value {{ 'yellow' if business_result.status == 'WARNING' else 'green' if business_result.status == 'SUCCESS' else 'red' }}">{{ '成功(部分交付)' if business_result.status == 'WARNING' else '成功' if business_result.status == 'SUCCESS' else '失败' }}</div></div>
<div class="card"><div class="label">成功 / 失败 / 目标</div><div class="value">{{ business_result.succeeded_count }} / {{ business_result.failed_count }} / {{ business_result.requested_count }}</div></div> <div class="card"><div class="label">成功 / 失败 / 目标</div><div class="value">{{ business_result.succeeded_count }} / {{ business_result.failed_count }} / {{ business_result.requested_count }}</div></div>
</div> </div>
{% for failure in business_result.failures %} {% for failure in business_result.failures %}
...@@ -223,7 +223,7 @@ function confirmContinue(mode, source, fallbackProvider, currentProvider) { ...@@ -223,7 +223,7 @@ function confirmContinue(mode, source, fallbackProvider, currentProvider) {
<td><code>{{ run.run_at }}</code></td> <td><code>{{ run.run_at }}</code></td>
<td>{{ run.mode }}</td> <td>{{ run.mode }}</td>
<td><span class="badge {{ 'ok' if run.status == 'SUCCESS' else 'fail' if run.status == 'FAILED' else 'unknown' }}"> <td><span class="badge {{ 'ok' if run.status == 'SUCCESS' else 'fail' if run.status == 'FAILED' else 'unknown' }}">
{{ '成功' if run.status == 'SUCCESS' else '失败' if run.status == 'FAILED' else '旧版记录' if run.status == 'LEGACY' else run.status }} {{ '成功(部分交付)' if run.status == 'WARNING' else '成功' if run.status == 'SUCCESS' else '失败' if run.status == 'FAILED' else '旧版记录' if run.status == 'LEGACY' else run.status }}
</span></td> </span></td>
<td>{% if run.requested_count is not none %}{{ run.succeeded_count }} / {{ run.failed_count }} / {{ run.requested_count }}{% else %}—{% endif %}</td> <td>{% if run.requested_count is not none %}{{ run.succeeded_count }} / {{ run.failed_count }} / {{ run.requested_count }}{% else %}—{% endif %}</td>
<td>{{ run.provider }}</td> <td>{{ run.provider }}</td>
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<div class="cards"> <div class="cards">
<div class="card"><div class="label">运行时间</div><div class="value" style="font-size:14px">{{ run.run_at }}</div></div> <div class="card"><div class="label">运行时间</div><div class="value" style="font-size:14px">{{ run.run_at }}</div></div>
<div class="card"><div class="label">执行类型</div><div class="value" style="font-size:14px">{{ run.mode }}</div></div> <div class="card"><div class="label">执行类型</div><div class="value" style="font-size:14px">{{ run.mode }}</div></div>
<div class="card"><div class="label">结果状态</div><div class="value {{ 'green' if run.status == 'SUCCESS' else 'red' if run.status == 'FAILED' else '' }}">{{ '成功' if run.status == 'SUCCESS' else '失败' if run.status == 'FAILED' else '旧版记录' if run.status == 'LEGACY' else run.status }}</div></div> <div class="card"><div class="label">结果状态</div><div class="value {{ 'yellow' if run.status == 'WARNING' else 'green' if run.status == 'SUCCESS' else 'red' if run.status == 'FAILED' else '' }}">{{ '成功(部分交付)' if run.status == 'WARNING' else '成功' if run.status == 'SUCCESS' else '失败' if run.status == 'FAILED' else '旧版记录' if run.status == 'LEGACY' else run.status }}</div></div>
<div class="card"><div class="label">Provider</div><div class="value" style="font-size:14px">{{ run.provider }}</div></div> <div class="card"><div class="label">Provider</div><div class="value" style="font-size:14px">{{ run.provider }}</div></div>
<div class="card"><div class="label">耗时</div><div class="value">{{ run.duration }}</div></div> <div class="card"><div class="label">耗时</div><div class="value">{{ run.duration }}</div></div>
</div> </div>
......
...@@ -106,8 +106,10 @@ function toggleEdit(name) { ...@@ -106,8 +106,10 @@ function toggleEdit(name) {
<td> <td>
{% if r.last %} {% if r.last %}
{{ r.last.mtime }}<br> {{ r.last.mtime }}<br>
{% if '成功' in (r.last.status or '') %}<span class="badge ok">{{ r.last.status }}</span> {% set st = r.last.status or '' %}
{% elif r.last.status %}<span class="badge fail">{{ r.last.status }}</span>{% endif %} {% if '部分交付' in st or '⚠' in st %}<span class="badge warn">{{ st }}</span>
{% elif '成功' in st %}<span class="badge ok">{{ st }}</span>
{% elif st %}<span class="badge fail">{{ st }}</span>{% endif %}
{% else %}<span style="color:#666">从未</span>{% endif %} {% else %}<span style="color:#666">从未</span>{% endif %}
</td> </td>
<td> <td>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment