package com.hyzq.boss; import android.os.Bundle; import android.widget.Button; import android.widget.LinearLayout; import androidx.annotation.Nullable; import org.json.JSONArray; import org.json.JSONObject; import java.util.LinkedHashMap; import java.util.Map; public class OpsCenterActivity extends BossScreenActivity { private static final long REALTIME_RELOAD_THROTTLE_MS = 900L; private LinearLayout contentRoot; private @Nullable BossRealtimeClient realtimeClient; private final Map recentRealtimeEventTimestamps = new LinkedHashMap<>(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); configureScreen("运维与修复", "运维会话、修复回放与 standby 切换"); setHeaderAction("刷新", v -> reload()); contentRoot = new LinearLayout(this); contentRoot.setOrientation(LinearLayout.VERTICAL); replaceContent(contentRoot); realtimeClient = new BossRealtimeClient(apiClient, this::handleRealtimeEvent); reload(); } @Override protected void onResume() { super.onResume(); updateRealtimeSubscription(); } @Override protected void onPause() { stopRealtimeUpdates(); super.onPause(); } @Override protected void onDestroy() { stopRealtimeUpdates(); super.onDestroy(); } @Override protected void reload() { setRefreshing(true); executor.execute(() -> { try { BossApiClient.ApiResponse ops = apiClient.getOpsSummary(); if (!ops.ok()) { throw new IllegalStateException("OPS_LOAD_FAILED"); } runOnUiThread(() -> render(ops.json)); } catch (Exception error) { runOnUiThread(() -> { setRefreshing(false); replaceContent(BossUi.buildEmptyCard(this, "运维与修复加载失败:" + error.getMessage())); }); } }); } private void updateRealtimeSubscription() { if (apiClient != null && apiClient.hasSessionHints() && realtimeClient != null) { realtimeClient.start(); return; } stopRealtimeUpdates(); } private void stopRealtimeUpdates() { if (realtimeClient != null) { realtimeClient.stop(); } } void handleRealtimeEvent(BossRealtimeEvent event) { if (event == null || event.eventName.isEmpty()) { return; } if (!shouldReloadForRealtimeEvent(event)) { return; } String eventFingerprint = BossRealtimeClient.buildEventFingerprint(event); if (eventFingerprint.isEmpty()) { return; } long now = System.currentTimeMillis(); if (isDuplicateRealtimeEvent(eventFingerprint, now)) { return; } runOnUiThread(this::reload); } private boolean shouldReloadForRealtimeEvent(BossRealtimeEvent event) { return "app.logs.updated".equals(event.eventName) || "project.context_risk.updated".equals(event.eventName); } private boolean isDuplicateRealtimeEvent(String eventFingerprint, long now) { pruneRecentRealtimeEvents(now); Long previousEventAt = recentRealtimeEventTimestamps.get(eventFingerprint); if (previousEventAt != null && now - previousEventAt < REALTIME_RELOAD_THROTTLE_MS) { return true; } recentRealtimeEventTimestamps.put(eventFingerprint, now); return false; } private void pruneRecentRealtimeEvents(long now) { java.util.Iterator> iterator = recentRealtimeEventTimestamps.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); if (now - entry.getValue() >= REALTIME_RELOAD_THROTTLE_MS) { iterator.remove(); } } } private void render(JSONObject ops) { replaceContent(contentRoot); contentRoot.removeAllViews(); renderOpsTab(ops); setRefreshing(false); } private void renderOpsTab(JSONObject ops) { contentRoot.addView(BossUi.buildWechatMenuRow( this, "巡检状态", ops.optString("mode", "idle").equals("active") ? "active:当前存在风险线程或未关闭运维工单。" : "idle:当前没有高风险工单,保持低频巡检。", "这里只保留修复与验证的轻量入口。", null, null )); JSONArray faults = ops.optJSONArray("faults"); if (faults == null || faults.length() == 0) { contentRoot.addView(BossUi.buildEmptyCard(this, "当前没有运维故障。")); } else { for (int i = 0; i < faults.length(); i++) { JSONObject fault = faults.optJSONObject(i); if (fault == null) continue; contentRoot.addView(buildFaultCard(fault, ops.optJSONArray("tickets"))); } } } private LinearLayout buildFaultCard(JSONObject fault, @Nullable JSONArray tickets) { LinearLayout card = new LinearLayout(this); card.setOrientation(LinearLayout.VERTICAL); card.addView(BossUi.buildWechatMenuRow( this, fault.optString("faultKey", "故障"), fault.optString("summary", "暂无摘要"), fault.optString("severity", "-") + " · " + fault.optString("status", "-") + " · " + fault.optString("nodeId", "-") + " · " + fault.optString("serviceName", "-") + " · 建议 " + fault.optString("suggestedNextAction", "暂无"), null, null )); if (tickets != null) { for (int i = 0; i < tickets.length(); i++) { JSONObject ticket = tickets.optJSONObject(i); if (ticket == null) continue; if (!fault.optString("faultId").equals(ticket.optString("faultId"))) continue; card.addView(buildTicketCard(ticket)); } } return card; } private LinearLayout buildTicketCard(JSONObject ticket) { LinearLayout card = new LinearLayout(this); card.setOrientation(LinearLayout.VERTICAL); card.addView(BossUi.buildWechatMenuRow( this, ticket.optString("title", "修复工单"), ticket.optString("actionSummary", "暂无动作摘要"), ticket.optString("approvalStatus", "-") + " · " + ticket.optString("executionStatus", "-") + " · " + ticket.optString("targetNodeId", "-") + " · " + ticket.optString("updatedAt", "-"), null, null )); if (ticket.optJSONObject("verification") != null) { JSONObject verification = ticket.optJSONObject("verification"); card.addView(BossUi.buildWechatMenuRow( this, "验证结果", verification.optString("summary", "暂无"), verification.optString("status", "-") + " · " + verification.optString("verifiedAt", "-"), null, null )); } Button approve = BossUi.buildMiniActionButton(this, "批准修复", true); approve.setOnClickListener(v -> approveTicket(ticket.optString("ticketId"))); Button verify = BossUi.buildMiniActionButton(this, "验证修复", false); verify.setOnClickListener(v -> verifyTicket(ticket.optString("ticketId"))); card.addView(BossUi.buildInlineActionRow(this, approve, verify)); return card; } private void approveTicket(String ticketId) { if (ticketId == null || ticketId.isEmpty()) { showMessage("缺少 ticketId"); return; } setRefreshing(true); executor.execute(() -> { try { BossApiClient.ApiResponse response = apiClient.approveRepairTicket(ticketId); if (!response.ok()) throw new IllegalStateException(response.message()); runOnUiThread(() -> { showMessage("修复工单已批准"); reload(); }); } catch (Exception error) { runOnUiThread(() -> { setRefreshing(false); showMessage("批准失败:" + error.getMessage()); }); } }); } private void verifyTicket(String ticketId) { if (ticketId == null || ticketId.isEmpty()) { showMessage("缺少 ticketId"); return; } setRefreshing(true); executor.execute(() -> { try { BossApiClient.ApiResponse response = apiClient.verifyRepairTicket(ticketId); if (!response.ok()) throw new IllegalStateException(response.message()); runOnUiThread(() -> { showMessage("修复结果已验证"); reload(); }); } catch (Exception error) { runOnUiThread(() -> { setRefreshing(false); showMessage("验证失败:" + error.getMessage()); }); } }); } private String joinArray(@Nullable JSONArray values) { if (values == null || values.length() == 0) return "-"; StringBuilder builder = new StringBuilder(); for (int i = 0; i < values.length(); i++) { String value = values.optString(i); if (value == null || value.isEmpty()) continue; if (builder.length() > 0) builder.append(" · "); builder.append(value); } return builder.length() == 0 ? "-" : builder.toString(); } }