Files
claw/docs/superpowers/specs/2026-04-13-rust-side-lineloss-xlsx-export.md
木炎 c60cd308ca feat: service console auto-connect, settings panel, and batch of enhancements
- Auto-connect WebSocket on page load in service console
- Settings modal for editing sgclaw_config.json (API key, base URL, model, skills dir, etc.)
- UpdateConfig/ConfigUpdated protocol messages for remote config save
- save_to_path() for SgClawSettings serialization
- ConfigUpdated handler in sg_claw_client binary
- Protocol serialization tests for new message types
- HTML test assertions for auto-connect and settings UI
- Additional pending changes: deterministic submit, org units, lineloss xlsx export, browser script tool, and docs

🤖 Generated with [Qoder][https://qoder.com]
2026-04-14 14:32:46 +08:00

3.5 KiB

Rust-Side Lineloss XLSX Export

Problem

collect_lineloss.js runs on a remote page (http://20.76.57.61:18080/gsllys). The script successfully queries API data (12 rows), but cannot call http://localhost:13313/.../faultDetailsExportXLSX because the browser blocks cross-origin requests from a remote page to localhost.

The original scene architecture had a local scene page acting as a proxy, but skill mode has no local page -- so export is architecturally impossible from the browser side.

Decision

Move XLSX generation to the Rust side. JS only collects data; Rust generates the .xlsx file locally after receiving the artifact.

Report log (setReportLog) is deferred to a later iteration.

Design

JS Changes (collect_lineloss.js)

  1. Remove exportWorkbook() call and writeReportLog() call
  2. Return artifact with rows array and column_defs array
  3. Status is ok when rows > 0, empty when rows == 0, error/blocked unchanged

Artifact shape:

{
  "type": "report-artifact",
  "report_name": "tq-lineloss-report",
  "status": "ok",
  "org": { "label": "...", "code": "..." },
  "period": { "mode": "month", "value": "2026-03" },
  "column_defs": [["ORG_NAME","供电单位"], ["YGDL","累计供电量"], ...],
  "rows": [
    {"ORG_NAME":"xxx", "YGDL":"12345.67", ...}
  ],
  "counts": { "rows": 12 }
}

Rust Changes

New file: src/compat/lineloss_xlsx_export.rs

Generates a standard .xlsx file using zip crate + OpenXML XML strings. Follows the pattern established in openxml_office_tool.rs.

Public API:

pub struct LinelossExportRequest {
    pub column_defs: Vec<(String, String)>,  // (key, chinese_header)
    pub rows: Vec<Map<String, Value>>,
    pub sheet_name: String,
    pub output_path: PathBuf,
}

pub fn export_lineloss_xlsx(request: &LinelossExportRequest) -> anyhow::Result<PathBuf>;

Internals:

  • Build header row from column_defs[*].1 (chinese names)
  • Build data rows by looking up column_defs[*].0 keys in each row map
  • Generate worksheet_xml with inline string cells
  • Package with standard OpenXML boilerplate (content_types, rels, workbook)
  • Write to output_path

Modified: src/compat/deterministic_submit.rs

In execute_deterministic_submit_with_browser_backend (and the non-backend variant):

let output = execute_browser_script_skill_raw_output_with_browser_backend(...)?;
let artifact = parse_lineloss_artifact(&output);

if artifact has rows > 0 && column_defs present:
    let export_path = workspace_root/out/tq-lineloss-{timestamp}.xlsx
    export_lineloss_xlsx(LinelossExportRequest { ... })?
    // attach export_path to outcome summary

Ok(summarize_lineloss_output_with_export(&output, export_path))

Modified: src/compat/mod.rs

Add pub mod lineloss_xlsx_export;

Output Path

{workspace_root}/out/tq-lineloss-{org_label}-{period}-{timestamp_nanos}.xlsx

Error Handling

  • XLSX generation failure: outcome status = partial, reason = xlsx_export_failed
  • Artifact parse failure: fall through to existing summarize_lineloss_output

Files Changed

File Change Type
collect_lineloss.js Modify: remove export/log calls, add rows+column_defs to artifact
src/compat/lineloss_xlsx_export.rs New: XLSX generation
src/compat/deterministic_submit.rs Modify: post-process artifact, call XLSX export
src/compat/mod.rs Modify: register new module

Requires Recompilation

Yes. Rust code changes require cargo build.