@mldong/jeeflow 1.6.3 → 1.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/persist.d.ts CHANGED
@@ -35,10 +35,19 @@ export declare class SqliteDynamicTableWriter implements DynamicTableWriter {
35
35
  /** 用户列默认值(issues/19):优先取 data 中已注入的 apply_user_id=流程 operator,
36
36
  * 否则用此配置值,缺省 "system"——多数框架业务表 create_user/update_user 为 BIGINT 存 userId */
37
37
  defaultUserValue: unknown;
38
+ /** 列匹配(issues/20):默认宽松——驼峰↔下划线归一匹配(表单字段 companyName ↔ 表列 company_name);
39
+ * 需要精确控制列名的集成方显式开启严格模式(忽略大小写精确匹配) */
40
+ strictColumnMatch: boolean;
41
+ /** 主键生成器(issues/21):非自增主键表(雪花/应用生成)插入时生成主键值,入参表名 */
42
+ primaryKeyGenerator?: (tableName: string) => unknown;
38
43
  constructor(db: DatabaseSync);
39
44
  private tableColumns;
40
45
  filterColumns(tableName: string, columns: string[]): string[];
41
46
  insert(tableName: string, data: Record<string, unknown>): unknown;
47
+ /** 列匹配(issues/20):严格=忽略大小写精确;宽松(默认)=驼峰↔下划线归一匹配 */
48
+ private findColumn;
49
+ /** 在 data 中找匹配指定表列的 key(宽松模式驼峰 key 匹配下划线列) */
50
+ private findDataKey;
42
51
  exists(tableName: string, bizKey: string, bizKeyValue: unknown): boolean;
43
52
  fillSystemFields(data: Record<string, unknown>, isInsert: boolean): void;
44
53
  /** 默认用户值(issues/19):优先取 data 中已注入的 apply_user_id
package/dist/persist.js CHANGED
@@ -2,6 +2,10 @@ import { KeySubmitType, KeyDeptID } from './engine.js';
2
2
  import { TypeEnd, InstanceState, SubmitType } from './model.js';
3
3
  // ─── 默认实现:node:sqlite(内置零依赖) ──────────────────────────────────────
4
4
  const TABLE_NAME_RE = /^[A-Za-z0-9_]+$/;
5
+ /** 列名归一(issues/20):转小写 + 去下划线(companyName / company_name / COMPANY_NAME 等价) */
6
+ function normalizeColumn(name) {
7
+ return name.toLowerCase().replace(/_/g, '');
8
+ }
5
9
  function nowText() {
6
10
  const d = new Date();
7
11
  const p = (n) => String(n).padStart(2, '0');
@@ -34,6 +38,11 @@ export class SqliteDynamicTableWriter {
34
38
  /** 用户列默认值(issues/19):优先取 data 中已注入的 apply_user_id=流程 operator,
35
39
  * 否则用此配置值,缺省 "system"——多数框架业务表 create_user/update_user 为 BIGINT 存 userId */
36
40
  defaultUserValue = 'system';
41
+ /** 列匹配(issues/20):默认宽松——驼峰↔下划线归一匹配(表单字段 companyName ↔ 表列 company_name);
42
+ * 需要精确控制列名的集成方显式开启严格模式(忽略大小写精确匹配) */
43
+ strictColumnMatch = false;
44
+ /** 主键生成器(issues/21):非自增主键表(雪花/应用生成)插入时生成主键值,入参表名 */
45
+ primaryKeyGenerator;
37
46
  constructor(db) {
38
47
  this.db = db;
39
48
  }
@@ -41,36 +50,43 @@ export class SqliteDynamicTableWriter {
41
50
  const cached = this.cache.get(tableName);
42
51
  if (cached)
43
52
  return cached;
44
- // PRAGMA 不支持占位符——表名已过安全校验
53
+ // PRAGMA 不支持占位符——表名已过安全校验;INTEGER PRIMARY KEY 为 rowid 别名(自增)
45
54
  const rows = this.db.prepare(`PRAGMA table_info(${tableName})`).all();
46
55
  if (rows.length === 0)
47
56
  throw new Error(`persist: table ${tableName} not found`);
48
- const cols = rows.map(r => r.name.toUpperCase());
57
+ const cols = rows.map(r => ({
58
+ name: r.name.toUpperCase(),
59
+ primaryKey: r.pk === 1,
60
+ autoIncrement: r.pk === 1 && r.type.trim().toUpperCase() === 'INTEGER',
61
+ }));
49
62
  this.cache.set(tableName, cols);
50
63
  return cols;
51
64
  }
52
65
  filterColumns(tableName, columns) {
53
66
  checkTableName(tableName);
54
- const set = new Set(this.tableColumns(tableName));
55
- return columns.filter(c => set.has(c.toUpperCase()));
67
+ const cols = this.tableColumns(tableName);
68
+ return columns.filter(c => this.findColumn(cols, c) !== '');
56
69
  }
57
70
  insert(tableName, data) {
58
71
  checkTableName(tableName);
59
72
  const cols = this.tableColumns(tableName);
60
73
  const names = [];
61
74
  const values = [];
62
- // 保持插入顺序稳定(对象键无序,按表列顺序取)
63
- for (const col of cols) {
64
- if (Object.prototype.hasOwnProperty.call(data, col)) {
65
- names.push(col);
66
- values.push(data[col]);
75
+ // 保持插入顺序稳定(对象键无序,按表列顺序取);写入用表列原名(issues/20)
76
+ for (const m of cols) {
77
+ const key = this.findDataKey(data, m.name);
78
+ if (key !== '') {
79
+ names.push(m.name);
80
+ values.push(data[key]);
81
+ continue;
67
82
  }
68
- else {
69
- const key = Object.keys(data).find(k => k.toUpperCase() === col);
70
- if (key !== undefined) {
71
- names.push(col);
72
- values.push(data[key]);
83
+ // 主键生成(issues/21):非自增主键表且 data 无主键值 → 调生成器;未配置 → 清晰报错
84
+ if (m.primaryKey && !m.autoIncrement) {
85
+ if (!this.primaryKeyGenerator) {
86
+ throw new Error(`persist: table ${tableName} primary key ${m.name} is not auto-increment and no primary key generator configured (set primaryKeyGenerator, e.g. snowflake)`);
73
87
  }
88
+ names.push(m.name);
89
+ values.push(this.primaryKeyGenerator(tableName));
74
90
  }
75
91
  }
76
92
  if (names.length === 0)
@@ -80,6 +96,32 @@ export class SqliteDynamicTableWriter {
80
96
  const res = stmt.run(...values);
81
97
  return res.lastInsertRowid;
82
98
  }
99
+ /** 列匹配(issues/20):严格=忽略大小写精确;宽松(默认)=驼峰↔下划线归一匹配 */
100
+ findColumn(cols, key) {
101
+ for (const m of cols) {
102
+ if (this.strictColumnMatch) {
103
+ if (m.name.toUpperCase() === key.toUpperCase())
104
+ return m.name;
105
+ }
106
+ else if (normalizeColumn(m.name) === normalizeColumn(key)) {
107
+ return m.name;
108
+ }
109
+ }
110
+ return '';
111
+ }
112
+ /** 在 data 中找匹配指定表列的 key(宽松模式驼峰 key 匹配下划线列) */
113
+ findDataKey(data, col) {
114
+ for (const k of Object.keys(data)) {
115
+ if (this.strictColumnMatch) {
116
+ if (col.toUpperCase() === k.toUpperCase())
117
+ return k;
118
+ }
119
+ else if (normalizeColumn(col) === normalizeColumn(k)) {
120
+ return k;
121
+ }
122
+ }
123
+ return '';
124
+ }
83
125
  exists(tableName, bizKey, bizKeyValue) {
84
126
  checkTableName(tableName);
85
127
  this.tableColumns(tableName); // 表不存在提前报错
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mldong/jeeflow",
3
- "version": "1.6.3",
3
+ "version": "1.6.5",
4
4
  "description": "jeeflow workflow engine — Node.js / TypeScript implementation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",