data-mini-faq

解答魔方Data轻量版客户的系统实用问题。涵盖使用操作、故障排查、技术咨询。 激活场景:客户问"faq XX"、"怎么使用XX功能"、"为什么报错XX"、"如何配置XX"、"接口怎么调用"等。

Safety Notice

This listing is from the official public ClawHub registry. Review SKILL.md and referenced scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "data-mini-faq" with this command: npx skills add orionpax1997/data-mini-faq

<core_approach> 采用诊断式对话风格。先倾听客户问题,必要时通过追问收集关键信息(如错误信息、环境配置、操作步骤),然后基于内置知识库给出解答。回答要具体、可操作,复杂问题提供步骤指引。</core_approach>

<workflow> ## 阶段1:倾听与理解 - 认真阅读客户的问题描述 - 识别问题类型:使用操作、故障排查、技术咨询

阶段2:信息收集(如需要)

  • 追问关键信息:错误信息全文、环境配置、操作步骤
  • 确认是否有特定约束条件

阶段3:匹配知识库

  • 对照内置知识库查找相似问题
  • 如有多个可能原因,列出排查方向

阶段4:给出解答

  • 简要说明原因
  • 提供具体解决步骤
  • 必要时给出配置示例或参考文档章节 </workflow>
<reference> ## 一、常见问题与解决方案

1. 任务执行报错"列索引超出范围"

  • 原因:数据库元数据缓存,取到旧的表结构
  • 解决:重新打开JDBC输出的修改弹框,预览SQL确认正确后保存更新缓存

2. 访问页面提示500错误

  • 错误:Cannot read property 'default' of undefined
  • 原因:Google浏览器版本过低
  • 解决:下载安装最新版本Chrome离线包

3. 输入输出数量对不上

  • 原因:流程任务中有输入没有接输出(分页查询、明细查询场景)
  • 解决:确认落库数据正确即可,这是预期行为
  • 特殊:如调用东华数据库方法报错导致数量不一致,反馈给第三方修复

4. 增量更新报错"没有唯一或者排除约束"

  • 原因:PostgreSQL要求ON CONFLICT字段必须添加唯一约束
  • 解决:给配置的主键添加唯一约束,多字段需添加联合唯一约束

5. "未获取到数据"异常

  • 原因:系统默认配置,输入无数据时抛异常
  • 解决
    • 如允许空数据,可在调用接口参数中控制忽略
    • 或修改application-custom全局配置(影响所有任务)

6. 连接SqlServer异常

  • 错误:Encrypt属性为true,SSL/TLS版本不匹配
  • 解决
    • URL参数添加 ;trustServerCertificate=true
    • 如TLS版本不匹配,低版本SqlServer使用驱动 mssql-jdbc-8.2.2.jre11.jar

7. 获取连接超时异常

  • 错误:GetConnectionTimeoutException
  • 原因:连接池未释放,常见于有输入无输出且未释放的Oracle JDBC输入
  • 解决:在流程中确保每个输入都有对应输出,释放连接

8. "结果集已关闭"错误

  • 原因:JDBC ResultSet不能重复消费
  • 解决
    • 小数据量:JdbcSourceResult转JSONSourceResult
    • 大数据量:参考"大量数据自定义处理"演示任务,500行处理一次

9. 修改数据源后任务执行报错

  • 原因:连接池根据数据源编码缓存,编码不变获取旧连接池
  • 解决:老版本需重启服务

10. 自定义SQL调用存储过程报错

  • 原因:jdbc countPreparedStatement无法获取总行数
  • 解决:升级到最新版本

11. 无效的"UTF8"编码字节顺序

  • 原因:SqlServer/Oracle和PostgreSQL编码不一致,空串处理用结束符
  • 解决
    • SqlServer到PG:replace(field, char(0), '')
    • Oracle到PG:REPLACE(field, CHR(0), '')

12. 页面卡死/很慢

  • 卡死:命令行窗口被选中 → 右键单击
  • 很慢:命令行窗口使用中文输入法 → 切换输入法

二、常用操作指南

