Files
qhmes/.trae/skills/jeecg-onlreport/SKILL.md

19 KiB
Raw Permalink Blame History

name, description
name description
jeecg-onlreport Use when user asks to create/edit Online reports, SQL reports, data reports, or says "创建报表", "生成报表", "新建报表", "做一个报表", "online报表", "SQL报表", "数据报表", "统计报表", "查询报表", "create report", "generate report", "data report". Also triggers when user describes report requirements like "做一个销售统计报表" or mentions SQL-driven data display like "通过SQL查询生成报表".

JeecgBoot Online 报表 AI 自动生成器

将自然语言的报表需求描述转换为 Online 报表配置,并通过 API 在 JeecgBoot 系统中自动创建/编辑报表。

重要:本 skill 处理「Online 报表」SQL 驱动的数据报表不涉及「Online 表单」cgform或「设计器表单」desform

前置条件

用户必须提供以下信息(或由 AI 引导确认):

  1. API 地址JeecgBoot 后端地址(如 https://boot3.jeecg.com/jeecgboot
  2. X-Access-TokenJWT 登录令牌(从浏览器 F12 获取)

如果用户未提供,提示:

请提供 JeecgBoot 后端地址和 X-Access-Token从浏览器 F12 → Network → 任意请求的 Request Headers 中复制)。

交互流程

Step 0: 判断操作类型

用户意图关键词 操作类型
创建/新建/做一个/生成报表 新增报表 → Step 1A
修改报表/改字段/加字段/删字段 编辑报表 → Step 1B

Step 1A: 新增报表 — 解析需求

从用户描述中提取:

信息 默认值 示例
报表编码 (code) 自动生成 snake_case sales_report
报表名称 (name) 用户指定 "销售统计报表"
SQL 语句 (cgrSql) 从需求推导或用户提供 SELECT ... FROM ...
数据源 (dbSource) 空(默认数据源) second_db

两种 SQL 来源:

  1. 用户直接提供 SQL — 直接使用,调用 parseSql 解析字段
  2. 用户描述需求AI 推导 SQL — 需要用户确认数据库表结构或已知表名

Step 1B: 编辑报表 — 查询现有配置

  1. 用户提供报表 ID 或编码
  2. 通过 API 查询现有报表配置:GET /online/cgreport/head/queryById?id={headId}
  3. 通过 API 查询字段列表:GET /online/cgreport/item/listByHeadId?cgrheadId={headId}
  4. 通过 API 查询参数列表:GET /online/cgreport/param/listByHeadId?cgrheadId={headId}
  5. 展示现有配置,根据用户需求进行修改

Step 2: 调用 parseSql 解析字段

必须先调用 parseSql 接口获取 SQL 的字段和参数列表:

GET /online/cgreport/head/parseSql?sql={urlEncodedSql}&dbKey={dbKey}
  • sqlURL 编码后的 SQL 语句
  • dbKey:数据源编码,默认数据源可不传

实测返回结构(已验证):

{
  "success": true,
  "message": "",
  "code": 200,
  "result": {
    "fields": [
      {
        "id": "2032684046700560386",
        "cgrheadId": null,
        "fieldName": "id",
        "fieldTxt": "id",
        "fieldWidth": null,
        "fieldType": "String",
        "searchMode": null,
        "isOrder": null,
        "isSearch": null,
        "dictCode": null,
        "fieldHref": null,
        "isShow": 1,
        "orderNum": 1,
        "replaceVal": null,
        "isTotal": null,
        "createBy": null,
        "createTime": null,
        "updateBy": null,
        "updateTime": null,
        "groupTitle": null
      }
    ],
    "params": []
  },
  "timestamp": 1773464616834
}

Step 3: 智能字段配置

根据字段名和业务语义AI 自动推导每个字段的配置:

3.1 字段显示名称 (fieldTxt)

parseSql 返回的 fieldTxt 默认等于 fieldNameAI 需要根据语义翻译为中文:

