# 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: ```json { "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: ```rust pub struct LinelossExportRequest { pub column_defs: Vec<(String, String)>, // (key, chinese_header) pub rows: Vec>, pub sheet_name: String, pub output_path: PathBuf, } pub fn export_lineloss_xlsx(request: &LinelossExportRequest) -> anyhow::Result; ``` 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`.