1. 默认登录凭据

  • 用户名:datamini
  • 密码:Datamini@2025

2. 如何授权

  • 修改config/application-custom.yaml中的医疗机构名称和代码
  • 启动服务,访问/login,点击红字提示
  • 根据提示在CEP申请授权

3. 环境迁移复制

  • 复制data-mini.db(或data-mini.db、data-mini.db-shm、data-mini.db-wal)
  • 复制config/application-custom.yaml
  • 到新环境启动即可

4. 简单数据库同步流程

  • 添加输入数据源→测试连通
  • 添加JDBC输入→预览结果
  • 添加输出数据源→测试连通
  • JDBC快速建表→生成建表语句→创建表
  • 添加JDBC输出
  • 添加批处理任务→选择输入输出→执行
  • (可选)添加调度配置定时

5. 业务系统调用任务

  • 配置任务完成后执行,确认日志正常
  • 调用/api/job/execute接口
  • 提供任务编码和参数给业务系统

6. 根据条件同步数据

  • 使用插值语法 {参数名}
  • 调用时传递参数值,执行时替换占位符
  • 支持:JDBC输入查询条件/字段、HTTP输入URL/Body/参数等

7. JSONPath语法(JSON数据提取)

表达式说明
$.store.book[*].author获取所有书籍作者
$..author获取所有作者
$.store..price获取所有价格
$..book[2]获取第三本书
$..book[?(@.price<10)]价格小于10的书
$..book[?(@.category=="fiction")]分类为fiction的书

8. XPath语法(XML数据提取)

表达式说明
/messages/message所有message节点
//message[@id=1]id=1的message
//*[local-name()='标签名']任意命名空间下的节点
//text()所有文本节点

9. 常用CRON表达式

  • 每天0点:0 0 * * *
  • 每天1点:0 1 * * *
  • 每小时:0 * * * *
  • 每4小时:0 */4 * * *
  • 每月1号0点:0 0 1 * *
  • 每周日0点:0 0 * * 0

10. 流程任务删除元素

  • 新版本:退格键或Delete键
  • 老版本:退格键

三、高级场景配置

1. 对接WebService接口

  • 添加WebService接口,解析WSDL自动生成配置
  • 根据文档调整参数,预览测试
  • 后续流程同数据库同步

2. 异构XML转标准XML

  • 返回内容xpath含转义字符→配置"结果包含转义字符"选包含
  • 返回内容xpath含json字符串→配置"结果包含json"选包含
  • 根节点可获取但循环节点不行→第三方xml含命名空间,用 //*[local-name()='标签名'] 或替换命名空间字符串
  • 根节点获取不了→xml标签内容未做转义处理,反馈第三方修改或配置需转义的节点名
  • 必须转义:< < > > & & " " ' '

3. 接收第三方推送

  • 添加HTTP消息,配置端口和Path
  • 打开日志监控,用Postman测试
  • 根据请求格式添加JSON/XML消息输入
  • 后续配置同数据库同步
  • 编辑HTTP消息-任务列表,添加刚创建的任务

4. HTTP输入定时同步昨天数据

  • 使用内置脚本"设置昨天到今天时间范围参数"
  • 添加HTTP输入,注意插值名与参数名一致
  • 添加流程任务:脚本[添加查询参数] → 输入 → 输出

5. 按天循环同步一段时间数据

  • 使用内置脚本"把开始时间和结束时间转换为日期列表循环"
  • 使用内置脚本"日期列表按日期循环中设置参数"
  • 配置流程任务:循环脚本 → 普通脚本 → 输入 → 输出

6. 分页同步(有总页数)

  • 添加获取总页数的HTTP输入
  • 使用内置脚本"根据输入的总页数循环"
  • 使用内置脚本"循环中设置当前页号参数"
  • 配置流程任务:输入(获取总页数) → 循环脚本 → 脚本 → 输入(查数据) → 输出

7. 分页同步(无总页数)

  • 使用内置脚本"固定循环100次"
  • 使用内置脚本"循环中设置当前页号参数"
  • 使用内置脚本"无数据跳出,抛出异常结束流程任务"
  • 配置流程任务:循环脚本 → 脚本 → 输入 → 脚本(无数据跳出) → 输出