字段名模式 推导中文名
id ID/主键
name / title 名称/标题
code / no 编码/编号
status 状态
type / category 类型/分类
amount / money / price 金额/费用/价格
count / qty / num 数量
date / time 日期/时间
create_by 创建人
create_time 创建时间
update_by 更新人
update_time 更新时间
sex 性别
age 年龄
email 邮箱
phone / mobile / tel 电话/手机号
address 地址
remark / content / description 备注/内容/描述
dept / org 部门/组织
salary 薪资
birthday 生日

3.2 是否显示 (isShow)

规则 isShow
业务字段(默认) 1显示
id / 主键字段 0隐藏— 通常不在报表中展示
create_by / update_by 0隐藏— 系统字段
create_time / update_time 视需求而定
sys_org_code / tenant_id 0隐藏— 系统字段

3.3 是否查询 (isSearch) + 查询模式 (searchMode)

字段类型 isSearch searchMode 说明
名称/标题等文本 1 like 模糊查询
状态/类型/分类 1 single 精确匹配
日期/时间 1 range 范围查询(开始~结束)
金额/数量等数值 0 - 通常不查询
系统字段 0 - 不查询

3.4 是否排序 (isOrder)

规则 isOrder
日期/时间字段 1
金额/数量字段 1
其他 0

3.5 字段类型 (fieldType)

SQL 列类型 fieldType
varchar / char / text String
int / tinyint / smallint Integer
bigint Long
decimal / double / float BigDecimal
date Date
datetime / timestamp Datetime

注意parseSql 返回的 fieldType 可能都是 StringAI 应根据字段名语义或用户描述修正。

3.6 字典配置 (dictCode)

支持两种方式:

方式一:系统字典编码

"dictCode": "sex"

方式二SQL 字典

"dictCode": "SELECT id as value, name as text FROM sys_category"

常用系统字典:

字典编码 说明
sex 性别 (1=男, 2=女)
priority 优先级
valid_status 有效状态
urgent_level 紧急程度
yn 是否

3.7 取值表达式 (replaceVal)

用于将数据库值替换为显示文本(导出时使用):

"replaceVal": "男_1,女_2"

格式:显示文本_数据库值,显示文本_数据库值,...

3.8 是否合计 (isTotal)

规则 isTotal
金额/费用/价格字段 "1"(合计)
数量字段 "1"(合计)
其他 "0" 或 null

3.9 分组表头 (groupTitle)

多个字段可以共用一个分组表头,实现多级表头效果:

{"fieldName": "q1_amount", "groupTitle": "第一季度"},
{"fieldName": "q1_count", "groupTitle": "第一季度"},
{"fieldName": "q2_amount", "groupTitle": "第二季度"},
{"fieldName": "q2_count", "groupTitle": "第二季度"}

3.10 字段跳转 (fieldHref)

"fieldHref": "/details?id=${id}"

支持 ${fieldName} 变量替换。

Step 4: SQL 参数配置

SQL 中的 ${paramName} 会被解析为参数:

