# Jimureport

> 积木报表生成器 — 自然语言描述报表需求或提供截图，自动生成积木报表（支持数据报表、打印报表、分组报表、循环报表、数据填报等全类型）。Use when user says "积木报表", "jmreport", "Excel报表", "数据填报", "可视化报表", "打印报表", "分组报表", "循环报表", "按照截图生成报表", "创建积木报表", "做一个可视化报表", "积木设计器", "create jimureport", "visual report". Also triggers when user describes report requirements involving Excel-like layouts, data binding with #{}, or multi-sheet reports, or provides a screenshot to g…

- **Type:** Skill
- **Install:** `agentstack add skill-jeecgboot-skills-jimureport`
- **Verified:** Pending review
- **Seller:** [jeecgboot](https://agentstack.voostack.com/s/jeecgboot)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [jeecgboot](https://github.com/jeecgboot)
- **Source:** https://github.com/jeecgboot/skills/tree/main/jimureport
- **Website:** https://jeecg.com

## Install

```sh
agentstack add skill-jeecgboot-skills-jimureport
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# 积木报表 AI 生成器

> 不涉及「Online 报表」（cgreport）或「Online 表单」（cgform）。

## 临时配置文件规则（强制）

所有传给脚本的 `--config ` 必须写到 **`{系统临时目录}/{SKILL_NAME}/`** 下，由操作系统自动清理；skill 与脚本均不主动删除该目录或文件。

```python
import tempfile, os, json

SKILL_NAME = ""               # 请替换为实际的技能名称
skill_dir = os.path.join(tempfile.gettempdir(), SKILL_NAME)
os.makedirs(skill_dir, exist_ok=True)          # 确保目录存在，不主动检查

config_path = os.path.join(skill_dir, 'sk_audit_create.json')   # 示例文件名
with open(config_path, 'w', encoding='utf-8') as f:
    json.dump(cfg, f, ensure_ascii=False, indent=2)
```

`tempfile.gettempdir()` 自动适配：Windows `%TEMP%`、Linux `/tmp`、macOS `/var/folders/.../T`（注意 macOS 并非 `/tmp`）。  
文件名建议使用 **`_.json`**（如 `sk_audit_create.json`），无需重复技能前缀，因路径已包含技能名称，便于排错。

**❌ 禁止：**

- 写到 `/tmp/` 或当前工作目录（污染 skill / 用户项目）
- 硬编码 `/tmp`、`C:\Temp` 或任何固定路径（不跨平台）
- 每步完成后主动 `rm` / `Remove-Item`（操作系统会清理，属多余 tool call）
- 主动 `os.path.exists()` 检查（其本身即为一次 tool call）  
  （使用 `os.makedirs(…, exist_ok=True)` 满足需求，不算主动检查）

**临时文件可能被操作系统异步清理**，但仍遵循 **乐观调用 + 报错补救**：仅当脚本返回 `FileNotFoundError` 或 `配置文件不存在` 时，使用相同内容、**在相同的 `{系统临时目录}/{SKILL名称}/` 路径下重写**（重写前仍需 `os.makedirs(skill_dir, exist_ok=True)` 确保目录存在），切勿更换路径或回退至 skill 目录。

## 一键脚本（必看，覆盖三类全场景）

写自定义 JSON / Python 之前，先看用户需求是否命中下表现成脚本，命中则**直接调用，禁止重新组装 JSON 或 Python**：

| 用户描述（关键词） | 直接调用 | 默认覆盖 |
|------|---------|---------|
| 「全图表」「所有图表」「图表大全」「测试所有数据集类型」「SQL+API+JSON」「图表展示」 | `python scripts/generate_all_reports.py --base-url ... --token ... --name "..." --mysql-host ... --mysql-port ... --mysql-db ... --mysql-user ... --mysql-pwd ...` | 25 个图表（SQL 12 + API 2 + JSON 4 + 不绑 7），自动建 `chart_demo_all` 表插数据 + 自动创建 YApi mock + 一次保存 |

### 命中规则与禁止事项

- **关键词命中即用**：用户说「生成全部图表」「全图表测试」「演示所有图表」「测试 SQL/API/JSON 三种数据集」时，**第一反应**就是 `generate_all_reports.py`，**不要**回头自己写 chart_entry/echarts 模板
- **3 秒能跑完**：实测 ~3.1s 端到端创建。脚本启动后**不要分块等待、不要发 AskUser 求确认**，直接 `Bash` 等结果
- **Mock 新建必须用唯一路径**：`create_mock` 遇到同路径会**静默覆盖已有接口数据**，污染他人接口。创建新接口时必须在路径末尾追加时间戳或序号（如 `/sales_20260427`），只有用户明确说"修改/更新已有接口"时才可复用原路径
- **saveDb 串行**：脚本已改为串行调用 `save_db`，避免 `jimu_report_db_field` INSERT 并发引发 MySQL deadlock
- **新增图表类型时**：在 `CHARTS` 列表加一行，写一个 `tpl_xxx` 函数即可，无需重写主流程

## 执行流程

**第零步（必须）：Token 优先**

用户消息里没有 X-Access-Token 时，**立刻询问**，拿到 token 后再读任何文件。等待回复期间不要预读文件——等待时间不计入 3 分钟，文件读取时间计入。

> **⚠️ 凭证禁止读记忆，直接问用户**：需要数据库密码、账号密码等任何凭证时，**禁止**读取 memory 文件获取，必须直接在对话中问用户。

**第一步（必须）：按「执行速度规范」表选最小文件集**

> **不要先 Glob examples/**，直接查下方「执行速度规范」表，按场景只读指定文件。禁止在表外额外读文件。
> **场景匹配优先于文件名匹配**：`multi-level-header.md` 主要是**交叉表 groupRight/dynamic**，纵向分组+静态多级表头不要读它（浪费 ~30s 读不适用示例）。

读完指定文件后直接 Write JSON 配置 → 执行 CLI 命令 → 输出预览链接。**两步完成，禁止多余动作。**

> **报表链接格式**（创建成功后直接输出，禁止调接口验证是否存在）：
> - 设计器：`http://{host}/jmreport/index/{report_id}?token={token}&tenantId=1`
> - 预览：`http://{host}/jmreport/view/{report_id}?token={token}&tenantId=1`

> **报表名称规则**：用户明确指定名称时直接使用；未指定时 AI 自动生成名称，生成后须调 `GET /jmreport/query/report/folder?pageNo=1&pageSize=10&reportType=&name={name}&token={token}` 检查是否重复，有同名则追加后缀（如 `_2`、`_20260415`）。

**utils 子模块速查**（需确认某函数签名时，Grep 对应小文件，禁止读全量 jimureport_utils.py）：

| 需要确认的函数 | 读哪个文件 |
|--------------|----------|
| Session、gen_id/code/layer、col_letter、_compute_sign | `jimureport_core.py` |
| parse_api、parse_sql、save_db、update_db、parse_and_save_dataset、parallel_parse/save/api | `jimureport_dataset.py` |
| make_designer、base_save、get_report、report_urls、print_summary | `jimureport_report.py` |
| make_styles、**STYLE_BASE/DATA/HEADER/TITLE/LINK**（命名常量，禁止用魔法数字） | `jimureport_styles.py` |
| chart_entry、virtual_row、build_chart_layout、update_chart_config、parallel_fill_charts、**pick_chart_axes** | `jimureport_chart.py` |
| create_link、parallel_create_links | `jimureport_link.py` |
| ensure_datasource、find_datasource、get_ds_connection、query_mysql、**execute_ds** | `jimureport_datasource.py` |

| 禁止 | 替代 |
|------|------|
| 读全量 jimureport_utils.py | 按上表 Grep/Read 对应子模块（各 25-175 行） |
| **Grep/Read `jimureport_gen.py`（任何原因）** | `api_dataset/group/standard` 等函数的签名和参数在 SKILL.md 调用示例中已完整给出（base_url 默认 http://192.168.1.6:8085/jmreport，无需传），"不确定参数"不构成读源码的理由，直接信任文档 |
| **Grep/Read `jimureport_creator.py` 确认是否支持某功能**（如 fieldList searchMode、paramList 等） | SKILL.md 的 JSON 配置模板和禁止表已覆盖所有场景，直接写 JSON 配置执行，禁止读源码验证 |
| **Grep/Read `jimureport_dataset.py` 查看 parse_sql 实现** | `parse_sql` 接受含 FreeMarker 条件的 SQL，服务端以空参评估后解析字段列表，直接调用即可，无需看源码 |
| 找 DB 凭证 | 用 memory 中的配置或问用户 |
| Windows 下 Bash tool 跑 python | **改用 PowerShell tool 跑 `python xxx.py` / `python -c "..."`**，同步返回（详见下方「Windows 执行环境」） |
| 调外部 API 验证字段 | 直接按用户提供的字段写脚本，不预调 API |
| 擅自调用 `create_mock()` 或 `init_yapi()` | **⛔ 用户已提供接口 URL 时，直接 `save_db(api_url=URL)`，严禁调用 `create_mock()` / `init_yapi()` / 任何 YApi 登录或验证操作**；只有用户明确说"帮我创建 mock 接口"或完全未提供 URL 时才调用；URL 已知 = 直接用，不验证、不询问、不登录 |
| `sleep` + `cat` 轮询输出 | Bash 命令在 Windows 始终被后台化；若仍要走 Bash，**必须用 `TaskOutput(task_id, block=true)` 等待结果**，禁止用 sleep/cat 轮询 |
| 报表创建后调接口验证是否存在 | `/save` 返回 `success:true` 即成功，直接输出设计/预览链接，无需查报表列表 |
| **手写 border 样式**（`{"style":1}` 或任何非数组格式） | **必须用 `make_styles()` 获取 styles 列表**，它已内置正确的 `["thin","#d8d8d8"]` 数组格式；手写 border 一律禁止，会导致整表渲染空白 |
| **`customRows` 配合空 `columns: []`** | creator 靠 `columns` 生成 `queryInfo.dbf`；`columns` 为空则不写入绑定元数据，报表预览**完全无数据**（即使数据集 `records=N`）。`customRows` 只控制视觉布局，`columns` 必须填数据集的实际字段（至少一个）|
| **用户未指定的可选参数自行填值**（如 `customEditConf.eventParams`、`freeze`、`rpbar`、`background` 等） | 只写用户明确给出的字段，其余可选参数一律省略，不得自造默认值 |
| 调 `report_urls()` 工具函数当作 dict 用（如 `urls['designer']`） | `report_urls(report_id, base_url, token, tenant)` 返回 **tuple `(preview_url, design_url)`**，不是 dict；且第一个参数是 report_id 不是 base_url。**直接按本节"报表链接格式"拼字符串**，不要调用此函数 |
| **报表创建走自定义 .py 脚本** | ⛔ **必须用 JSON 配置文件 + `jimureport_creator.py` CLI**：只写 `xxx.json` → `python jimureport_creator.py --api-base URL --token TOKEN --config xxx.json`。需要同时创建 YApi mock 的 API 数据集报表，用 `jimureport_gen.api_dataset(..., mock_data=[...], mock_path="/xxx_日期")` 一步完成，内部自动生成 JSON 并调 CLI，无需手写 Python 脚本。用户反馈（2026-05-22 再次确认）："以后只生成JSON"。**⚠️ 典型错误场景1**：用户要求自定义样式，AI 认为高级函数不支持样式就手写 `rows/cols/styles/save_db/base_save`——严重违规。**⚠️ 典型错误场景2**：场景复杂（含钻取+图表+建表），AI 直接写 `drill_demo.py` 等全流程 Python 脚本——严重违规。正确做法：报表结构部分始终写 JSON；建表/链接创建等 JSON 不支持的步骤写成最小独立 PowerShell inline；两部分分开，不混写。|
| **PowerShell `Out-File -Encoding utf8` 写 JSON** | 产生 UTF-8 BOM 导致 `json.load` 报 `Unexpected UTF-8 BOM`。**必须用**：`[System.IO.File]::WriteAllText($path, $content, (New-Object System.Text.UTF8Encoding $false))` |

### Windows 执行环境（强制规则，违反会让用户吐槽"执行太慢"）

**现象**：Windows 的 Bash tool 会把 `python` / `python -c` / skill 脚本当作长命令自动 `run_in_background`，tool 立即返回 background ID，真正输出要等完成通知——把毫秒级调用放大到数秒，历史上多次让单报表从 1 分钟拖到 18 分钟。

**规则**：
- **Windows（platform=win32）** → 用 **PowerShell tool** 直接执行 `python xxx.py`，同步返回。禁止用 Bash tool 跑 python（会被后台化）。
- **Linux / macOS** → 用 Bash tool 直接调用 `python xxx.py`。
- 任何平台都不用 `curl`：跨平台不一致，Windows Bash 下同样被后台化。

**脚本执行前强制检查（2 项）**：
1. ✅ Windows 下用 **PowerShell tool** 执行 `python xxx.py`，**不是** Bash tool
2. ✅ 脚本第一行已加编码声明：`import sys; sys.stdout.reconfigure(encoding='utf-8')`（防 GBK 崩溃重试）

**Windows 正确示例**：
```
PowerShell: python /scripts/xxx.py --base-url ... --token ...
```
> `` 是本 SKILL.md 所在目录，运行时用实际路径替换，禁止写死 `C:/Users/...`。

**Windows 错误示例**：
```
Bash: python generate_all_reports.py ...    ← 返回 "Command running in background with ID: xxx"
Bash: curl -X POST ...                       ← 同上
```

> **历史教训**：曾因默认走 Bash + python 被用户连续吐槽"执行太慢了 / 生成这么慢"。根因是 Bash tool 在 Windows 对 python 会后台化，不限于 curl。另一常见重试原因：脚本缺编码声明导致 `UnicodeEncodeError: 'gbk' codec`，加第2项检查可消除。
> **典型症状**：报表生成完成后仍等待约 2 分钟才结束——这是 Bash 后台化的直接表现：脚本已跑完但 tool 在等 background 完成通知。遇到此现象立即确认是否误用了 Bash tool，改 PowerShell 即可消除。

## 前置条件

用户须提供 **X-Access-Token**。

### SQL 数据集数据源选取（用户未指定 `dbSource` 时必须执行）

**正确流程（必须每次执行，不可跳过）：**

1. 调 `GET /jmreport/initDataSource` 获取数据源列表
2. 按用户提供的数据库名（如 `jeecg-boot-cr`）**精确匹配** `name` 字段，取其 `id`
3. 找不到再询问用户

> **⚠️ 禁止用 memory 中存的数据源ID直接跳过查询**：memory 里的ID可能已过期或被重建，必须每次查询后按名字匹配拿到当前有效ID。memory 只用于记住数据库名，不用于记住ID。
> **⚠️ 禁止全量拉取后遍历猜测**：有明确数据库名时直接按名字匹配，不要靠含"积木"等模糊规则。

按以下规则处理：

| 返回结果 | 处理方式 |
|---------|---------|
| `result` 为空数组 | 告知用户需要先在积木报表中新增数据源，停止创建 |
| `result` 非空，存在 `name` 含"积木"的项 | 自动选该项，将其 `id` 作为 `db_source` 传入 `save_db` |
| `result` 非空，无含"积木"的项 | 列出所有数据源名称，询问用户选哪个，等待回复后再继续 |

接口返回字段：每项包含 `id`（传给 `db_source`）和 `name`（展示给用户）。

**脚本中直接调用**（禁止在脚本里重新手写此逻辑）：

```python
from jimureport_utils import resolve_db_source
# 用户未指定数据源时：
db_source = resolve_db_source(session)   # 自动选含「积木」的；无则抛 RuntimeError 列出清单
```

`RuntimeError` 消息已包含数据源列表，捕获后直接转告用户即可。

> **上下文优先**：本次对话中已经通过 `resolve_db_source` 或用户回复确定过 `db_source`，后续同一会话的报表直接复用，**不得重复调用 `initDataSource`**。
> **⚠️ 禁止全量拉取后遍历猜测**：不要拉取全部30+数据源再靠名字模糊匹配，应按用户提供的数据库名精确查找，或直接读 memory。全量拉取是浪费 + 容易选错。

### API 数据集前置询问（用户未提供 API 地址时必须先问）

用户未给出 API 地址时，**必须先询问**：

> 请问接口用哪种方式创建？
> - **mock 接口**：通过 YApi 创建 mock 接口（参见下方「YApi Mock 数据源」章节）
> - **本地代码**：请提供本地 JeecgBoot 项目路径，我直接把 Controller 写入项目

**收到答复后的处理规则：**

| 用户选择 | 处理方式 |
|---------|---------|
| mock 接口 | 按「YApi Mock 数据源」章节流程，用 `yapi_mock.py` 创建 mock 接口，返回 mock URL 填入数据集 |
| 本地代码 | 询问项目路径（如 `D:\path\to\jeecg-boot`），只生成 Controller 写入项目，返回静态数据（`{"data": [...]}`），**不生成** Entity / Mapper / Service / SQL |

## 🚀 CLI 创建（一条命令）

```bash
python /scripts/jimureport_creator.py \
  --api-base http://BASE_URL --token TOKEN --config /path/to/config.json
```

### 配置 A：SQL 普通/分组报表

```json
{
  "action": "create", "reportName": "报表名称", "theme": "blue",
  "datasets": [{"dbCode":"ds1","dbChName":"数据集","dbDynSql":"SELECT col1,col2 FROM t ORDER BY col1","dbSource":"","isPage":"0"}],
  "table": {"datasetCode":"ds1","title":"报表名称","columns":[
    {"field":"col1","title":"列1","width":120,"group":true},
    {"field":"col2","title":"列2","width":100,"funcname":"SUM"}
  ]}
}
```
> columns 可选属性：`group:true`(分组) / `funcname:"SUM"`(聚合) / `subtotalText:"小计"`

### 配置 B：SQL + 图表

```json
{
  "action":"create","reportName":"名称","layout":"chart_bottom",
  "datasets":[
    {"dbCode":"dt","dbChName":"表格","dbDynSql":"SELECT ...","isPage":"1"},
    {"dbCode":"dc","dbChName":"图表","dbDynSql":"SELECT x AS name,y AS value,'' AS type FROM ...","isPage":"0"}
  ],
  "table":{"datasetCode":"dt","title":"名称","columns":[...]},
  "chart":{"datasetCode":"dc","chartType":"bar.simple","title":"图表","width":"650","height":"380"}
}
```
> layout: `chart_bottom` / `chart_top` / `chart_right` / `chart_only`

### 配置 C：JSON 数据集（dbCode 必须字符串！）

```json
{
  "action":"create","reportName":"名称",
  "datasets":[{"dbCode":"my_data","dbChName":"数据","dbType":"3","isList":"1","isPage":"0",
    "jsonData":[{"name":"张三","age":"25"}],
    "fieldList":[["name","姓名"],["age","年龄"]]}],
  "table":{"datasetCode":"my_data","title":"名称","columns":[
    {"field":"name","title":"姓名","width":100},{"field":"age","title":"年龄","width":80}]}
}
```
> **禁止纯数字 dbCode**（如 gen_code()），JSON 数据集模板引擎无法解析。

> **f-string 写绑定字段时必须转义花括号**：`f"#{{{db_code}.{field}}}"` → 生成 `#{db_code.field}`。若写成 `f"#{db_code}.{field}#"` 则花括号被 Python 吃掉，变成 `#db_code.field#`（格式错误，末尾多 `#`，数据不渲染）。

### 配置 D：自定义 rows（复杂多级表头）

build_table_rows 无法满足时（如四级合并表头），传 `customRows` + `customMerges` 跳过自动构建：

```json
{
  "action":"create","reportName":"名称",
  "datasets":[{"dbCode":"ds1","dbType":"3","jsonData":[...],"fieldList":[...]}],
  "table":{"datasetCode":"ds1","columns":[{"field":"f1","title":"F1","width":100}]},
  "groupField":"ds1.group_field",
  "customRows":{"1":{"cells":{"1":{"text":"标题","style":0,"merge":[0,5]}},"height":40}},
  "customMerges":["B2:G2"],
  "customStyles":[{"align":"center","font":{"size":16,"bold":true}},{"align":"center","font":{"bold":true,"color":"#FFF"},"bgcolor":"#4472C4"},{"align":"center","valign":"middle"}],
  "customCols":{"0":{"width":27},"1":{"width":100},"len":100}
}
```

> **⚠️ `table.columns` 与 `customRows` 同时存在时，`columns` 仍不可为空数组 `[]`**
> creator 依赖 `columns` 来生成 `designerObj` 中的 `queryInfo.dbf`（数据集绑定元数据）。
> 若 `columns: []`，`queryInfo.dbf` 不会写入，报表预览将**完全空白（无数据）**，即使数据集已保存成功（`records=N`）。
> **正确做法**：`columns` 填入数据集的实际字段（哪怕只有一个），`customRows` 再覆盖视觉布局；两者独立互不影响。

### 配置 E：含钻取（drilling）——**无需任何额外 Python 脚本**

`drilling` 和 `linkages` 键已**内置**于 creator：creator 自动调用 `/link/saveAndEdit` 并把 `linkIds` 回填到 cells / chart extData，全程只需一个 JSON 文件。

```json
{
  "action": "create",
  "reportName": "主报表（三种钻取演示）",
  "theme": "blue",
  "layout": "chart_bottom",
  "datasets": [
    {
      "dbCode": "sales", "dbChName": "学校汇总",
      "dbDynSql": "SELECT leibie AS name, SUM(jine) AS value, '' AS type, 'jeecg.com' AS website FROM school_demo GROUP BY leibie ORDER BY value DESC",
      "dbSource": "1161942757348524032", "isPage": "0"
    }
  ],
  "table": {
    "datasetCode": "sales", "title": "学校汇总报表",
    "columns": [
      {"field": "name",    "title": "类别（点击→子报表）", "width": 140},
      {"field": "value",   "title": "总金额",              "width": 120},
      {"field": "website", "title": "官网（点击→jeecg.com）","width": 180}
    ]
  },
  "chart": {
    "datasetCode": "sales", "chartType": "bar.simple",
    "title": "各类别总金额（点击柱子钻取）", "width": "560", "height": "360"
  },
  "drilling": [
    {
      "name": "钻取1-报表钻取报表",
      "linkType": "0",
      "targetReportId": "",
      "ejectType": "0",
      "source": {"type": "cell", "field": "name"},
      "params": [
        {"paramName": "leibie", "paramValue": "name", "fieldName": "name", "dbCode": "sales", "tableIndex": 0}
      ]
    },
    {
      "name": "钻取2-图表钻取报表",
      "linkType": "0",
      "targetReportId": "",
      "ejectType": "0",
      "source": {"type": "chart"},
      "params": [
        {"paramName": "leibie", "paramValue": "name", "fieldName": "", "dbCode": "sales", "tableIndex": 0}
      ]
    },
    {
      "name": "钻取3-网络钻取",
      "linkType": "1",
      "targetUrl": "http://jeecg.com",
      "ejectType": "0",
      "source": {"type": "cell", "field": "website"},
      "params": [
        {"paramName": "school", "paramValue": "name", "fieldName": "name", "dbCode": "sales", "tableIndex": 0}
      ]
    }
  ]
}
```

**drilling 字段说明：**

| 字段 | 说明 |
|------|------|
| `linkType` | `"0"` 报表钻取 / `"1"` 网络链接 |
| `targetReportId` | linkType=0 时填目标报表 ID；linkType=1 时留空 |
| `targetUrl` | linkType=1 时填外部 URL |
| `ejectType` | `"0"` 新窗口 / `"1"` 当前窗口 |
| `source.type` | `"cell"` 单元格触发 / `"chart"` 图表触发 |
| `source.field` | cell 时填数据集字段名（creator 自动匹配含该字段的数据行单元格并回填 `linkIds`） |
| `params[].paramValue` | 单元格钻取填字段名；图表钻取填 `name`（X轴）/ `value`（Y轴）/ `seriesName` |

> **跨报表钻取的顺序**：先创建子报表拿到 `detail_id`，再把 `detail_id` 填入主报表 JSON 的 `targetReportId`，两个 JSON 分别跑 CLI 即可，无需额外 Python 脚本。

### 配置 F：NoSQL 数据集（ES / MongoDB / Redis）

`dbType` 填 `"es"/"mongo"/"mongodb"/"redis"`，creator 自动补 Calcite schema 前缀；`dbSource` 必填数据源 ID。

```json
{"action":"create","reportName":"名称",
 "datasets":[
   {"dbCode":"esDs",    "dbChName":"ES员工",   "dbType":"es",    "esIndex":"jmreport_test_employee","dbSource":"",    "isPage":"1"},
   {"dbCode":"mongoDs", "dbChName":"Mongo订单", "dbType":"mongo", "mongoCollection":"orders",        "dbSource":"", "isPage":"1"},
   {"dbCode":"redisDs", "dbChName":"Redis缓存", "dbType":"redis", "dbDynSql":"SELECT * FROM cache",  "dbSource":"", "isPage":"0"}
 ],
 "table":{"datasetCode":"esDs","title":"员工列表","columns":[
   {"field":"emp_id","title":"工号","width":80},{"field":"emp_name","title":"姓名","width":100}
 ]}
}
```

| 字段 | 说明 |
|------|------|
| `esIndex` | ES 专用简写，自动生成 `SELECT * FROM es.{索引名}`；有它可省略 `dbDynSql` |
| `mongoCollection` | MongoDB 专用简写，自动生成 `SELECT * FROM mongo.{集合名}` |
| `dbDynSql` | 显式 SQL；ES/Mongo 缺 `es.`/`mongo.` 前缀时自动补全；Redis 原样传入 |

> ES 字段名若与 Calcite 保留字冲突（`position`/`date`/`type`/`value` 等），需反引号转义或改用 `esIndex` 简写（`SELECT *` 无需列出字段名，天然规避）。详见 pitfalls.md §数据库数据源。

## 修改已有报表

```python
# get_report → 改 design → base_save(**design 展开，get_report 返回的 design 是安全的)
designer, design = get_report(session, report_id)
design["rows"]["3"]["cells"]["1"]["text"] = "新值"
design["chartList"] = filled_charts   # 如有图表回填，直接替换 chartList
session.request("/save", base_save(report_id, designer, **design))
# ↑ get_report 返回的 design 只含 base_save 接受的 key，**design 展开无冲突
# 注意：手动拼的 design dict 禁止 **展开，必须显式列出 rows/cols/styles/merges/chartList
```

> **⚠️ Bug 修复必须

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [jeecgboot](https://github.com/jeecgboot)
- **Source:** [jeecgboot/skills](https://github.com/jeecgboot/skills)
- **License:** Apache-2.0
- **Homepage:** https://jeecg.com

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-jeecgboot-skills-jimureport
- Seller: https://agentstack.voostack.com/s/jeecgboot
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