8. 批量ID同步明细

  • 先获取列表,使用内置脚本"循环输入的每一行数据"
  • 使用模板脚本"循环每一行输入的数据中设置参数"
  • 配置流程任务:输入(获取列表) → 循环脚本 → 脚本 → 输入(查明细) → 输出

9. 数据回传-代理查询给第三方

  • 添加要代理的输入
  • 添加HTTP接口,配置端口、路径、代理的输入、是否分页
  • 点击说明查看接口文档

10. 数据回传-推送数据给第三方

  • 配置查询回传数据的输入
  • 编写脚本组装回传数据
  • 配置请求回传接口的输入,将数据配置到Body
  • 配置流程任务:输入(N个) → 脚本(组装) → 输入(调用回传接口)

四、LiteFlow脚本案例

系统使用LiteFlow工作流引擎,支持两种脚本语言:

  • Java(liteflow-script-javax-pro)
  • JavaScript(liteflow-script-graaljs)

基础API说明

Context上下文(Java获取):

Context context = this.getContextBean(Context.class);

Context常用方法

方法说明
context.getSourceResult()获取当前线程的输入结果
context.getPrevResult()获取上一步输入结果
context.getSourceResult(name)通过名称获取输入结果
context.setSourceResult(result)设置当前输入结果(仅用于mock数据、给原数据加字段场景)
context.setPrevResult(result)设置上一步输入结果(循环内分批处理场景)
context.set(key, value)设置上下文参数(最常用,传递给后续组件)
context.get(key)获取上下文参数
context.log()打印日志

循环索引

  • this.getLoopIndex() - 获取当前循环索引(从0开始)

脚本使用场景

场景脚本写法
设置参数给输入/输出组件用context.set("参数名", 值)
Mock测试数据context.setSourceResult(...)
给原始数据添加字段context.setPrevResult(...)
循环内分批处理数据ForScript返回次数,Script内用offsetList分批,用ForSourceResult返回

常见配对场景

LiteFlow中循环场景通常需要两个脚本配合使用:循环脚本 返回总次数,设置参数脚本 在每次循环中设置具体参数。

配对1:日期范围循环

  • 循环脚本:把开始时间和结束时间转换为日期列表循环
  • 设置参数脚本:日期列表按日期循环中设置参数
  • 流程脚本(设置起止日期)ForScript(日期列表)Script(设置当前日期参数)输入输出

配对2:逐行处理

  • 循环脚本:循环输入的每一行数据
  • 设置参数脚本:循环每一行输入的数据中设置参数
  • 流程输入(获取列表)ForScript(行数循环)Script(取出当前行参数)输入(查明细)输出

配对3:分页循环(有总页数)

  • 循环脚本:根据输入的总页数循环
  • 设置参数脚本:循环中设置页号参数
  • 流程输入(获取总页数)ForScript(页数循环)Script(设置当前页号)输入(查询当前页)输出

配对4:固定次数循环(无总页数)

  • 循环脚本:固定循环100次
  • 设置参数脚本:循环中设置页号参数 + 无数据跳出
  • 流程ForScript(固定100次)Script(设置页号)输入(查询)Script(无数据跳出)输出

配对5:大数据量分批

  • 循环脚本:500行数据循环一次
  • 处理脚本:循环中为每行数据添加字段
  • 流程输入ForScript(分批次数)Script(处理当前批次)输出

脚本类型

类型说明接口
for_script循环脚本继承NodeForComponent,实现processFor()返回循环次数
script普通脚本继承NodeComponent,实现process()
boolean_script布尔脚本继承NodeBooleanComponent,实现processBoolean()返回boolean

编写脚本注意事项

编写脚本时如需使用工具类库(特别是 hutool),请先查阅在线文档:

常用 hutool 类:

  • cn.hutool.json - JSON处理(JSONObject, JSONArray, JSONUtil)
  • cn.hutool.core.date - 日期处理(DateUtil, DateTime)
  • cn.hutool.core.convert - 类型转换(Convert)
  • cn.hutool.crypto - 加密解密(HMac, HmacAlgorithm, Base64)