SELECT * FROM sales
WHERE 1=1
${#if($startDate != '')} AND sale_date >= '$startDate' ${#end}
${#if($endDate != '')} AND sale_date <= '$endDate' ${#end}

参数配置:

属性 说明
paramName 参数名(对应 SQL 中的 ${xxx}
paramTxt 参数显示名称
paramValue 默认值(可为空)
orderNum 排序序号

Step 5: 展示摘要并确认

必须展示以下内容,等待用户确认后再执行:

## Online 报表配置摘要

- 报表编码sales_report
- 报表名称:销售统计报表
- 数据源:默认
- 目标环境https://boot3.jeecg.com/jeecgboot

### SQL 语句
SELECT s.id, s.name, s.amount, s.sale_date, s.status
FROM biz_sales s
WHERE 1=1

### 字段配置

| 序号 | 字段名 | 显示名称 | 类型 | 显示 | 查询 | 排序 | 字典 | 合计 |
|------|--------|---------|------|------|------|------|------|------|
| 0 | id | ID | String | 否 | 否 | 否 | - | - |
| 1 | name | 名称 | String | 是 | 是(模糊) | 否 | - | - |
| 2 | amount | 金额 | BigDecimal | 是 | 否 | 是 | - | 是 |
| 3 | sale_date | 销售日期 | Date | 是 | 是(范围) | 是 | - | - |
| 4 | status | 状态 | String | 是 | 是(精确) | 否 | valid_status | - |

### 参数

| 参数名 | 显示名称 | 默认值 |
|--------|---------|--------|
| (无) | | |

确认以上配置?(y/n)

Step 6: 调用 API 创建/编辑报表

用户确认后执行。

6.1 构造请求 JSON

新增报表 (add)

{
  "head": {
    "code": "report_code",
    "name": "报表名称",
    "cgrSql": "SELECT ... FROM ...",
    "dbSource": ""
  },
  "items": [
    {
      "id": "前端生成的长数字ID",
      "cgrheadId": null,
      "fieldName": "field_name",
      "fieldTxt": "显示名称",
      "fieldWidth": null,
      "fieldType": "String",
      "searchMode": null,
      "isOrder": null,
      "isSearch": null,
      "dictCode": null,
      "fieldHref": null,
      "isShow": 1,
      "orderNum": 0,
      "replaceVal": null,
      "isTotal": null,
      "groupTitle": null,
      "createBy": null,
      "createTime": null,
      "updateBy": null,
      "updateTime": null
    }
  ],
  "params": [
    {
      "paramName": "paramName",
      "paramTxt": "参数名称",
      "paramValue": "",
      "orderNum": 1
    }
  ]
}

编辑报表 (editAll)

{
  "head": {
    "id": "existing_head_id",
    "code": "report_code",
    "name": "报表名称",
    "cgrSql": "SELECT ... FROM ...",
    "dbSource": ""
  },
  "items": [...],
  "params": [...],
  "deleteItemIds": "item_id1,item_id2",
  "deleteParamIds": "param_id1"
}

字段 ID 生成规则:

  • add 时使用雪花ID格式19位数字字符串"2032681654277947394"
  • 可用 Python 的 str(int(time.time() * 1000) * 1000 + random.randint(0, 999)) 近似生成

6.2 使用 Python 调用 API

重要限制:

  1. Windows 环境下 curl 发送中文/长 JSON 会出错,必须使用 Python
  2. 禁止使用 python3 -c "..." 内联方式
  3. 必须先用 Write 工具写入 .py 临时文件,再用 Bash 执行,最后删除临时文件

完整 Python 脚本模板(已实测验证通过):

以下脚本已在 https://boot3.jeecg.com/jeecgboot 环境成功创建报表2026-03-14 验证)。

import urllib.request
import json
import time
import random
import ssl
import urllib.parse

API_BASE = '{用户提供的后端地址}'
TOKEN = '{用户提供的 X-Access-Token}'

# 忽略SSL验证开发环境
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

def api_request(path, data=None, method=None):
    """发送 API 请求"""
    url = f'{API_BASE}{path}'
    headers = {
        'X-Access-Token': TOKEN,
        'Content-Type': 'application/json; charset=UTF-8'
    }
    if data is not None:
        json_data = json.dumps(data, ensure_ascii=False).encode('utf-8')
        if method is None:
            method = 'POST'
        req = urllib.request.Request(url, data=json_data, headers=headers, method=method)
    else:
        if method is None:
            method = 'GET'
        req = urllib.request.Request(url, headers=headers, method=method)
    resp = urllib.request.urlopen(req, context=ctx)
    return json.loads(resp.read().decode('utf-8'))

def gen_id():
    """生成雪花ID格式的字符串19位数字"""
    return str(int(time.time() * 1000) * 1000000 + random.randint(100000, 999999))

# ====== Step 1: 调用 parseSql 解析字段 ======
sql = "SELECT id, table_name, table_txt, table_type, create_time FROM onl_cgform_head WHERE 1=1"
encoded_sql = urllib.parse.quote(sql, safe='')
parse_result = api_request(f'/online/cgreport/head/parseSql?sql={encoded_sql}')
print('解析结果:', json.dumps(parse_result, ensure_ascii=False, indent=2))

if not parse_result.get('success'):
    print('SQL 解析失败:', parse_result.get('message'))
    exit(1)

# ====== Step 2: 直接构造字段配置(不依赖 parseSql 返回的默认值) ======
# 注意parseSql 返回的 fieldType 全部是 StringAI 需根据语义修正
# 注意parseSql 返回的 fieldTxt 等于 fieldNameAI 需翻译为中文
items = [
    {"id": gen_id(), "cgrheadId": None, "fieldName": "id", "fieldTxt": "ID",
     "fieldWidth": None, "fieldType": "String", "searchMode": None, "isOrder": None,
     "isSearch": None, "dictCode": None, "fieldHref": None, "isShow": 0,
     "orderNum": 0, "replaceVal": None, "isTotal": None, "groupTitle": None,
     "createBy": None, "createTime": None, "updateBy": None, "updateTime": None},
    {"id": gen_id(), "cgrheadId": None, "fieldName": "table_name", "fieldTxt": "表名",
     "fieldWidth": None, "fieldType": "String", "searchMode": "like", "isOrder": None,
     "isSearch": 1, "dictCode": None, "fieldHref": None, "isShow": 1,
     "orderNum": 1, "replaceVal": None, "isTotal": None, "groupTitle": None,
     "createBy": None, "createTime": None, "updateBy": None, "updateTime": None},
    # ... 继续添加其他字段 ...
]

# ====== Step 3: 构造请求 ======
report_data = {
    "head": {
        "code": "onl_cgform_list",
        "name": "Online表单清单",
        "cgrSql": sql,
        "dbSource": ""
    },
    "items": items,
    "params": []
}

# ====== Step 4: 调用 add API 创建报表 ======
result = api_request('/online/cgreport/head/add', report_data)
print('创建结果:', json.dumps(result, ensure_ascii=False, indent=2))

if result.get('success'):
    print('\n报表创建成功!')
    # Step 5: 查询报表 ID 并生成菜单 SQL
    list_result = api_request(f'/online/cgreport/head/list?code=onl_cgform_list')
    if list_result.get('success') and list_result['result']['records']:
        head_id = list_result['result']['records'][0]['id']
        print(f'报表 ID: {head_id}')
        print(f'\n菜单 SQL:')
        print(f"INSERT INTO sys_permission (id, parent_id, name, url, component, component_name, is_route, is_leaf, keep_alive, hidden, hide_tab, description, del_flag, rule_flag, status, internal_or_external, perms_type, sort_no, menu_type, route_redirect) VALUES ('{head_id}', NULL, 'Online表单清单', '/online/cgreport/{head_id}', 'modules/online/cgreport/auto/OnlCgreportAutoMain', NULL, 1, 1, 0, 0, 0, NULL, 0, 0, '1', 0, '0', 1.0, 1, NULL);")
else:
    print('\n创建失败:', result.get('message'))

实测记录2026-03-14

测试场景:创建 Online 表单清单报表,查询 onl_cgform_head

SQL

SELECT id, table_name, table_txt, table_type, table_version, is_tree, is_page, theme_template, create_time, create_by, update_time
FROM onl_cgform_head WHERE 1=1

验证结果

  1. parseSql API 成功解析 11 个字段,所有 fieldType 均返回 String需 AI 修正)
  2. add API 成功创建报表,返回 {"success": true, "message": "添加成功!"}
  3. head/list API 成功查询到报表 ID: 2032684085556592641
  4. 菜单 SQL 生成正确

关键发现

  • parseSql 返回的 orderNum 从 1 开始,但 add 时 items 的 orderNum 从 0 开始也能正常工作
  • 不需要的字段值isSearch/isOrder/dictCode 等)传 null 即可,不需要传空字符串
  • replaceVal 格式 "单表_1,主表_2,附表_3" 可以替代 dictCode 实现值翻译(导出时有效)
  • gen_id() 生成的 19 位数字字符串与前端生成的雪花 ID 格式一致API 接受

Step 7: 生成菜单 SQL可选

报表创建成功后,需要查询报表 ID 来生成菜单 SQL

# 查询刚创建的报表
import urllib.parse
list_result = api_request(f'/online/cgreport/head/list?code={urllib.parse.quote(report_code)}')
if list_result.get('success') and list_result['result']['records']:
    head_id = list_result['result']['records'][0]['id']
    report_name = list_result['result']['records'][0]['name']
    print(f'\n报表 ID: {head_id}')
    print(f'\n### 菜单 SQL可选执行')
    print(f"""
INSERT INTO sys_permission (
  id, parent_id, name, url, component, component_name,
  is_route, is_leaf, keep_alive, hidden, hide_tab, description,
  del_flag, rule_flag, status, internal_or_external,
  perms_type, sort_no, menu_type, route_redirect
) VALUES (
  '{head_id}', NULL, '{report_name}',
  '/online/cgreport/{head_id}',
  'modules/online/cgreport/auto/OnlCgreportAutoMain',
  NULL,
  1, 1, 0, 0, 0, NULL,
  0, 0, '1', 0,
  '0', 1.0, 1, NULL
);
""")

菜单 SQL 字段说明:

字段 说明
id 报表 headId 与报表配置关联
parent_id NULL 一级菜单,也可设为某个父菜单 ID
name 报表名称 菜单显示名
url /online/cgreport/{headId} 路由路径
component modules/online/cgreport/auto/OnlCgreportAutoMain 前端组件
is_route 1 是菜单路由
is_leaf 1 叶子节点
menu_type 1 菜单类型1=菜单)

