diff --git a/android/app/src/main/java/com/hyzq/boss/MasterAgentEvolutionActivity.java b/android/app/src/main/java/com/hyzq/boss/MasterAgentEvolutionActivity.java index 5bef503..3579120 100644 --- a/android/app/src/main/java/com/hyzq/boss/MasterAgentEvolutionActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/MasterAgentEvolutionActivity.java @@ -17,6 +17,8 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity { private long lastRealtimeReloadAt; private boolean contentLoaded; private @Nullable String currentMode; + private @Nullable String statusMessage; + private boolean statusIsError; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -57,12 +59,16 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity { if (!response.ok()) { throw new IllegalStateException(response.message()); } - runOnUiThread(() -> renderDashboard(response.json)); + runOnUiThread(() -> { + clearStatusMessage(); + renderDashboard(response.json); + }); } catch (Exception error) { runOnUiThread(() -> { setRefreshing(false); contentLoaded = false; - replaceContent(BossUi.buildEmptyCard(this, "自动进化加载失败:" + error.getMessage())); + showStatusMessage("自动进化加载失败:" + error.getMessage(), true); + renderLoadErrorState(error.getMessage()); }); } }); @@ -119,6 +125,7 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity { "autonomous".equals(currentMode) ? "完全自我进化" : "受控自动进化", autoApplyLowRiskRules ? "低风险提案会自动采纳。" : "所有提案都需要人工确认。" )); + maybeRenderStatusBanner(); Button controlledButton = BossUi.buildMiniActionButton(this, "切到受控模式", false); controlledButton.setEnabled(!"controlled".equals(currentMode)); @@ -181,7 +188,7 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity { proposal.optString("summary", "暂无摘要"), proposal.optString("proposalType", "-") + " · " + proposal.optString("riskLevel", "-") - + " · " + proposal.optString("createdAt", "-"), + + " · " + formatTime(proposal.optString("createdAt", "-")), null, null )); @@ -208,7 +215,7 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity { this, signal.optString("kind", "signal"), signal.optString("requestText", ""), - signal.optString("createdAt", "-"), + formatTime(signal.optString("createdAt", "-")), null, null )); @@ -227,7 +234,7 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity { this, rule.optString("ruleType", "rule"), rule.optString("sourceProposalId", "直接创建"), - rule.optString("createdAt", "-"), + formatTime(rule.optString("createdAt", "-")), null, null )); @@ -261,14 +268,14 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity { throw new IllegalStateException(response.message()); } runOnUiThread(() -> { - showMessage("已切到 " + ("autonomous".equals(mode) ? "完全自我进化" : "受控自动进化")); + showStatusMessage("已切到 " + ("autonomous".equals(mode) ? "完全自我进化" : "受控自动进化"), false); setResult(RESULT_OK); reload(); }); } catch (Exception error) { runOnUiThread(() -> { setRefreshing(false); - showMessage("切换失败:" + error.getMessage()); + showStatusMessage("切换失败:" + error.getMessage(), true); }); } }); @@ -289,16 +296,73 @@ public class MasterAgentEvolutionActivity extends BossScreenActivity { throw new IllegalStateException(response.message()); } runOnUiThread(() -> { - showMessage(approve ? "提案已批准" : "提案已拒绝"); + showStatusMessage(approve ? "提案已批准" : "提案已拒绝", false); setResult(RESULT_OK); reload(); }); } catch (Exception error) { runOnUiThread(() -> { setRefreshing(false); - showMessage((approve ? "批准失败:" : "拒绝失败:") + error.getMessage()); + showStatusMessage((approve ? "批准失败:" : "拒绝失败:") + error.getMessage(), true); }); } }); } + + private void maybeRenderStatusBanner() { + if (statusMessage == null || statusMessage.isEmpty()) { + return; + } + contentRoot.addView(BossUi.buildSoftPanel( + this, + statusIsError ? "最近状态" : "最近操作", + statusMessage, + statusIsError ? "你可以直接点顶部刷新重试,或继续切换模式/审批提案。" : "本页已经同步到最新自动进化状态。" + )); + } + + private void renderLoadErrorState(String message) { + replaceContent(contentRoot); + contentRoot.removeAllViews(); + contentRoot.addView(BossUi.buildSimpleProfileHeader( + this, + "主 Agent 自动进化", + "信号、提案与生效规则", + "当前加载失败,保留在这个页面直接重试即可。" + )); + maybeRenderStatusBanner(); + Button retryButton = BossUi.buildMiniActionButton(this, "重新加载", true); + retryButton.setOnClickListener(v -> reload()); + contentRoot.addView(BossUi.buildInlineActionRow(this, retryButton)); + contentRoot.addView(BossUi.buildEmptyCard(this, "自动进化中心暂时不可用:" + message)); + } + + private void showStatusMessage(String message, boolean isError) { + statusMessage = message; + statusIsError = isError; + showMessage(message); + } + + private void clearStatusMessage() { + if (!statusIsError) { + statusMessage = null; + } + statusIsError = false; + } + + private String formatTime(String value) { + if (value == null || value.isEmpty() || "-".equals(value)) { + return "-"; + } + String normalized = value.replace('T', ' '); + int plusIndex = normalized.indexOf('+'); + if (plusIndex > 0) { + return normalized.substring(0, plusIndex); + } + int zIndex = normalized.indexOf('Z'); + if (zIndex > 0) { + return normalized.substring(0, zIndex); + } + return normalized; + } } diff --git a/android/app/src/test/java/com/hyzq/boss/MasterAgentEvolutionActivityTest.java b/android/app/src/test/java/com/hyzq/boss/MasterAgentEvolutionActivityTest.java index 83fa0de..570b504 100644 --- a/android/app/src/test/java/com/hyzq/boss/MasterAgentEvolutionActivityTest.java +++ b/android/app/src/test/java/com/hyzq/boss/MasterAgentEvolutionActivityTest.java @@ -91,6 +91,68 @@ public class MasterAgentEvolutionActivityTest { assertEquals(1, activity.reloadCount); } + @Test + public void renderDashboardShowsStatusBannerAndFormattedTimes() throws Exception { + TestMasterAgentEvolutionActivity activity = Robolectric + .buildActivity(TestMasterAgentEvolutionActivity.class, new Intent()) + .setup() + .get(); + + ReflectionHelpers.setField(activity, "statusMessage", "提案已批准"); + ReflectionHelpers.setField(activity, "statusIsError", false); + + JSONObject payload = new JSONObject() + .put("config", new JSONObject() + .put("mode", "controlled") + .put("autoApplyLowRiskRules", false)) + .put("signals", new JSONArray() + .put(new JSONObject() + .put("signalId", "signal-1") + .put("kind", "backend_fallback") + .put("requestText", "主节点暂时不可用") + .put("createdAt", "2026-04-16T12:00:00+08:00"))) + .put("proposals", new JSONArray()) + .put("rules", new JSONArray() + .put(new JSONObject() + .put("ruleId", "rule-1") + .put("ruleType", "fast_path_rule") + .put("createdAt", "2026-04-16T12:02:00+08:00"))); + + ReflectionHelpers.callInstanceMethod( + activity, + "renderDashboard", + ReflectionHelpers.ClassParameter.from(JSONObject.class, payload) + ); + + View content = activity.findViewById(R.id.screen_content); + assertTrue(viewTreeContainsText(content, "最近操作")); + assertTrue(viewTreeContainsText(content, "提案已批准")); + assertTrue(viewTreeContainsText(content, "2026-04-16 12:00:00")); + assertTrue(viewTreeContainsText(content, "2026-04-16 12:02:00")); + } + + @Test + public void renderLoadErrorStateKeepsRetryEntryVisible() throws Exception { + TestMasterAgentEvolutionActivity activity = Robolectric + .buildActivity(TestMasterAgentEvolutionActivity.class, new Intent()) + .setup() + .get(); + + ReflectionHelpers.setField(activity, "statusMessage", "自动进化加载失败:network down"); + ReflectionHelpers.setField(activity, "statusIsError", true); + ReflectionHelpers.callInstanceMethod( + activity, + "renderLoadErrorState", + ReflectionHelpers.ClassParameter.from(String.class, "network down") + ); + + View content = activity.findViewById(R.id.screen_content); + assertTrue(viewTreeContainsText(content, "当前加载失败,保留在这个页面直接重试即可。")); + assertTrue(viewTreeContainsText(content, "重新加载")); + assertTrue(viewTreeContainsText(content, "自动进化中心暂时不可用:network down")); + assertTrue(viewTreeContainsText(content, "最近状态")); + } + private static boolean viewTreeContainsText(View root, String expectedText) { if (root instanceof TextView) { CharSequence text = ((TextView) root).getText();