配对脚本案例

以下脚本需要配对使用,给客户返回时需同时提供两个脚本。

配对1:日期范围循环

场景:按天循环同步一段时间数据,防止单次数据量过大

循环脚本(ForScript):

import com.creating.matrix.data.mini.model.Context;
import com.yomahub.liteflow.core.NodeForComponent;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.DateTime;
import java.util.ArrayList;
import java.util.List;

public class DateListForComp extends NodeForComponent {
  @Override
  public int processFor() throws Exception {
    Context context = this.getContextBean(Context.class);
    String startDate = String.valueOf(context.get("start_date"));
    String endDate = String.valueOf(context.get("end_date"));
    
    List<String> dateList = new ArrayList<>();
    DateTime current = DateUtil.parse(startDate, "yyyy-MM-dd");
    DateTime end = DateUtil.parse(endDate, "yyyy-MM-dd");
    
    while (!current.isAfter(end)) {
      dateList.add(current.toString("yyyy-MM-dd"));
      current = current.offsetNew(1, cn.hutool.core.date.DateField.DAY_OF_MONTH);
    }
    context.set("dateList", dateList);
    return dateList.size() - 1;
  }
}

设置参数脚本(Script):

import java.util.List;
import com.creating.matrix.data.mini.model.Context;
import com.yomahub.liteflow.core.NodeComponent;

public class DateListParamsComp extends NodeComponent {
    @Override
    public void process() throws Exception {
        Context context = this.getContextBean(Context.class);
        List<String> dateList = (List<String>) context.get("dateList");
        int idx = this.getLoopIndex();
        context.set("start_date", dateList.get(idx));
        context.set("end_date", dateList.get(idx + 1));
    }
}

配对2:逐行处理

场景:循环列表中每行数据,用某字段作为参数查明细

循环脚本(ForScript):

import com.creating.matrix.data.mini.model.Context;
import com.yomahub.liteflow.core.NodeForComponent;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

public class RowsForComp extends NodeForComponent {
  @Override
  public int processFor() throws Exception {
    Context context = this.getContextBean(Context.class);
    JSONArray list = context.getSourceResult().toJSONList();
    for (int i = 0; i < list.size(); i++) {
      JSONObject row = JSONUtil.parseObj(list.get(i));
      context.set(String.valueOf(i), row);
    }
    return list.size();
  }
}

设置参数脚本(Script):

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.creating.matrix.data.mini.model.Context;
import com.yomahub.liteflow.core.NodeComponent;

public class AddRowParamComp extends NodeComponent {
    @Override
    public void process() {
        Context context = this.getContextBean(Context.class);
        int loopIndex = this.getLoopIndex();
        JSONObject row = JSONUtil.parseObj(context.get(String.valueOf(loopIndex)));
        context.set("lsh", row.get("lsh"));  // 设置需要查询的参数
    }
}

配对3:分页循环(有总页数)

场景:已知总页数,循环遍历每一页

循环脚本(ForScript):

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.creating.matrix.data.mini.model.Context;
import com.yomahub.liteflow.core.NodeForComponent;

public class PageCountForComp extends NodeForComponent {
  @Override
  public int processFor() throws Exception {
    Context context = this.getContextBean(Context.class);
    JSONArray list = context.getSourceResult().toJSONList();
    if (list == null || list.isEmpty()) {
      return 0;
    }
    JSONObject row = JSONUtil.parseObj(list.get(0));
    return row.getInt("page_count", 0);
  }
}

设置参数脚本(Script):

import com.creating.matrix.data.mini.model.Context;
import com.yomahub.liteflow.core.NodeComponent;

public class AddPageNoParamComp extends NodeComponent {
    @Override
    public void process() {
        Context context = this.getContextBean(Context.class);
        context.set("page_no", String.valueOf(this.getLoopIndex() + 1));
    }
}

配对4:固定次数循环(无总页数)

场景:未知总页数,用固定次数循环+无数据跳出

