From 121a46ce3359d838b810e18fe2ba61a10e12f79e Mon Sep 17 00:00:00 2001 From: Jafeng <2998840497@qq.com> Date: Wed, 24 Jun 2026 14:55:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9C=A8=E6=9F=A5=E8=AF=A2=E5=85=83?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=94=AE=E4=B8=AD=E6=B7=BB=E5=8A=A0=20=5Fwhe?= =?UTF-8?q?re=20=E6=94=AF=E6=8C=81=EF=BC=8C=E5=B9=B6=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E8=BF=87=E6=BB=A4=E5=99=A8=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/adapters/gk/index.js | 3 --- src/sources/view/mssql.js | 33 +++++++++++++++++++++++++++++- src/sources/view/oracle-sqlplus.js | 27 +++++++++++++++++++++++- src/sources/view/oracle.js | 33 +++++++++++++++++++++++++++++- 4 files changed, 90 insertions(+), 6 deletions(-) diff --git a/src/adapters/gk/index.js b/src/adapters/gk/index.js index 63ef22b..205b60c 100644 --- a/src/adapters/gk/index.js +++ b/src/adapters/gk/index.js @@ -479,7 +479,6 @@ function mapOutpatientVisit(row, options = {}) { const registerType = pick(row, ["ST_REG_TYPE_NAME", "ST_REG_CATEGORY_NAME"]); const visitType = pick(row, ["VISIT_TYPE", "ST_VISIT_TYPE"]); const chargeType = pick(row, ["ST_CHARGE_TYPE", "SC_CHARGE_TYPE"]); - const serviceItems = joinPicked(row, ["ST_REG_TYPE_NAME", "ST_REG_CATEGORY_NAME", "ST_VISIT_TYPE", "VISIT_TYPE", "ST_CHARGE_TYPE"]); const amount = pick(row, [ "TREATMENT_COST", "REGISTER_TOTAL_COST", @@ -522,8 +521,6 @@ function mapOutpatientVisit(row, options = {}) { visitType, chargeType, insuranceType: pick(row, ["INSURANCE_TYPE"]), - serviceItems, - serviceItem: serviceItems, amount, treatmentCost: pick(row, ["TREATMENT_COST"]), registerCost: pick(row, ["REGISTER_COST"]), diff --git a/src/sources/view/mssql.js b/src/sources/view/mssql.js index 24c5b8d..6b97eb9 100644 --- a/src/sources/view/mssql.js +++ b/src/sources/view/mssql.js @@ -2,7 +2,7 @@ const sql = require("mssql"); const { getConfig, parsePositiveInteger } = require("../../core/config"); const pools = new Map(); -const QUERY_META_KEYS = new Set(["page", "pageSize", "saveSnapshot"]); +const QUERY_META_KEYS = new Set(["page", "pageSize", "saveSnapshot", "_where"]); const RANGE_FILTERS = { startCreateTime: { field: "create_time", op: ">=" }, endCreateTime: { field: "create_time", op: "<=" }, @@ -97,6 +97,7 @@ function buildWhereClause({ query, fields, fieldMapping, resourceConfig }) { const standardFields = new Set(fields); const inputs = []; const clauses = []; + addCustomFilters({ clauses, inputs, where: query && query._where, standardFields, fieldMapping }); for (const [key, rawValue] of Object.entries(query || {})) { if (QUERY_META_KEYS.has(key) || isEmpty(rawValue)) continue; @@ -123,6 +124,14 @@ function buildWhereClause({ query, fields, fieldMapping, resourceConfig }) { function addFilter({ clauses, inputs, standardField, value, fieldMapping, op }) { const sourceField = assertIdentifier(fieldMapping[standardField] || standardField, `filter.${standardField}`); const paramName = `p${inputs.length}`; + if (op === "notEmpty") { + clauses.push(`(${quoteName(sourceField)} IS NOT NULL AND LTRIM(RTRIM(CONVERT(NVARCHAR(MAX), ${quoteName(sourceField)}))) <> '')`); + return; + } + if (op === "isEmpty") { + clauses.push(`(${quoteName(sourceField)} IS NULL OR LTRIM(RTRIM(CONVERT(NVARCHAR(MAX), ${quoteName(sourceField)}))) = '')`); + return; + } if (op === "like") { clauses.push(`${quoteName(sourceField)} LIKE @${paramName}`); inputs.push({ name: paramName, value: `%${String(value).trim()}%` }); @@ -132,6 +141,28 @@ function addFilter({ clauses, inputs, standardField, value, fieldMapping, op }) inputs.push({ name: paramName, value }); } +function addCustomFilters({ clauses, inputs, where, standardFields, fieldMapping }) { + if (!Array.isArray(where)) return; + for (const item of where) { + const standardField = item && String(item.field || item.column || "").trim(); + if (!standardField || !standardFields.has(standardField)) continue; + const op = normalizeCustomOperator(item.op || item.operator || item.type); + if (!op) continue; + if (op !== "notEmpty" && op !== "isEmpty" && isEmpty(item.value)) continue; + addFilter({ clauses, inputs, standardField, value: item.value, fieldMapping, op }); + } +} + +function normalizeCustomOperator(op) { + const text = String(op || "eq").trim().toLowerCase(); + if (["notempty", "not_empty", "isnotempty", "is_not_empty"].includes(text)) return "notEmpty"; + if (["isempty", "is_empty", "empty"].includes(text)) return "isEmpty"; + if (["like", "contains"].includes(text)) return "like"; + if (["ne", "neq", "!=", "<>"].includes(text)) return "<>"; + if (["eq", "="].includes(text)) return "="; + return ""; +} + function getFilterOperator(resourceConfig, standardField) { const configured = resourceConfig.filterOperators && resourceConfig.filterOperators[standardField]; if (!configured) return "="; diff --git a/src/sources/view/oracle-sqlplus.js b/src/sources/view/oracle-sqlplus.js index 4d1e6c9..a546fd3 100644 --- a/src/sources/view/oracle-sqlplus.js +++ b/src/sources/view/oracle-sqlplus.js @@ -2,7 +2,7 @@ const { spawn } = require("child_process"); const iconv = require("iconv-lite"); const { getConfig, parsePositiveInteger } = require("../../core/config"); -const QUERY_META_KEYS = new Set(["page", "pageSize", "saveSnapshot"]); +const QUERY_META_KEYS = new Set(["page", "pageSize", "saveSnapshot", "_where"]); const RANGE_FILTERS = { startCreateTime: { field: "create_time", op: ">=" }, endCreateTime: { field: "create_time", op: "<=" }, @@ -182,6 +182,7 @@ function parseSqlplusOutput(output, fields) { function buildWhereClause({ query, fields, fieldMapping, resourceConfig }) { const standardFields = new Set(fields); const clauses = []; + addCustomFilters({ clauses, where: query && query._where, standardFields, fieldMapping }); for (const [key, rawValue] of Object.entries(query || {})) { if (QUERY_META_KEYS.has(key) || isEmpty(rawValue)) continue; @@ -202,10 +203,34 @@ function buildWhereClause({ query, fields, fieldMapping, resourceConfig }) { function buildFilterSql({ standardField, value, fieldMapping, op }) { const sourceField = assertIdentifier(fieldMapping[standardField] || standardField, `filter.${standardField}`); + if (op === "notEmpty") return `${cleanSqlValue(identifier(sourceField))} <> ''`; + if (op === "isEmpty") return `${cleanSqlValue(identifier(sourceField))} = ''`; if (op === "like") return `${cleanSqlValue(identifier(sourceField))} LIKE ${literal(`%${String(value).trim()}%`)}`; return `${cleanSqlValue(identifier(sourceField))} ${op || "="} ${literal(value)}`; } +function addCustomFilters({ clauses, where, standardFields, fieldMapping }) { + if (!Array.isArray(where)) return; + for (const item of where) { + const standardField = item && String(item.field || item.column || "").trim(); + if (!standardField || !standardFields.has(standardField)) continue; + const op = normalizeCustomOperator(item.op || item.operator || item.type); + if (!op) continue; + if (op !== "notEmpty" && op !== "isEmpty" && isEmpty(item.value)) continue; + clauses.push(buildFilterSql({ standardField, value: item.value, fieldMapping, op })); + } +} + +function normalizeCustomOperator(op) { + const text = String(op || "eq").trim().toLowerCase(); + if (["notempty", "not_empty", "isnotempty", "is_not_empty"].includes(text)) return "notEmpty"; + if (["isempty", "is_empty", "empty"].includes(text)) return "isEmpty"; + if (["like", "contains"].includes(text)) return "like"; + if (["ne", "neq", "!=", "<>"].includes(text)) return "<>"; + if (["eq", "="].includes(text)) return "="; + return ""; +} + function getFilterOperator(resourceConfig, standardField) { const configured = resourceConfig.filterOperators && resourceConfig.filterOperators[standardField]; if (!configured) return "="; diff --git a/src/sources/view/oracle.js b/src/sources/view/oracle.js index 095a2b0..24ab113 100644 --- a/src/sources/view/oracle.js +++ b/src/sources/view/oracle.js @@ -4,7 +4,7 @@ const { getConfig, parsePositiveInteger } = require("../../core/config"); oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT; const pools = new Map(); -const QUERY_META_KEYS = new Set(["page", "pageSize", "saveSnapshot"]); +const QUERY_META_KEYS = new Set(["page", "pageSize", "saveSnapshot", "_where"]); const RANGE_FILTERS = { startCreateTime: { field: "create_time", op: ">=" }, endCreateTime: { field: "create_time", op: "<=" }, @@ -96,6 +96,7 @@ function buildWhereClause({ query, fields, fieldMapping, resourceConfig }) { const standardFields = new Set(fields); const binds = {}; const clauses = []; + addCustomFilters({ clauses, binds, where: query && query._where, standardFields, fieldMapping }); for (const [key, rawValue] of Object.entries(query || {})) { if (QUERY_META_KEYS.has(key) || isEmpty(rawValue)) continue; @@ -122,6 +123,14 @@ function buildWhereClause({ query, fields, fieldMapping, resourceConfig }) { function addFilter({ clauses, binds, standardField, value, fieldMapping, op }) { const sourceField = assertIdentifier(fieldMapping[standardField] || standardField, `filter.${standardField}`); const paramName = `p${Object.keys(binds).length}`; + if (op === "notEmpty") { + clauses.push(`((${identifier(sourceField)} IS NOT NULL) AND TRIM(TO_CHAR(${identifier(sourceField)})) <> '')`); + return; + } + if (op === "isEmpty") { + clauses.push(`((${identifier(sourceField)} IS NULL) OR TRIM(TO_CHAR(${identifier(sourceField)})) = '')`); + return; + } if (op === "like") { clauses.push(`${identifier(sourceField)} LIKE :${paramName}`); binds[paramName] = `%${String(value).trim()}%`; @@ -131,6 +140,28 @@ function addFilter({ clauses, binds, standardField, value, fieldMapping, op }) { binds[paramName] = value; } +function addCustomFilters({ clauses, binds, where, standardFields, fieldMapping }) { + if (!Array.isArray(where)) return; + for (const item of where) { + const standardField = item && String(item.field || item.column || "").trim(); + if (!standardField || !standardFields.has(standardField)) continue; + const op = normalizeCustomOperator(item.op || item.operator || item.type); + if (!op) continue; + if (op !== "notEmpty" && op !== "isEmpty" && isEmpty(item.value)) continue; + addFilter({ clauses, binds, standardField, value: item.value, fieldMapping, op }); + } +} + +function normalizeCustomOperator(op) { + const text = String(op || "eq").trim().toLowerCase(); + if (["notempty", "not_empty", "isnotempty", "is_not_empty"].includes(text)) return "notEmpty"; + if (["isempty", "is_empty", "empty"].includes(text)) return "isEmpty"; + if (["like", "contains"].includes(text)) return "like"; + if (["ne", "neq", "!=", "<>"].includes(text)) return "<>"; + if (["eq", "="].includes(text)) return "="; + return ""; +} + function getFilterOperator(resourceConfig, standardField) { const configured = resourceConfig.filterOperators && resourceConfig.filterOperators[standardField]; if (!configured) return "=";