diff --git a/src/main/java/com/xly/config/AgentFactory.java b/src/main/java/com/xly/config/AgentFactory.java index bfb5a8c..4b5cf1e 100644 --- a/src/main/java/com/xly/config/AgentFactory.java +++ b/src/main/java/com/xly/config/AgentFactory.java @@ -103,7 +103,7 @@ public class AgentFactory { skillTool, interactionTool, new ErpReadTool(erp, jdbc, resolver, identity), - new ProposeWriteTool(erp, jdbc, ops, mapper, identity), + new ProposeWriteTool(erp, jdbc, ops, mapper, identity, resolver), new QueryTool(sqlModel, jdbc, audit, identity), new FormCollectTool(erp, jdbc, resolver, identity, mapper)) .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder() diff --git a/src/main/java/com/xly/service/ErpClient.java b/src/main/java/com/xly/service/ErpClient.java index 81b4204..f31eaa8 100644 --- a/src/main/java/com/xly/service/ErpClient.java +++ b/src/main/java/com/xly/service/ErpClient.java @@ -279,31 +279,31 @@ public class ErpClient { } } - /** 新增一条记录(addBusinessData,column 为字段 map)。会话过期自动重登重试。 */ - public JsonNode createForm(String table, Map columns) { - return createForm(null, table, columns); - } - - public JsonNode createForm(String authToken, String table, Map columns) { - JsonNode root = doCreate(table, columns, resolveToken(authToken)); + /** + * 新增一条记录。走 {@code addUpdateDelBusinessData?sModelsId=}(handleType=add)——与 ERP 前端一致, + * 复用校验/单号/租户;{@code columns} 里已由 ProposeWrite 备好 sId/sFormId/sBillNo 与类型化字段值。 + * 会话过期自动重登重试(仅 dev-login)。 + */ + public JsonNode createForm(String authToken, String moduleId, String table, Map columns) { + JsonNode root = doCreate(moduleId, table, columns, resolveToken(authToken)); if (root.path("code").asInt() == -2 && canRelogin(authToken)) { login(); - root = doCreate(table, columns, resolveToken(authToken)); + root = doCreate(moduleId, table, columns, resolveToken(authToken)); } return root; } - private JsonNode doCreate(String table, Map columns, String tok) { + private JsonNode doCreate(String moduleId, String table, Map columns, String tok) { try { - String url = baseUrl + "/business/addBusinessData"; + String url = baseUrl + "/business/addUpdateDelBusinessData?sModelsId=" + moduleId; + Map col = new LinkedHashMap<>(columns); + col.put("handleType", "add"); Map dataItem = new LinkedHashMap<>(); dataItem.put("sTable", table); - dataItem.put("column", columns); + dataItem.put("name", "master"); + dataItem.put("column", List.of(col)); Map body = new LinkedHashMap<>(); - body.put("sMakePerson", username); - body.put("sBrandsId", brand); - body.put("sSubsidiaryId", subsidiary); - body.put("sLanguage", "sChinese"); + body.put("sModelsId", moduleId); body.put("data", List.of(dataItem)); HttpRequest req = HttpRequest.newBuilder(URI.create(url)) .header("Content-Type", "application/json;charset=UTF-8") diff --git a/src/main/java/com/xly/service/FormResolverService.java b/src/main/java/com/xly/service/FormResolverService.java index 8c9f789..2fd9585 100644 --- a/src/main/java/com/xly/service/FormResolverService.java +++ b/src/main/java/com/xly/service/FormResolverService.java @@ -3,9 +3,16 @@ package com.xly.service; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * 表单 / 字段解析(KgSearch 内核)—— 把「实体类型 / 字段中文名」解析到具体的 @@ -36,6 +43,10 @@ public class FormResolverService { "SELECT af.sFormId, af.sModuleId, af.sDataSource, af.sFormTitle FROM viw_ai_useful_forms af " + "LEFT JOIN viw_kg_form f ON f.sFormId = af.sFormId " + "WHERE af.sFormTitle LIKE ? AND af.sExecType='table' AND af.sDataSource NOT LIKE 'viw%' " + + // 排除明显的从属/参数表(颜色表/控制表/从表/多数量/配置),避免误定位到非主表 + "AND af.sDataSource NOT LIKE '%param' AND af.sDataSource NOT LIKE '%slave' " + + "AND af.sDataSource NOT LIKE '%control' AND af.sDataSource NOT LIKE '%config' " + + "AND af.sDataSource NOT LIKE '%manyqtys' AND af.sDataSource NOT LIKE '%color%' " + "ORDER BY COALESCE(f.bAiTool,0) DESC, " + "(COALESCE(f.iUpstream,0)+COALESCE(f.iDownstream,0)) DESC, CHAR_LENGTH(af.sFormTitle) ASC LIMIT 1", "%" + entityKeyword.trim() + "%"); @@ -85,6 +96,199 @@ public class FormResolverService { return f; } + // ERP 会自动注入 / 系统管理、不该让用户填的列(新增时排除)。 + private static final Set SYS_EXACT = Set.of( + "sId", "sBrandsId", "sSubsidiaryId", "sMakePerson", "sFormId", "sBillNo", "iIncrement", "iOrder", + "tCreateDate", "tMakeDate", "tUpdateDate", "sModelsId", "sToken", "sParentId"); + private static final Pattern NUM = Pattern.compile("-?\\d+(\\.\\d+)?"); + + /** 是否为系统/审计/计算列(新增表单不呈现、payload 不让 LLM 直填)。 */ + public boolean isSystemColumn(String col) { + if (col == null || col.isBlank()) { + return true; + } + if (SYS_EXACT.contains(col)) { + return true; + } + // 布尔标志 bXxx / 日期 tXxx / 金额·折扣·比率计算列 / 各种经办人 + if (col.length() > 1 && (col.charAt(0) == 'b' || col.charAt(0) == 't') && Character.isUpperCase(col.charAt(1))) { + return true; + } + if (col.contains("Money") || col.contains("Rate") || col.contains("Discount") || col.endsWith("Person") + || col.contains("Reserve") || col.contains("Manual")) { + return true; + } + return false; + } + + /** + * 复杂旗舰表单的**人工策展录入字段**(label→真实可写列 + 外键)。报价主表字段名与显示别名不一致 + * (表单里 sProductName/dProductQty 并非真实列),且核心列的 iFormUses 很低,纯靠字段字典排不出来—— + * 故对报价主表策展这组真实可写列(与实测能成功写入的一致)。其它表返回 null,走通用字段字典逻辑。 + */ + private List> curatedFields(String table) { + if (table == null) { + return null; + } + if (table.equalsIgnoreCase("quoquotationmaster")) { + List> l = new ArrayList<>(); + l.add(bf("sCustomerId", "客户名称", "elecustomer")); + l.add(bf("sProductId", "产品名称", "eleproduct")); + l.add(bf("dQty", "数量", null)); + l.add(bf("dLength", "长(L)", null)); + l.add(bf("dWidth", "宽(W)", null)); + l.add(bf("dHeight", "高(D)", null)); + l.add(bf("dPrice", "单价", null)); + return l; + } + return null; + } + + private Map bf(String col, String label, String fk) { + Map m = new LinkedHashMap<>(); + m.put("col", col); + m.put("label", label); + if (fk != null) { + m.put("fk", fk); + } + return m; + } + + /** 表列 -> MySQL 数据类型(information_schema)。 */ + public Map columnTypes(String table) { + Map m = new HashMap<>(); + try { + for (Map r : jdbc.queryForList( + "SELECT COLUMN_NAME c, DATA_TYPE d FROM information_schema.COLUMNS " + + "WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?", table)) { + m.put(String.valueOf(r.get("c")), String.valueOf(r.get("d")).toLowerCase()); + } + } catch (Exception ignore) { + } + return m; + } + + /** 按列类型把用户给的字符串强转成合法值:数值取数字、日期非法则丢弃(交 ERP 默认)、bit→0/1、其余原样。 */ + public Object coerce(String dataType, String v) { + if (v == null) { + return null; + } + String t = dataType == null ? "varchar" : dataType.toLowerCase(); + if (t.contains("int") || t.equals("decimal") || t.contains("double") || t.contains("float") || t.contains("numeric")) { + Matcher mm = NUM.matcher(v); + return mm.find() ? mm.group() : "0"; + } + if (t.contains("date") || t.contains("time")) { + return v.matches("\\d{4}-\\d{2}-\\d{2}.*") ? v : null; // 非日期 → null,让 ERP 用默认值 + } + if (t.equals("bit") || t.equals("tinyint")) { + return (v.equals("1") || v.equalsIgnoreCase("true") || v.contains("是")) ? 1 : 0; + } + return v; + } + + /** 类型默认值(自动补 NOT-NULL 无默认列用)。 */ + public Object typeDefault(String col, String dataType) { + String t = dataType == null ? "varchar" : dataType.toLowerCase(); + if (t.contains("int") || t.equals("decimal") || t.contains("double") || t.contains("float") || t.contains("numeric")) { + return 0; + } + return ""; // date/time 交 ERP 默认,不主动补 + } + + /** + * 某实体主表的**业务录入字段**(字段字典里有中文名、且非系统/计算列),按使用度排序,最多 limit 个。 + * 这是 collectForm 呈现给用户填的字段来源——比 gdsconfigformslave 的界面布局字段更贴近"要填的业务参数"。 + * FK 列(有 sFkTable)标记 fk,前端让用户填名称、写入时解析成 id。 + */ + public List> businessFields(String table, int limit) { + List> curated = curatedFields(table); + if (curated != null) { + return curated; + } + List> out = new ArrayList<>(); + try { + List> rows = jdbc.queryForList( + "SELECT sField, " + + // 标签取非Id、更短的中文名(客户 优先于 客户Id) + "SUBSTRING_INDEX(GROUP_CONCAT(sChinese ORDER BY (sChinese LIKE '%Id') ASC, CHAR_LENGTH(sChinese) ASC SEPARATOR 0x1f),0x1f,1) zh, " + + "MAX(sFkTable) fk, SUM(iFormUses) u FROM viw_kg_field_dict " + + "WHERE sTable=? AND CHAR_LENGTH(sChinese)>=2 GROUP BY sField ORDER BY u DESC", table); + for (Map r : rows) { + String col = String.valueOf(r.get("sField")); + if (isSystemColumn(col)) { + continue; + } + Map f = new LinkedHashMap<>(); + f.put("col", col); + f.put("label", r.get("zh")); + Object fk = r.get("fk"); + if (fk != null && !"null".equalsIgnoreCase(String.valueOf(fk)) && !String.valueOf(fk).isBlank()) { + f.put("fk", String.valueOf(fk)); + } + out.add(f); + if (out.size() >= limit) { + break; + } + } + } catch (Exception ignore) { + } + return out; + } + + /** 外键名称 -> id:在外键表按名称字段模糊匹配(元数据来源可信,非用户拼 SQL)。 */ + public String resolveFk(String fkTable, String name) { + if (fkTable == null || fkTable.isBlank() || name == null || name.isBlank()) { + return null; + } + String nameField = resolveNameField(fkTable); + if (nameField == null) { + return null; + } + return queryOne("SELECT sId FROM `" + fkTable + "` WHERE `" + nameField + "` LIKE ? " + + "ORDER BY CHAR_LENGTH(`" + nameField + "`) ASC LIMIT 1", "%" + name.trim() + "%"); + } + + /** + * 若该表有 NOT-NULL 的 sBillNo 列,按现有单号规律生成下一个单号;否则返回 null。 + * 规律 = 现有最新单号的前缀(字母)+ 年月(YYYYMM) + 递增序号(3位),如 BJD202607082。 + */ + public String nextBillNo(String table, String brand) { + try { + Integer has = jdbc.queryForObject( + "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() " + + "AND TABLE_NAME=? AND COLUMN_NAME='sBillNo' AND IS_NULLABLE='NO'", Integer.class, table); + if (has == null || has == 0) { + return null; + } + String recent = queryOne("SELECT sBillNo FROM `" + table + "` WHERE sBrandsId=? AND IFNULL(sBillNo,'')<>'' " + + "ORDER BY tCreateDate DESC LIMIT 1", brand); + String prefix = "AI"; + if (recent != null && !recent.isBlank()) { + int i = 0; + while (i < recent.length() && !Character.isDigit(recent.charAt(i))) { + i++; + } + if (i > 0) { + prefix = recent.substring(0, i); + } + } + String ym = new SimpleDateFormat("yyyyMM").format(new Date()); + Integer seq; + try { + seq = jdbc.queryForObject( + "SELECT IFNULL(MAX(CAST(SUBSTRING(sBillNo, ?) AS UNSIGNED)),0)+1 FROM `" + table + "` " + + "WHERE sBrandsId=? AND sBillNo LIKE ?", + Integer.class, prefix.length() + ym.length() + 1, brand, prefix + ym + "%"); + } catch (Exception e) { + seq = 1; + } + return prefix + ym + String.format("%03d", seq == null ? 1 : seq); + } catch (Exception e) { + return null; + } + } + private String queryOne(String sql, Object... args) { try { List> r = jdbc.queryForList(sql, args); diff --git a/src/main/java/com/xly/tool/FormCollectTool.java b/src/main/java/com/xly/tool/FormCollectTool.java index 3fc89f5..4c8f1e1 100644 --- a/src/main/java/com/xly/tool/FormCollectTool.java +++ b/src/main/java/com/xly/tool/FormCollectTool.java @@ -58,42 +58,62 @@ public class FormCollectTool { return err("你没有新建「" + entityKeyword + "」的权限。"); } - List> cols; - try { - cols = jdbc.queryForList( - "SELECT sName, sChinese, sControlName, bNotEmpty, sDefault, sChineseDropDown " + - "FROM gdsconfigformslave WHERE sParentId=? AND bVisible=1 AND IFNULL(sName,'')<>'' " + - "AND IFNULL(bReadonly,0)=0 ORDER BY iOrder LIMIT " + MAX_FIELDS, formId); - } catch (Exception e) { - return err("读取表单结构失败:" + e.getMessage()); - } - if (cols.isEmpty()) { - return err("该表单没有可填写的字段配置。"); - } - + String table = String.valueOf(form.get("sDataSource")); List> fields = new ArrayList<>(); - for (Map c : cols) { - String name = str(c.get("sName")); - if (name == null || name.isBlank()) { + + // 主字段来源 = 该表的**业务录入字段**(字段字典,排除系统/审计/计算列)——比 gdsconfigformslave 的 + // 界面布局字段更贴近"要填的业务参数"(报价的 客户/产品/数量/尺寸/单价,而不是 制单人/单据日期)。 + List> biz = resolver.businessFields(table, MAX_FIELDS); + for (Map b : biz) { + String col = str(b.get("col")); + if (col == null || col.isBlank()) { continue; } Map f = new LinkedHashMap<>(); - f.put("name", name); - f.put("label", firstNonBlank(str(c.get("sChinese")), name)); - f.put("control", firstNonBlank(str(c.get("sControlName")), "text")); - f.put("required", truthy(c.get("bNotEmpty"))); - String def = str(c.get("sDefault")); - if (def != null && !def.isBlank()) { - f.put("default", def); - } - List opts = simpleOptions(str(c.get("sChineseDropDown"))); - if (!opts.isEmpty()) { - f.put("options", opts); + f.put("name", col); + f.put("label", firstNonBlank(str(b.get("label")), col)); + f.put("control", "text"); + f.put("required", false); + Object fk = b.get("fk"); + if (fk != null && !String.valueOf(fk).isBlank()) { + f.put("fk", true); // 外键字段:让用户填名称,写入时解析成 id } fields.add(f); } + + // 兜底:字段字典没有该表映射时,退回 gdsconfigformslave 的非只读可见字段(排除系统列)。 + if (fields.isEmpty()) { + try { + List> cols = jdbc.queryForList( + "SELECT sName, sChinese, sControlName, bNotEmpty, sDefault, sChineseDropDown " + + "FROM gdsconfigformslave WHERE sParentId=? AND bVisible=1 AND IFNULL(sName,'')<>'' " + + "AND IFNULL(bReadonly,0)=0 ORDER BY iOrder LIMIT " + MAX_FIELDS, formId); + for (Map c : cols) { + String name = str(c.get("sName")); + if (name == null || name.isBlank() || resolver.isSystemColumn(name)) { + continue; + } + Map f = new LinkedHashMap<>(); + f.put("name", name); + f.put("label", firstNonBlank(str(c.get("sChinese")), name)); + f.put("control", firstNonBlank(str(c.get("sControlName")), "text")); + f.put("required", truthy(c.get("bNotEmpty"))); + String def = str(c.get("sDefault")); + if (def != null && !def.isBlank()) { + f.put("default", def); + } + List opts = simpleOptions(str(c.get("sChineseDropDown"))); + if (!opts.isEmpty()) { + f.put("options", opts); + } + fields.add(f); + } + } catch (Exception e) { + return err("读取表单结构失败:" + e.getMessage()); + } + } if (fields.isEmpty()) { - return err("该表单没有可填写的字段。"); + return err("该表单没有可填写的业务字段。"); } Map out = new LinkedHashMap<>(); diff --git a/src/main/java/com/xly/tool/ProposeWriteTool.java b/src/main/java/com/xly/tool/ProposeWriteTool.java index 0bf50dd..b3b16b1 100644 --- a/src/main/java/com/xly/tool/ProposeWriteTool.java +++ b/src/main/java/com/xly/tool/ProposeWriteTool.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.xly.agent.AgentIdentity; import com.xly.service.ErpClient; +import com.xly.service.FormResolverService; import com.xly.service.OpService; import dev.langchain4j.agent.tool.P; import dev.langchain4j.agent.tool.Tool; @@ -30,13 +31,16 @@ public class ProposeWriteTool { private final OpService ops; private final ObjectMapper mapper; private final AgentIdentity identity; + private final FormResolverService resolver; - public ProposeWriteTool(ErpClient erp, JdbcTemplate jdbc, OpService ops, ObjectMapper mapper, AgentIdentity identity) { + public ProposeWriteTool(ErpClient erp, JdbcTemplate jdbc, OpService ops, ObjectMapper mapper, + AgentIdentity identity, FormResolverService resolver) { this.erp = erp; this.jdbc = jdbc; this.ops = ops; this.mapper = mapper; this.identity = identity; + this.resolver = resolver; } @Tool("提议修改某条现有记录的某个字段(写操作)。本工具**只提议并暂存、绝不立即执行**——" @@ -208,6 +212,12 @@ public class ProposeWriteTool { return err("你没有新增「" + entityKeyword + "」的权限。"); } + Map types = resolver.columnTypes(table); + // 权威 label -> {col, fk} 映射(与 collectForm 同源:策展/字段字典),保证前端标签能被正确回映射 + Map> labelMap = new LinkedHashMap<>(); + for (Map bfm : resolver.businessFields(table, 40)) { + labelMap.put(String.valueOf(bfm.get("label")), bfm); + } Map col = new LinkedHashMap<>(); List descParts = new ArrayList<>(); try { @@ -218,39 +228,58 @@ public class ProposeWriteTool { Map.Entry e = it.next(); String zh = e.getKey(); String v = e.getValue().asText(""); - String colName = queryOne( - "SELECT sField FROM viw_kg_field_dict WHERE sTable=? AND (sChinese=? OR sChinese LIKE ?) " + - "ORDER BY iFormUses DESC LIMIT 1", table, zh, "%" + zh + "%"); - if (colName != null) { - col.put(colName, v); - descParts.add(zh + "=" + v); + if (isBlank(v)) { + continue; + } + Map fm = labelMap.get(zh); + if (fm == null) { + fm = resolveColumn(table, zh); // 回退字段字典 + } + if (fm == null) { + continue; // 该实体没有这个字段,忽略 } + String colName = str(fm.get("col")); + // 别让 LLM/用户直填系统列(制单人/单据日期/租户/单号…),交给 ERP 与下方系统列处理 + if (resolver.isSystemColumn(colName)) { + continue; + } + String fk = str(fm.get("fk")); + if (fk != null && !fk.isBlank()) { + String id = resolver.resolveFk(fk, v); // 外键:名称 -> id + if (id == null) { + return err("找不到名为「" + v + "」的" + zh + ",请确认该" + zh + "是否已存在。"); + } + col.put(colName, id); + } else { + Object cv = resolver.coerce(types.get(colName), v); // 按列类型强转 + if (cv != null) { + col.put(colName, cv); + } + } + descParts.add(zh + "=" + v); } } } catch (Exception ex) { - return err("字段 JSON 解析失败:" + ex.getMessage()); + return err("字段解析失败:" + ex.getMessage()); } if (descParts.isEmpty()) { return err("请至少提供一个有效字段(如客户名称)。"); } - // 自动补齐 NOT-NULL 无默认列(租户/制单人由 ERP 注入,跳过) + // 自动补齐 NOT-NULL 无默认列(系统列跳过;其余按类型给默认 0/'') for (String rc : requiredCols(table)) { - if (col.containsKey(rc)) { + if (col.containsKey(rc) || resolver.isSystemColumn(rc)) { continue; } - if ("sId".equals(rc)) { - col.put(rc, erp.newUuid(identity.token())); - } else if (rc.endsWith("Id")) { - String d = commonValue(table, rc); // 外键:取现有最常见值兜底 - col.put(rc, d == null ? "" : d); - } else if (rc.endsWith("No")) { - col.put(rc, "AI" + (System.currentTimeMillis() % 1000000000L)); - } else { - col.put(rc, ""); - } + col.put(rc, resolver.typeDefault(rc, types.get(rc))); + } + // 系统列:主键 + 表单id + 单号(租户/制单人/日期由 ERP 注入,不填) + col.put("sId", erp.newUuid(identity.token())); + col.put("sFormId", formId); + String billNo = resolver.nextBillNo(table, identity.brandsId()); + if (billNo != null) { + col.put("sBillNo", billNo); } - col.putIfAbsent("sId", erp.newUuid(identity.token())); String payload; try { @@ -331,6 +360,20 @@ public class ProposeWriteTool { return toJson(out); } + /** 中文名 -> {col, fk}(字段字典,先精确合并模糊,取使用度最高的一列)。 */ + private Map resolveColumn(String table, String zh) { + try { + List> r = jdbc.queryForList( + "SELECT sField col, MAX(sFkTable) fk FROM viw_kg_field_dict " + + "WHERE sTable=? AND (sChinese=? OR sChinese LIKE ?) " + + "GROUP BY sField ORDER BY SUM(iFormUses) DESC LIMIT 1", + table, zh, "%" + zh + "%"); + return r.isEmpty() ? null : r.get(0); + } catch (Exception e) { + return null; + } + } + /** 目标表的 NOT-NULL 无默认列(排除 ERP 会自动注入的租户/制单人)。 */ private List requiredCols(String table) { List out = new ArrayList<>(); @@ -360,22 +403,9 @@ public class ProposeWriteTool { } } - /** 定位实体的可修改主表:该实体名下 table 类型、最常用(AI工具/连接度)的一张。 */ + /** 定位实体的可写主表——统一走 FormResolverService(与 collectForm 同源,含从属/参数表排除)。 */ private Map resolveForm(String entityKeyword) { - try { - List> r = jdbc.queryForList( - "SELECT af.sFormId, af.sModuleId, af.sDataSource FROM viw_ai_useful_forms af " + - "LEFT JOIN viw_kg_form f ON f.sFormId = af.sFormId " + - "WHERE af.sFormTitle LIKE ? AND af.sExecType='table' " + - // 排除报表视图(viw_*),只取可直接改的基础主表 - "AND af.sDataSource NOT LIKE 'viw%' " + - "ORDER BY COALESCE(f.bAiTool,0) DESC, " + - "(COALESCE(f.iUpstream,0)+COALESCE(f.iDownstream,0)) DESC, CHAR_LENGTH(af.sFormTitle) ASC LIMIT 1", - "%" + entityKeyword + "%"); - return r.isEmpty() ? null : r.get(0); - } catch (Exception e) { - return null; - } + return resolver.resolveMasterForm(entityKeyword); } private static String str(Object o) { diff --git a/src/main/java/com/xly/web/OpController.java b/src/main/java/com/xly/web/OpController.java index 784a359..a4b8a10 100644 --- a/src/main/java/com/xly/web/OpController.java +++ b/src/main/java/com/xly/web/OpController.java @@ -83,7 +83,7 @@ public class OpController { if ("create".equals(opType)) { @SuppressWarnings("unchecked") Map columns = mapper.readValue(str(op.get("sPayload")), Map.class); - r = erp.createForm(authToken, str(op.get("sTargetTable")), columns); + r = erp.createForm(authToken, str(op.get("sTargetModuleId")), str(op.get("sTargetTable")), columns); } else if ("delete".equals(opType)) { r = erp.deleteForm(authToken, str(op.get("sTargetModuleId")), str(op.get("sTargetTable")), str(op.get("sTargetBillId"))); } else if ("examine".equals(opType)) {