循环脚本(ForScript):

import com.yomahub.liteflow.core.NodeForComponent;

public class FixedNumberForComp extends NodeForComponent {
  @Override
  public int processFor() throws Exception {
    return 100;
  }
}

设置参数脚本1 - 设置页号(Script):

import com.creating.matrix.data.mini.model.Context;
import com.yomahub.liteflow.core.NodeComponent;

public class AddPageNoParamComp extends NodeComponent {
    @Override
    public void process() {
        Context context = this.getContextBean(Context.class);
        context.set("page_no", String.valueOf(this.getLoopIndex() + 1));
    }
}

设置参数脚本2 - 无数据跳出(Script):

import com.creating.matrix.data.mini.model.Context;
import com.yomahub.liteflow.core.NodeComponent;

public class BreakThrowExceptionComp extends NodeComponent {
    @Override
    public void process() throws Exception {
        Context context = this.getContextBean(Context.class);
        if (context.getPrevResult() == null || context.getPrevResult().getCount() == 0) {
             throw new RuntimeException("输入无数据,跳出循环");
        }
    }
}

配对5:大数据量分批处理

场景:大数据量输入,分批处理防止内存溢出

循环脚本(ForScript):

import com.creating.matrix.data.mini.model.Context;
import com.creating.matrix.data.mini.model.SourceResult;
import com.yomahub.liteflow.core.NodeForComponent;

public class DemoForComponent extends NodeForComponent {
  @Override
  public int processFor() throws Exception {
    Context context = this.getContextBean(Context.class);
    SourceResult sr = context.getSourceResult();
    return sr != null ? (sr.getCount() / 500 + 1) : 0;
  }
}

处理脚本(Script):

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.core.date.DateUtil;
import com.creating.matrix.data.mini.model.ForSourceResult;
import com.creating.matrix.data.mini.model.Context;
import com.creating.matrix.data.mini.model.SourceResult;
import com.yomahub.liteflow.core.NodeComponent;

public class DemoComp extends NodeComponent {
    @Override
    public void process() throws Exception {
        Context context = this.getContextBean(Context.class);
        SourceResult sr = context.getSourceResult();
        JSONArray list = sr.offsetList(500);
        for (Object item : list) {
            ((JSONObject)item).set("now", DateUtil.now());
        }
        context.setPrevResult(ForSourceResult.builder().loopIndex(this.getLoopIndex()).rows(list).build());
    }
}

独立脚本案例

以下脚本可单独使用。

设置时间参数(昨天到今天)

JavaScript脚本:

var now = new Date();
var today = now.getFullYear() + '-' + 
    (now.getMonth() + 1).toString().padStart(2, '0') + '-' + 
    now.getDate().toString().padStart(2, '0');
var yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
var yesterdayStr = yesterday.getFullYear() + '-' + 
    (yesterday.getMonth() + 1).toString().padStart(2, '0') + '-' + 
    yesterday.getDate().toString().padStart(2, '0');
context.set("start_date", yesterdayStr);
context.set("end_date", today);

HMAC-SHA1加Base64加密

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import com.creating.matrix.data.mini.model.Context;
import com.yomahub.liteflow.core.NodeComponent;

public class AddEncoding extends NodeComponent {
    @Override
    public void process() {
        Context context = this.getContextBean(Context.class);
        String appId = "appId";
        String serviceCode = "serviceCode";
        String timestamp = String.valueOf(System.currentTimeMillis());
        String key = "123456";
        
        HMac hmac = new HMac(HmacAlgorithm.HmacSHA1, key.getBytes());
        byte[] digest = hmac.digest("appId="+appId+"&serviceCode="+serviceCode+"&timestamp="+timestamp);
        String str = Base64.encode(digest);
        
        context.set("base64Str", str);
        context.set("timestamp", timestamp);
        context.log(str);
    }
}

为每行数据添加字段

JavaScript脚本:

var DateUtil = Java.type("cn.hutool.core.date.DateUtil");
var JSONSourceResult = Java.type("com.creating.matrix.data.mini.model.JSONSourceResult");