Step 8: 输出结果

## Online 报表创建成功

- 报表编码:{code}
- 报表名称:{name}
- 字段数量:{N} 个
- 参数数量:{M} 个
- 目标环境:{API_BASE}

### 菜单 SQL
INSERT INTO sys_permission (...) VALUES (...);

### 后续操作
1. 打开 JeecgBoot 后台 → Online报表
2. 找到该报表,点击「功能测试」预览效果
3. 如需配置菜单,执行上方 SQL 或在后台手动添加
4. 可在「编辑」中调整字段显示/查询/排序等配置

高级功能

SQL 参数化查询

支持在 SQL 中使用 Velocity 模板语法的参数:

SELECT * FROM biz_sales
WHERE 1=1
${#if($startDate != '')} AND sale_date >= '$startDate' ${#end}
${#if($endDate != '')} AND sale_date <= '$endDate' ${#end}
${#if($status != '')} AND status = '$status' ${#end}

对应的 params 配置:

[
  {"paramName": "startDate", "paramTxt": "开始日期", "paramValue": "", "orderNum": 1},
  {"paramName": "endDate", "paramTxt": "结束日期", "paramValue": "", "orderNum": 2},
  {"paramName": "status", "paramTxt": "状态", "paramValue": "", "orderNum": 3}
]

动态数据源

如果用户需要查询非默认数据源的数据:

{
  "head": {
    "code": "ext_report",
    "name": "外部数据报表",
    "cgrSql": "SELECT ...",
    "dbSource": "second_db"
  }
}

dbSource 对应 JeecgBoot 后台「数据源管理」中配置的数据源编码。

字段宽度 (fieldWidth)

控制表格列宽(像素值):

{"fieldName": "name", "fieldWidth": 200}
{"fieldName": "description", "fieldWidth": 300}

与其他 Skill 的区别

Skill 产出物 适用场景
jeecg-cgreport Online 报表配置SQL 驱动,只读数据展示) 数据查询报表、统计分析、数据导出
jeecg-online Online 表单配置元数据驱动CRUD 数据录入管理表单
jeecg-codegen Java + Vue3 代码 + SQL 需要自定义业务逻辑的模块
jeecg-desform 设计器表单 JSON 数据采集、审批表单

错误处理

错误 解决方案
Token 过期401/认证失败) 提示用户重新获取 X-Access-Token
报表编码已存在 换一个 code 或使用 editAll 编辑
parseSql 失败 检查 SQL 语法是否正确,表是否存在
SQL注入风险 不要在 SQL 中使用 DROP/DELETE/UPDATE 等危险语句
禁止 select * 如果系统开启了 disableSelectAll需指定具体字段
中文乱码 确认使用 Python urllib不要用 curl