JsonImporterFuncs.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. // JSON 对象数据导入数据库的函数库
  2. // 应用配置信息
  3. eval(dfs.read("/script/matrix/utils/ajs/config.js"))
  4. function compitable(dta, dtb) {
  5. switch (dta) {
  6. case "timestamp":
  7. switch (dtb) {
  8. case "timestamp":
  9. return true;
  10. default:
  11. return false;
  12. }
  13. case "boolean":
  14. switch (dtb) {
  15. case "boolean":
  16. return true;
  17. default:
  18. return false;
  19. }
  20. case "int":
  21. case "integer":
  22. case "bigint":
  23. switch (dtb) {
  24. case "int":
  25. case "integer":
  26. case "bigint":
  27. return true;
  28. default:
  29. return false;
  30. }
  31. case "number":
  32. case "float":
  33. case "double":
  34. switch (dtb) {
  35. case "number":
  36. case "float":
  37. case "double":
  38. case "int":
  39. case "integer":
  40. case "bigint":
  41. return true;
  42. default:
  43. return false;
  44. }
  45. case "string":
  46. case "varchar":
  47. case "text":
  48. return true;
  49. }
  50. return false
  51. }
  52. // 根据 JSON 对象数据建类或新增字段
  53. // classname 类名
  54. // clsoption 建类选项
  55. // data 数据示例
  56. // fieldmap 字段名映射,参数中指定的字段名均为JSON对象中的属性名,默认将 camelStyle 转换为 snake_style
  57. // fields 字段类型
  58. function alterClass(input) {
  59. // 返回信息
  60. var output = {};
  61. try {
  62. // 参数检查
  63. if (!input.classname) { // like /cncc/itil/project
  64. throw ("需要指定classname");
  65. }
  66. if (input.classname[0] != "/") { // like /cncc/itil/project
  67. throw ("classname必须以 / 开头");
  68. }
  69. if (!input.data || typeof (input.data) != "object") {
  70. throw ("data必须指定为一个对象");
  71. }
  72. // 检查父类是否存在,不存在就创建
  73. cns = input.classname.split("/");
  74. clsname = "";
  75. for (var cni = 1; cni < cns.length - 1; cni++) {
  76. clsname += "/" + cns[cni];
  77. odb.mql("create class if not exists " + clsname + "()");
  78. }
  79. pclsname = clsname;
  80. clsname += "/" + cns[cns.length - 1];
  81. // 提取现有类的字段信息
  82. clsexist = false;
  83. clsfields = {};
  84. try {
  85. fieldslist = odb.classfields(clsname);
  86. for (var fi = 0; fi < fieldslist.length; fi++) {
  87. fld = fieldslist[fi];
  88. fname = fld.name;
  89. if (/^\w+\:.*/.test(fname)) {
  90. fname = fname.replace(/^\w+\:/, "");
  91. }
  92. if (fname && fld.ftype) {
  93. clsfields[fname] = {
  94. iskey: fld.iskey,
  95. ftype: fld.ftype,
  96. };
  97. }
  98. }
  99. clsexist = true;
  100. } catch (e) { }
  101. if (!clsexist) {
  102. try {
  103. fieldslist = odb.classfields(pclsname);
  104. for (var fi = 0; fi < fieldslist.length; fi++) {
  105. fld = fieldslist[fi];
  106. fname = fld.name;
  107. if (/^\w+\:.*/.test(fname)) {
  108. fname = fname.replace(/^\w+\:/, "");
  109. }
  110. if (fname && fld.ftype) {
  111. clsfields[fname] = {
  112. iskey: fld.iskey,
  113. ftype: fld.ftype,
  114. };
  115. }
  116. }
  117. } catch (e) { }
  118. }
  119. output.clsfields = clsfields;
  120. // 通过参数确定的字段类型信息
  121. fields = input.fields;
  122. if (!fields) {
  123. fields = {};
  124. }
  125. // 字段名映射,参数中指定的字段名均为JSON对象中的属性名,默认将 camelStyle 转换为 snake_style
  126. fieldmap = input.fieldmap;
  127. if (!fieldmap) {
  128. fieldmap = {};
  129. }
  130. // 反向字段名映射,类中的字段名映射为JSON对象中的属性名
  131. xfieldmap = {};
  132. // 类中的字段名列表,用于排序
  133. xfieldslist = [];
  134. // 类字段类型信息
  135. xfields = {};
  136. xpks = [];
  137. for (var jk in input.data) {
  138. xk = fieldmap[jk];
  139. // 通过参数确定的字段类型信息
  140. if (!xk) {
  141. // 默认将 camelStyle 转换为 snake_style
  142. xk = "j_"; // 区别于其它字段,自动创建的字段以 j_ 开头
  143. for (var i = 0; i < jk.length; i++) {
  144. if (jk[i] >= 'A' && jk[i] <= 'Z') {
  145. xk += "_" + jk[i].toLowerCase();
  146. } else {
  147. xk += jk[i];
  148. }
  149. }
  150. fieldmap[jk] = xk;
  151. }
  152. xfieldslist.push(xk);
  153. xfieldmap[xk] = jk;
  154. dt = fields[jk];
  155. if (dt && dt.indexOf(",key") > 0) {
  156. dt = dt.replace(",key", "");
  157. xpks.push(xk);
  158. }
  159. switch (dt) {
  160. case "date":
  161. case "time":
  162. case "datetime":
  163. dt = "timestamp";
  164. break;
  165. case "int":
  166. case "integer":
  167. dt = "bigint";
  168. break;
  169. case "number":
  170. case "float":
  171. dt = "double";
  172. break;
  173. case "text":
  174. dt = "text";
  175. break;
  176. case "string":
  177. dt = "varchar";
  178. break;
  179. case "boolean":
  180. dt = "boolean";
  181. break;
  182. }
  183. if (!dt) {
  184. // 没有明确定义的字段类型
  185. // 根据数据判断类型
  186. dt = "varchar";
  187. d = input.data[jk];
  188. if (d || d == "" || d == 0 || d == false) {
  189. switch (typeof (d)) {
  190. case 'object':
  191. // 转为text
  192. dt = "text";
  193. break;
  194. case 'number':
  195. // 是否为整数
  196. if (("" + d).indexOf(".") < 0) {
  197. dt = "bigint";
  198. } else {
  199. dt = "double";
  200. }
  201. break;
  202. case 'boolean':
  203. dt = "boolean";
  204. break;
  205. case 'string':
  206. default:
  207. // 是否为日期时间
  208. if (/_time/.test(xk) || /_date/.test(xk)) {
  209. dt = "timestamp";
  210. } else {
  211. dt = "varchar";
  212. }
  213. break;
  214. }
  215. } else {
  216. if (/_time/.test(xk) || /_date/.test(xk)) {
  217. dt = "timestamp";
  218. } else {
  219. dt = "varchar";
  220. }
  221. }
  222. }
  223. xfields[xk] = dt;
  224. }
  225. output.xfields = xfields;
  226. if (!clsexist) {
  227. // 类不存在,自动创建类
  228. mql = "create class if not exists " + clsname + "(";
  229. indexes = "indexes(";
  230. for (var fi = 0; fi < xfieldslist.length; fi++) {
  231. xk = xfieldslist[fi];
  232. if (xk in { "id": "", "class": "", "name": "", "day": "", "tags": "", "vtime": "" }) {
  233. throw ("内部使用字段名 " + xk + ",需要映射成其它名称");
  234. }
  235. if (clsfields[xk] && clsfields[xk].ftype != xfields[xk] && !compitable(clsfields[xk].ftype, xfields[xk])) {
  236. throw ("类继承字段 " + xk + " 信息不一致," + clsfields[xk].ftype + "!=" + xfields[xk] + ",需手动干预");
  237. }
  238. mql += fi == 0 ? "\n" : ",\n";
  239. mql += xk + " " + xfields[xk];
  240. if (fi > 0) {
  241. indexes += ",";
  242. }
  243. indexes += xk;
  244. }
  245. indexes += ")";
  246. mql += ",\n" + indexes;
  247. if (xpks.length > 0) {
  248. mql += ",\n";
  249. mql += "keys(";
  250. for (var pki = 0; pki < xpks.length; pki++) {
  251. if (pki > 0) {
  252. mql += ",";
  253. }
  254. mql += xpks[pki];
  255. }
  256. mql += ")";
  257. }
  258. mql += "\n";
  259. mql += ")";
  260. if (input.clsoption) {
  261. mql += input.clsoption;
  262. }
  263. odb.mql(mql);
  264. output = { mql: mql };
  265. } else {
  266. // 类已存在,检查主键信息是否存在
  267. // for (var pki = 0; pki < xpks.length; pki++) {
  268. // xk = xpks[pki];
  269. // if (!clsfields[xk] || !clsfields[xk].iskey) {
  270. // throw ("已经存在同名类主键 " + xk + " 信息不一致,需手动干预");
  271. // }
  272. // }
  273. // 追加字段
  274. mql = "alter class " + clsname + " add index column";
  275. addn = 0;
  276. for (var fi = 0; fi < xfieldslist.length; fi++) {
  277. xk = xfieldslist[fi];
  278. if (!clsfields[xk]) {
  279. mql += (addn == 0) ? " " : ", ";
  280. mql += xk + " " + xfields[xk];
  281. addn++;
  282. } else if (clsfields[xk].ftype != xfields[xk] && !compitable(clsfields[xk].ftype, xfields[xk])) {
  283. throw ("已经存在同名类字段 " + xk + " 信息不一致," + clsfields[xk].ftype + "!=" + xfields[xk] + ",需手动干预");
  284. }
  285. }
  286. if (addn > 0) {
  287. odb.mql(mql);
  288. output = { mql: mql };
  289. }
  290. }
  291. } catch (e) {
  292. if (typeof (e) == "object") {
  293. output.error = e;
  294. } else if (typeof (e) == "string") {
  295. output.error = "错误:" + e;
  296. } else {
  297. output.error = JSON.stringify(e);
  298. }
  299. }
  300. return output
  301. }
  302. // 根据 JSON 数据生成导入脚本
  303. function generateJsonImporterJS(input) {
  304. var output = {};
  305. try {
  306. // 参数检查
  307. if (!input.classname) { // like /cncc/itil/project
  308. throw ("需要指定classname");
  309. }
  310. if (input.classname[0] != "/") { // like /cncc/itil/project
  311. throw ("classname必须以 / 开头");
  312. }
  313. sjsfn = input.jsfilename;
  314. if (!sjsfn) {
  315. sjsfn = "/script" + input.classname; // like /script/cncc/itil/project
  316. }
  317. if (sjsfn.substring(0, 8) != "/script/") {
  318. throw ("jsfilename必须以 /script/ 开头");
  319. }
  320. sjsfn = sjsfn.replace(/\.js$/, "");
  321. output.file = sjsfn;
  322. dtm = new Date().toJSON();
  323. mql = "insert into " + input.classname + " (\n";
  324. mql_values = ") values (\n";
  325. mql_end = ")";
  326. values = "";
  327. fieldmap = input.fieldmap;
  328. if (!fieldmap) {
  329. fieldmap = {};
  330. }
  331. xfieldmap = {};
  332. xfields = [];
  333. clsfields = {};
  334. try {
  335. fieldslist = odb.classfields(input.classname);
  336. for (var fi = 0; fi < fieldslist.length; fi++) {
  337. fld = fieldslist[fi];
  338. fname = fld.name;
  339. if (/^\w+\:.*/.test(fname)) {
  340. fname = fname.replace(/^\w+\:/, "");
  341. }
  342. if (fname && fld.ftype) {
  343. clsfields[fname] = {
  344. iskey: fld.iskey,
  345. ftype: fld.ftype,
  346. };
  347. }
  348. }
  349. } catch (e) { }
  350. for (var k in input.data) {
  351. xk = fieldmap[k];
  352. if (!xk) {
  353. xk = "j_"; // 区别于其它字段,自动创建的字段以 j_ 开头
  354. for (var i = 0; i < k.length; i++) {
  355. if (k[i] >= 'A' && k[i] <= 'Z') {
  356. xk += "_" + k[i].toLowerCase();
  357. } else {
  358. xk += k[i];
  359. }
  360. }
  361. fieldmap[k] = xk;
  362. }
  363. xfields.push(xk);
  364. xfieldmap[xk] = k;
  365. }
  366. xfields.sort();
  367. fsep = ",";
  368. for (var i = 0; i < xfields.length; i++) {
  369. if (i == xfields.length - 1) {
  370. fsep = "";
  371. }
  372. xk = xfields[i];
  373. k = xfieldmap[xk];
  374. mql += xk + fsep + "\n";
  375. mql_values += "?" + fsep + "\n";
  376. values += " ";
  377. v = input.data[k];
  378. if (!clsfields[xk]) {
  379. throw ("字段不存在", xk)
  380. }
  381. switch (clsfields[xk].ftype) {
  382. case "text":
  383. case "varchar":
  384. if (typeof (v) == "object") {
  385. values += "JSON.stringify(";
  386. values += "input." + k + ", \" \", 4)" + fsep;
  387. values += JSON.stringify(v, " ", 4).replace(/^/mg, " // ").replace(/ /, " ");
  388. } else {
  389. values += "\"\"+";
  390. values += "input." + k + fsep;
  391. values += ("" + v).replace(/^/mg, " // ").replace(/ /, " ");
  392. }
  393. break;
  394. default:
  395. values += "input." + k + fsep;
  396. values += ("" + v).replace(/^/mg, " // ").replace(/ /, " ");
  397. }
  398. values += "\n ";
  399. }
  400. mql += mql_values + mql_end;
  401. values = "\n " + values;
  402. datainfo = "{";
  403. datacheck = "";
  404. if (input.mustfield) {
  405. ids = input.mustfield.split(",");
  406. for (var i = 0; i < ids.length; i++) {
  407. id = ids[i];
  408. if (i > 0) {
  409. datacheck += "\n ";
  410. }
  411. datacheck += "if (!input." + id + ") {\n ";
  412. datacheck += " throw (\"输入参数必须为对象,且指定属性" + id + "\");\n ";
  413. datacheck += "}";
  414. if (i == 0) {
  415. datainfo += "\n ";
  416. } else {
  417. datainfo += ",\n ";
  418. }
  419. datainfo += " " + id + ": input." + id;
  420. }
  421. }
  422. datainfo += "\n ";
  423. datainfo += "}";
  424. importjs = dfs.read("/script/matrix/utils/ajs/JsonImporter.template.js");
  425. importjs = importjs.replace(/___field_map___/mg, JSON.stringify(fieldmap));
  426. importjs = importjs.replace(/___classname___/mg, input.classname);
  427. importjs = importjs.replace(/___datetime_now___/mg, dtm);
  428. importjs = importjs.replace(/if\s*\(\s*___datacheck___\s*\)\s*\{.*\}/mg, datacheck);
  429. importjs = importjs.replace(/___mql___/mg, mql);
  430. importjs = importjs.replace(/___values___/mg, values);
  431. importjs = importjs.replace(/___datainfo___/mg, datainfo);
  432. output.content = importjs;
  433. dfs.write(sjsfn + ".js", importjs);
  434. } catch (e) {
  435. if (typeof (e) == "object") {
  436. output.error = e;
  437. } else if (typeof (e) == "string") {
  438. output.error = "错误:" + e;
  439. } else {
  440. output.error = JSON.stringify(e);
  441. }
  442. }
  443. return output
  444. }
  445. // 激活脚本,生成的脚本文件需要激活后才能生效
  446. function activeServerJS(jsfilename) {
  447. result = {};
  448. calljsfp = encodeURIComponent(jsfilename.replace(/\/script/, ""));
  449. http.do("POST",
  450. cfg.server + "/fs/tolocal/script" + calljsfp + "?issys=true",
  451. { "Authorization": "Basic " + cfg.author },
  452. '',
  453. function (response) {
  454. // success func
  455. ret = response.data;
  456. if (ret.message) {
  457. result = ret.message;
  458. } else {
  459. result = ret;
  460. }
  461. },
  462. function (response) {
  463. // error func
  464. result.error = response.data;
  465. });
  466. return result;
  467. }
  468. // 验证测试执行脚本
  469. function runServerJS(jsfilename, data) {
  470. result = {};
  471. reqinput = encodeURIComponent(base64.encode(JSON.stringify(data)));
  472. calljsfp = encodeURIComponent(jsfilename.replace(/\/script/, ""));
  473. http.do("POST",
  474. cfg.server + "/script/exec/js?filepath=" + calljsfp + "&input=" + reqinput,
  475. { "Authorization": "Basic " + cfg.author },
  476. '',
  477. function (response) {
  478. // success func
  479. ret = response.data;
  480. if (ret.message) {
  481. result = ret.message;
  482. } else {
  483. result = ret;
  484. }
  485. },
  486. function (response) {
  487. // error func
  488. result.error = response.data;
  489. });
  490. return result;
  491. }