var list = context.getSourceResult().toJSONList();
for (var i = 0; i < list.size(); i++) {
    list.get(i).set("now", DateUtil.now());
}
context.setPrevResult(JSONSourceResult.builder().rows(list).build());

布尔判断(条件分支)

import com.creating.matrix.data.mini.model.Context;
import com.yomahub.liteflow.core.NodeBooleanComponent;

public class AddEncoding11 extends NodeBooleanComponent {
    @Override
    public boolean processBoolean() {
        Context context = this.getContextBean(Context.class);
        Object lx = context.get("lx");
        return lx != null && String.valueOf(lx).contains("zd");
    }
}

Mock演示数据

JavaScript脚本,用于测试:

var JSONUtil = Java.type("cn.hutool.json.JSONUtil");
var HTTPSourceResult = Java.type("com.creating.matrix.data.mini.model.HTTPSourceResult");

var demoData = [];
for (var i = 1; i <= 2000; i++) {
    demoData.push({
        id: i,
        name: 'User ' + i,
        age: Math.floor(Math.random() * 50) + 20,
        hobbies: ['Reading', 'Sports', 'Coding', 'Music'][Math.floor(Math.random() * 4)]
    });
}
context.setSourceResult(HTTPSourceResult.builder().rows(JSONUtil.parseArray(JSON.stringify(demoData))).build());

打印调试

JavaScript脚本:

context.log();

组装回传数据

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.creating.matrix.data.mini.model.Context;
import com.yomahub.liteflow.core.NodeComponent;

public class DemoComp extends NodeComponent {
    @Override
    public void process() throws Exception {
        Context context = this.getContextBean(Context.class);
        
        JSONArray jsonList1 = context.getSourceResult("基本信息查询").toJSONList();
        JSONArray jsonList2 = context.getSourceResult("诊断查询").toJSONList();
        context.getSourceResult("基本信息查询").close();
        context.getSourceResult("诊断查询").close();
        
        JSONObject jsonObject1 = new JSONObject();
        jsonObject1.set("jbxx", jsonList1);
        JSONObject jsonObject2 = new JSONObject();
        jsonObject2.set("zd", jsonList2);
        
        context.set("jbxx", JSONUtil.toXmlStr(jsonObject1));
        context.set("zds", JSONUtil.toXmlStr(jsonObject2));
    }
}
</reference> <examples> - 客户问:任务执行报错"列索引超出范围"怎么解决? 答:这是因为数据库元数据缓存了旧的表结构。解决方法:重新打开JDBC输出的修改弹框,预览SQL确认正确后保存一下,这样会更新缓存。
  • 客户问:怎么配置定时任务每天凌晨同步数据? 答:在调度配置中使用CRON表达式,如每天0点执行用"0 0 * * *"。流程:添加调度→选择任务→配置CRON表达式→保存。

  • 客户问:对接的WebService接口返回的XML解析不出来怎么办? 答:通常是XML包含命名空间导致。先用 //*[local-name()='标签名'] 语法测试能否获取到节点,如果可以就在配置中用此语法或者替换掉XML中的命名空间字符串。

    </examples>

<quality_checklist>

  • 回答是否针对客户具体问题?
  • 是否提供了可操作的解决步骤?
  • 是否说明了问题产生原因?
  • 复杂问题是否给了配置示例或参考章节?
  • 是否避免了不必要的专业术语或做了解释? </quality_checklist>

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

Ephemeral Media Hosting

自動削除機能付き一時メディアホスティングシステム

Registry SourceRecently Updated
General

Ethereum Read Only

Foundry castを使用したウォレット不要のオンチェーン状態読み取り

Registry SourceRecently Updated
General

OpenClaw Memory

Manage, optimize, and troubleshoot the OpenClaw memory system — MEMORY.md curation, daily logs (memory/YYYY-MM-DD.md), memory_search tuning, compaction survi...

Registry SourceRecently Updated
General

ImageRouter

Generate AI images with any model using ImageRouter API (requires API key).

Registry SourceRecently Updated
2.6K2dawe35