2826 lines
230 KiB
JSON
2826 lines
230 KiB
JSON
[
|
||
{
|
||
"id": "0ac020c7380665e7",
|
||
"type": "tab",
|
||
"label": "测试流程",
|
||
"disabled": false,
|
||
"info": "",
|
||
"env": []
|
||
},
|
||
{
|
||
"id": "2bbc1a949095492c",
|
||
"type": "tab",
|
||
"label": "CRUD子流程",
|
||
"disabled": false,
|
||
"info": "",
|
||
"env": []
|
||
},
|
||
{
|
||
"id": "78d15f59dee4b6d8",
|
||
"type": "tab",
|
||
"label": "接口",
|
||
"disabled": false,
|
||
"info": "",
|
||
"env": []
|
||
},
|
||
{
|
||
"id": "8aaa24cf39b5742a",
|
||
"type": "http proxy",
|
||
"name": "https",
|
||
"url": "https://10.22.98.21:8080",
|
||
"noproxy": []
|
||
},
|
||
{
|
||
"id": "97353ed49f690c0b",
|
||
"type": "http proxy",
|
||
"name": "http",
|
||
"url": "http://10.22.98.21:8080",
|
||
"noproxy": []
|
||
},
|
||
{
|
||
"id": "be9c605d4cdaa297",
|
||
"type": "http request",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "创建井",
|
||
"method": "POST",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 1030,
|
||
"y": 560,
|
||
"wires": [
|
||
[
|
||
"6f07c9e60472a814",
|
||
"2220f90bcc99b3a4",
|
||
"df389955c2ae3156"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "96fbb1fa63a1cb8b",
|
||
"type": "inject",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "创建",
|
||
"props": [
|
||
{
|
||
"p": "payload"
|
||
},
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{\"version\":\"1.0.0\",\"act\":-1,\"data\":[{\"dsid\":\"testid2\",\"wellId\":\"WELL-zzlhTEST-002\",\"wellCommonName\":\"zzlh测试用井名\",\"wellLegalName\":\"zzlh-test-01\",\"wellPurpose\":\"开发井\",\"wellType\":\"直井\",\"dataRegion\":\"ZZLH\",\"projectId\":\"PROJ-ZZLH-001\",\"projectName\":\"zzlh测试地质单元\",\"orgId\":\"ORG-ZZLH-01\",\"orgName\":\"zzlh采油厂\",\"bsflag\":1,\"wellState\":\"生产中\",\"spudDate\":\"2024-01-15\",\"completionDate\":\"2024-05-20\",\"prodDate\":\"2024-06-01\",\"egl\":145.5,\"kbd\":5.2,\"kb\":150.7,\"actualXAxis\":550123.45,\"actualYAxis\":4998765.32,\"coordinateSystemName\":\"zzlh测试坐标系\",\"geoDescription\":\"位于zzlh测试区域\",\"remarks\":\"这是一口用于系统测试的生产井。\",\"createUserId\":\"testuser001\",\"createDate\":\"2025-09-12T10:00:00Z\",\"updateUserId\":\"testuser001\",\"updateDate\":\"2025-09-12T10:00:00Z\"}]}",
|
||
"payloadType": "json",
|
||
"x": 850,
|
||
"y": 400,
|
||
"wires": [
|
||
[
|
||
"013f3a0718a44e18"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "6f07c9e60472a814",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "创建结果",
|
||
"active": false,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1180,
|
||
"y": 440,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "1b18d7419c63f2b5",
|
||
"type": "inject",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "查询详情",
|
||
"props": [
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "url",
|
||
"v": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well/1.0.0/testid2",
|
||
"vt": "str"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"x": 160,
|
||
"y": 220,
|
||
"wires": [
|
||
[
|
||
"70a49687f823bf4e"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "88cf35d496381288",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "查询",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 470,
|
||
"y": 220,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "70a49687f823bf4e",
|
||
"type": "http request",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "查询井详情",
|
||
"method": "GET",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "basic",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 330,
|
||
"y": 240,
|
||
"wires": [
|
||
[
|
||
"88cf35d496381288"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "ae5c8d27d69b77ed",
|
||
"type": "http request",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "更新井",
|
||
"method": "PUT",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 270,
|
||
"y": 340,
|
||
"wires": [
|
||
[
|
||
"2b37a50f557b48ff"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "7f1ff1fda219baaa",
|
||
"type": "inject",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "更新",
|
||
"props": [
|
||
{
|
||
"p": "payload"
|
||
},
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{\"version\":\"1.0.0\",\"act\":-1,\"data\":[{\"dsid\":\"testid2\",\"wellId\":\"WELL-zzlhTEST-002\",\"wellCommonName\":\"zzlh测试用井名\",\"wellLegalName\":\"zzlh-test-01\",\"wellPurpose\":\"开发井---更新16\",\"wellType\":\"直井\",\"dataRegion\":\"ZZLH\",\"projectId\":\"PROJ-ZZLH-001\",\"projectName\":\"zzlh测试地质单元\",\"orgId\":\"ORG-ZZLH-01\",\"orgName\":\"zzlh采油厂\",\"bsflag\":1,\"wellState\":\"生产中\",\"spudDate\":\"2024-01-15\",\"completionDate\":\"2024-05-20\",\"prodDate\":\"2024-06-01\",\"egl\":145.5,\"kbd\":5.2,\"kb\":150.7,\"actualXAxis\":550123.45,\"actualYAxis\":4998765.32,\"coordinateSystemName\":\"zzlh测试坐标系\",\"geoDescription\":\"位于zzlh测试区域\",\"remarks\":\"这是一口用于系统测试的生产井。\",\"createUserId\":\"testuser001\",\"createDate\":\"2025-09-12T10:00:00Z\",\"updateUserId\":\"testuser001\",\"updateDate\":\"2025-09-12T10:00:00Z\"}]}",
|
||
"payloadType": "json",
|
||
"x": 110,
|
||
"y": 280,
|
||
"wires": [
|
||
[
|
||
"ae5c8d27d69b77ed"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "2b37a50f557b48ff",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "更新结果",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 460,
|
||
"y": 340,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "c3514fbf44147aed",
|
||
"type": "inject",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "删除",
|
||
"props": [
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "url",
|
||
"v": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "payload"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{\"version\":\"1.0.0\",\"data\":[\"a1b2c3d4-e5f6-7890-1234-567890abcdef\"]}",
|
||
"payloadType": "json",
|
||
"x": 90,
|
||
"y": 380,
|
||
"wires": [
|
||
[
|
||
"0d437a2a77570c57"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "647fd9a7f8fc214c",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "删除结果",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 500,
|
||
"y": 420,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "0d437a2a77570c57",
|
||
"type": "http request",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "删除井",
|
||
"method": "DELETE",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 230,
|
||
"y": 420,
|
||
"wires": [
|
||
[
|
||
"647fd9a7f8fc214c"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "044945e983586ce5",
|
||
"type": "inject",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "查询列表",
|
||
"props": [
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "url",
|
||
"v": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well/1.0.0",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "payload"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{}",
|
||
"payloadType": "json",
|
||
"x": 120,
|
||
"y": 480,
|
||
"wires": [
|
||
[
|
||
"5921574867140eb3"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "286d3b129ca1b756",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "查询",
|
||
"active": false,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 490,
|
||
"y": 480,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "5921574867140eb3",
|
||
"type": "http request",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "查询井列表",
|
||
"method": "POST",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 290,
|
||
"y": 500,
|
||
"wires": [
|
||
[
|
||
"286d3b129ca1b756",
|
||
"819906f13bb83e88"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "819906f13bb83e88",
|
||
"type": "function",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "function 1",
|
||
"func": "try {\n \n const payload = JSON.parse(msg.payload);\n if (payload && payload.data && Array.isArray(payload.data.list)) {\n msg.isEmpty = payload.data.list.length === 0;\n } else {\n msg.isEmpty = false; \n }\n} catch (e) {\n \n \n msg.isEmpty = false\n}\n\n\nreturn msg;",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 300,
|
||
"y": 600,
|
||
"wires": [
|
||
[
|
||
"7b828761c4ae4f35"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "7b828761c4ae4f35",
|
||
"type": "switch",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "",
|
||
"property": "isEmpty",
|
||
"propertyType": "msg",
|
||
"rules": [
|
||
{
|
||
"t": "eq",
|
||
"v": "true",
|
||
"vt": "jsonata"
|
||
},
|
||
{
|
||
"t": "neq",
|
||
"v": "true",
|
||
"vt": "jsonata"
|
||
}
|
||
],
|
||
"checkall": "true",
|
||
"repair": false,
|
||
"outputs": 2,
|
||
"x": 470,
|
||
"y": 580,
|
||
"wires": [
|
||
[
|
||
"e308201b5e361b2e",
|
||
"013f3a0718a44e18"
|
||
],
|
||
[
|
||
"5712828285fd7a38"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "5712828285fd7a38",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "debug 1",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "\"初始不为空,错误\"",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 490,
|
||
"y": 680,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "e308201b5e361b2e",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "debug 2",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "\"初始为空,正确\"",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 690,
|
||
"y": 640,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "013f3a0718a44e18",
|
||
"type": "change",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "",
|
||
"rules": [
|
||
{
|
||
"t": "set",
|
||
"p": "payload",
|
||
"pt": "msg",
|
||
"to": "{\"version\":\"1.0.0\",\"act\":-1,\"data\":[{\"dsid\":\"testid2\",\"wellId\":\"WELL-zzlhTEST-002\",\"wellCommonName\":\"zzlh测试用井名\",\"wellLegalName\":\"zzlh-test-01\",\"wellPurpose\":\"开发井\",\"wellType\":\"直井\",\"dataRegion\":\"ZZLH\",\"projectId\":\"PROJ-ZZLH-001\",\"projectName\":\"zzlh测试地质单元\",\"orgId\":\"ORG-ZZLH-01\",\"orgName\":\"zzlh采油厂\",\"bsflag\":1,\"wellState\":\"生产中\",\"spudDate\":\"2024-01-15\",\"completionDate\":\"2024-05-20\",\"prodDate\":\"2024-06-01\",\"egl\":145.5,\"kbd\":5.2,\"kb\":150.7,\"actualXAxis\":550123.45,\"actualYAxis\":4998765.32,\"coordinateSystemName\":\"zzlh测试坐标系\",\"geoDescription\":\"位于zzlh测试区域\",\"remarks\":\"这是一口用于系统测试的生产井。\",\"createUserId\":\"testuser001\",\"createDate\":\"2025-09-12T10:00:00Z\",\"updateUserId\":\"testuser001\",\"updateDate\":\"2025-09-12T10:00:00Z\"}]}",
|
||
"tot": "json"
|
||
},
|
||
{
|
||
"t": "set",
|
||
"p": "headers",
|
||
"pt": "msg",
|
||
"to": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"tot": "json"
|
||
},
|
||
{
|
||
"t": "set",
|
||
"p": "rejectUnauthorized",
|
||
"pt": "msg",
|
||
"to": "false",
|
||
"tot": "str"
|
||
}
|
||
],
|
||
"action": "",
|
||
"property": "",
|
||
"from": "",
|
||
"to": "",
|
||
"reg": false,
|
||
"x": 880,
|
||
"y": 560,
|
||
"wires": [
|
||
[
|
||
"be9c605d4cdaa297"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "8f574d489345a331",
|
||
"type": "switch",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "",
|
||
"property": "isCreated",
|
||
"propertyType": "msg",
|
||
"rules": [
|
||
{
|
||
"t": "eq",
|
||
"v": "true",
|
||
"vt": "jsonata"
|
||
},
|
||
{
|
||
"t": "eq",
|
||
"v": "false",
|
||
"vt": "jsonata"
|
||
}
|
||
],
|
||
"checkall": "true",
|
||
"repair": false,
|
||
"outputs": 2,
|
||
"x": 1330,
|
||
"y": 620,
|
||
"wires": [
|
||
[
|
||
"23f668f265c1db5c",
|
||
"8f280fa3aac33505"
|
||
],
|
||
[
|
||
"4fd140a8f2df7e53"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "2220f90bcc99b3a4",
|
||
"type": "function",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "function 2",
|
||
"func": "try {\n const payloadObject = JSON.parse(msg.payload);\n msg.isCreated = (payloadObject.code === 0);\n\n} catch (e) {\n msg.isCreated = false;\n}\n\n// 返回更新后的 msg 对象\nreturn msg;",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 1160,
|
||
"y": 620,
|
||
"wires": [
|
||
[
|
||
"8f574d489345a331"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "23f668f265c1db5c",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "debug 3",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "\"创建成功\"",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1390,
|
||
"y": 460,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "4fd140a8f2df7e53",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "debug 4",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "创建失败",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1410,
|
||
"y": 740,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "df389955c2ae3156",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "debug 6",
|
||
"active": false,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "msg",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1000,
|
||
"y": 680,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "39c28a10430cce71",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "debug 5",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "false",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 800,
|
||
"y": 740,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "551a78c76ae2bccf",
|
||
"type": "http request",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "删除井",
|
||
"method": "DELETE",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 1770,
|
||
"y": 660,
|
||
"wires": [
|
||
[
|
||
"99f3d98419231d12",
|
||
"e3cc68ad6021510f"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "99f3d98419231d12",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "删除结果",
|
||
"active": false,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1960,
|
||
"y": 540,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "7d94b96bcc85d684",
|
||
"type": "inject",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "删除",
|
||
"props": [
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "url",
|
||
"v": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "payload"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{\"version\":\"1.0.0\",\"data\":[\"testid2\"]}",
|
||
"payloadType": "json",
|
||
"x": 1710,
|
||
"y": 480,
|
||
"wires": [
|
||
[
|
||
"551a78c76ae2bccf"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "8f280fa3aac33505",
|
||
"type": "change",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "",
|
||
"rules": [
|
||
{
|
||
"t": "set",
|
||
"p": "payload",
|
||
"pt": "msg",
|
||
"to": "{\"version\":\"1.0.0\",\"data\":[\"testid2\"]}",
|
||
"tot": "json"
|
||
},
|
||
{
|
||
"t": "set",
|
||
"p": "headers",
|
||
"pt": "msg",
|
||
"to": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"tot": "json"
|
||
},
|
||
{
|
||
"t": "set",
|
||
"p": "rejectUnauthorized",
|
||
"pt": "msg",
|
||
"to": "false",
|
||
"tot": "str"
|
||
},
|
||
{
|
||
"t": "set",
|
||
"p": "url",
|
||
"pt": "msg",
|
||
"to": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well",
|
||
"tot": "str"
|
||
}
|
||
],
|
||
"action": "",
|
||
"property": "",
|
||
"from": "",
|
||
"to": "",
|
||
"reg": false,
|
||
"x": 1580,
|
||
"y": 620,
|
||
"wires": [
|
||
[
|
||
"551a78c76ae2bccf"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "6585c529e8732137",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "debug 7",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "\"删除成功\"",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 2170,
|
||
"y": 540,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "3b9a9af3fb68645f",
|
||
"type": "switch",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "",
|
||
"property": "isDeleted",
|
||
"propertyType": "msg",
|
||
"rules": [
|
||
{
|
||
"t": "eq",
|
||
"v": "true",
|
||
"vt": "jsonata"
|
||
},
|
||
{
|
||
"t": "eq",
|
||
"v": "false",
|
||
"vt": "jsonata"
|
||
}
|
||
],
|
||
"checkall": "true",
|
||
"repair": false,
|
||
"outputs": 2,
|
||
"x": 2110,
|
||
"y": 700,
|
||
"wires": [
|
||
[
|
||
"6585c529e8732137",
|
||
"cfbf5ff4275a974e"
|
||
],
|
||
[
|
||
"f80987279214f312"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "f80987279214f312",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "debug 8",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "删除失败",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 2190,
|
||
"y": 820,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "e3cc68ad6021510f",
|
||
"type": "function",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "function 3",
|
||
"func": "try {\n const payloadObject = JSON.parse(msg.payload);\n msg.isDeleted = (payloadObject.code === 0);\n\n} catch (e) {\n msg.isDeleted = false;\n}\n\n// 返回更新后的 msg 对象\nreturn msg;",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 1920,
|
||
"y": 700,
|
||
"wires": [
|
||
[
|
||
"3b9a9af3fb68645f",
|
||
"f92150ebdd4ca3ff"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "f92150ebdd4ca3ff",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "debug 9",
|
||
"active": false,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "msg",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 2000,
|
||
"y": 760,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "c281f9583e262827",
|
||
"type": "function",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "function 4",
|
||
"func": "msg.headers = { \"Content-Type\": \"application/json\", \"Authorization\": \"1\", \"Dataregion\": \"ZZLH\" }\nmsg.body={}\nmsg.rejectUnauthorized=false\nmsg.url = \"https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well/1.0.0\"\nreturn msg;",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 100,
|
||
"y": 580,
|
||
"wires": [
|
||
[
|
||
"5921574867140eb3"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "ea8a01974cede86a",
|
||
"type": "http in",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "",
|
||
"url": "/test",
|
||
"method": "get",
|
||
"upload": false,
|
||
"skipBodyParsing": false,
|
||
"swaggerDoc": "",
|
||
"x": 80,
|
||
"y": 660,
|
||
"wires": [
|
||
[
|
||
"c281f9583e262827"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "fdf4a7a6166cf05c",
|
||
"type": "http response",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "",
|
||
"statusCode": "",
|
||
"headers": {},
|
||
"x": 2370,
|
||
"y": 700,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "cfbf5ff4275a974e",
|
||
"type": "function",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "function 5",
|
||
"func": "msg.payload = { \"code\": 0, \"message\": \"井创建删除流程测试成功\",}\nreturn msg;",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 2260,
|
||
"y": 640,
|
||
"wires": [
|
||
[
|
||
"fdf4a7a6166cf05c"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "ac556871dae3b9ea",
|
||
"type": "inject",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "查询列表",
|
||
"props": [
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "url",
|
||
"v": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well/1.0.0",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "payload"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{}",
|
||
"payloadType": "json",
|
||
"x": 280,
|
||
"y": 100,
|
||
"wires": [
|
||
[
|
||
"ee3b0307d3ddb476"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "c69cfc2d862f1fe4",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "查询",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 650,
|
||
"y": 100,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "ee3b0307d3ddb476",
|
||
"type": "http request",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "查询井列表",
|
||
"method": "POST",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 450,
|
||
"y": 120,
|
||
"wires": [
|
||
[
|
||
"c69cfc2d862f1fe4"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "d50ee4d6f62682dd",
|
||
"type": "inject",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "创建",
|
||
"props": [
|
||
{
|
||
"p": "payload"
|
||
},
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{\"version\":\"1.0.0\",\"act\":-1,\"data\":[{\"dsid\":\"testid2\",\"wellId\":\"WELL-zzlhTEST-002\",\"wellCommonName\":\"zzlh测试用井名\",\"wellLegalName\":\"zzlh-test-01\",\"wellPurpose\":\"开发井\",\"wellType\":\"直井\",\"dataRegion\":\"ZZLH\",\"projectId\":\"PROJ-ZZLH-001\",\"projectName\":\"zzlh测试地质单元\",\"orgId\":\"ORG-ZZLH-01\",\"orgName\":\"zzlh采油厂\",\"bsflag\":1,\"wellState\":\"生产中\",\"spudDate\":\"2024-01-15\",\"completionDate\":\"2024-05-20\",\"prodDate\":\"2024-06-01\",\"egl\":145.5,\"kbd\":5.2,\"kb\":150.7,\"actualXAxis\":550123.45,\"actualYAxis\":4998765.32,\"coordinateSystemName\":\"zzlh测试坐标系\",\"geoDescription\":\"位于zzlh测试区域\",\"remarks\":\"这是一口用于系统测试的生产井。\",\"createUserId\":\"testuser001\",\"createDate\":\"2025-09-12T10:00:00Z\",\"updateUserId\":\"testuser001\",\"updateDate\":\"2025-09-12T10:00:00Z\"}]}",
|
||
"payloadType": "json",
|
||
"x": 850,
|
||
"y": 160,
|
||
"wires": [
|
||
[
|
||
"61b9d6e67e3f48f6"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "61b9d6e67e3f48f6",
|
||
"type": "http request",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "创建井",
|
||
"method": "POST",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 990,
|
||
"y": 100,
|
||
"wires": [
|
||
[
|
||
"9821ae3eea5804dc"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "9821ae3eea5804dc",
|
||
"type": "debug",
|
||
"z": "0ac020c7380665e7",
|
||
"name": "创建结果",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1080,
|
||
"y": 220,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "6b82573f9ffd406c",
|
||
"type": "http request",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "创建",
|
||
"method": "use",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 950,
|
||
"y": 480,
|
||
"wires": [
|
||
[
|
||
"4de04763f97579e6",
|
||
"dff6a9719efe6bb8",
|
||
"b18ba558db58cd2c",
|
||
"5bd9db9eda3b1538"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "fd19275e55b06039",
|
||
"type": "inject",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "创建",
|
||
"props": [
|
||
{
|
||
"p": "payload"
|
||
},
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{\"version\":\"1.0.0\",\"act\":-1,\"data\":[{\"dsid\":\"testid2\",\"wellId\":\"WELL-zzlhTEST-002\",\"wellCommonName\":\"zzlh测试用井名\",\"wellLegalName\":\"zzlh-test-01\",\"wellPurpose\":\"开发井\",\"wellType\":\"直井\",\"dataRegion\":\"ZZLH\",\"projectId\":\"PROJ-ZZLH-001\",\"projectName\":\"zzlh测试地质单元\",\"orgId\":\"ORG-ZZLH-01\",\"orgName\":\"zzlh采油厂\",\"bsflag\":1,\"wellState\":\"生产中\",\"spudDate\":\"2024-01-15\",\"completionDate\":\"2024-05-20\",\"prodDate\":\"2024-06-01\",\"egl\":145.5,\"kbd\":5.2,\"kb\":150.7,\"actualXAxis\":550123.45,\"actualYAxis\":4998765.32,\"coordinateSystemName\":\"zzlh测试坐标系\",\"geoDescription\":\"位于zzlh测试区域\",\"remarks\":\"这是一口用于系统测试的生产井。\",\"createUserId\":\"testuser001\",\"createDate\":\"2025-09-12T10:00:00Z\",\"updateUserId\":\"testuser001\",\"updateDate\":\"2025-09-12T10:00:00Z\"}]}",
|
||
"payloadType": "json",
|
||
"x": 850,
|
||
"y": 320,
|
||
"wires": [
|
||
[
|
||
"6b82573f9ffd406c"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "4de04763f97579e6",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "创建结果",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1180,
|
||
"y": 360,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "9d308d75e615a2b1",
|
||
"type": "http request",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "更新井",
|
||
"method": "PUT",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 270,
|
||
"y": 280,
|
||
"wires": [
|
||
[
|
||
"53b9ebee8b3ad17f"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "7b342ce3a2bf48c1",
|
||
"type": "inject",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "更新",
|
||
"props": [
|
||
{
|
||
"p": "payload"
|
||
},
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{\"version\":\"1.0.0\",\"act\":-1,\"data\":[{\"dsid\":\"testid2\",\"wellId\":\"WELL-zzlhTEST-002\",\"wellCommonName\":\"zzlh测试用井名\",\"wellLegalName\":\"zzlh-test-01\",\"wellPurpose\":\"开发井---更新16\",\"wellType\":\"直井\",\"dataRegion\":\"ZZLH\",\"projectId\":\"PROJ-ZZLH-001\",\"projectName\":\"zzlh测试地质单元\",\"orgId\":\"ORG-ZZLH-01\",\"orgName\":\"zzlh采油厂\",\"bsflag\":1,\"wellState\":\"生产中\",\"spudDate\":\"2024-01-15\",\"completionDate\":\"2024-05-20\",\"prodDate\":\"2024-06-01\",\"egl\":145.5,\"kbd\":5.2,\"kb\":150.7,\"actualXAxis\":550123.45,\"actualYAxis\":4998765.32,\"coordinateSystemName\":\"zzlh测试坐标系\",\"geoDescription\":\"位于zzlh测试区域\",\"remarks\":\"这是一口用于系统测试的生产井。\",\"createUserId\":\"testuser001\",\"createDate\":\"2025-09-12T10:00:00Z\",\"updateUserId\":\"testuser001\",\"updateDate\":\"2025-09-12T10:00:00Z\"}]}",
|
||
"payloadType": "json",
|
||
"x": 110,
|
||
"y": 260,
|
||
"wires": [
|
||
[
|
||
"9d308d75e615a2b1"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "53b9ebee8b3ad17f",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "更新结果",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 460,
|
||
"y": 260,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "cfd7ae558fdd3340",
|
||
"type": "inject",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "查询列表",
|
||
"props": [
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "url",
|
||
"v": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well/1.0.0",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "payload"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{}",
|
||
"payloadType": "json",
|
||
"x": 120,
|
||
"y": 400,
|
||
"wires": [
|
||
[
|
||
"1c0decb86a5b16bc"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "54b7e6f656002a74",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "查询",
|
||
"active": false,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 490,
|
||
"y": 400,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "1c0decb86a5b16bc",
|
||
"type": "http request",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "查询列表",
|
||
"method": "use",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 280,
|
||
"y": 420,
|
||
"wires": [
|
||
[
|
||
"54b7e6f656002a74",
|
||
"c226f4cb78c4eeda"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "c226f4cb78c4eeda",
|
||
"type": "function",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "判断是否list请求成功",
|
||
"func": "try {\n// msg.isEmpty = true\n// return msg\n const payload = JSON.parse(msg.payload);\n if (payload && payload.data && Array.isArray(payload.data.list)) {\n // msg.isEmpty = payload.data.list.length === 0;\n \n msg.isEmpty = (payload.code === 0);\n } else {\n msg.isEmpty = false; \n }\n} \ncatch (e) {\n \n \n msg.isEmpty = false\n}\n\n\nreturn msg;",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 320,
|
||
"y": 520,
|
||
"wires": [
|
||
[
|
||
"8c029cbbaf7a4083"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "8c029cbbaf7a4083",
|
||
"type": "switch",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "",
|
||
"property": "isEmpty",
|
||
"propertyType": "msg",
|
||
"rules": [
|
||
{
|
||
"t": "eq",
|
||
"v": "true",
|
||
"vt": "jsonata"
|
||
},
|
||
{
|
||
"t": "neq",
|
||
"v": "true",
|
||
"vt": "jsonata"
|
||
}
|
||
],
|
||
"checkall": "true",
|
||
"repair": false,
|
||
"outputs": 2,
|
||
"x": 470,
|
||
"y": 500,
|
||
"wires": [
|
||
[
|
||
"58738307f84d7063",
|
||
"e9fc4a2ffe2cd375"
|
||
],
|
||
[
|
||
"0d33ba7f9e75ba14"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "0d33ba7f9e75ba14",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "debug 12",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "\"list请求错误\"",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 490,
|
||
"y": 600,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "58738307f84d7063",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "debug 13",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "\"list请求正确\"",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 690,
|
||
"y": 560,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "eae40b8b1740da9d",
|
||
"type": "switch",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "",
|
||
"property": "isCreated",
|
||
"propertyType": "msg",
|
||
"rules": [
|
||
{
|
||
"t": "eq",
|
||
"v": "true",
|
||
"vt": "jsonata"
|
||
},
|
||
{
|
||
"t": "eq",
|
||
"v": "false",
|
||
"vt": "jsonata"
|
||
}
|
||
],
|
||
"checkall": "true",
|
||
"repair": false,
|
||
"outputs": 2,
|
||
"x": 1330,
|
||
"y": 540,
|
||
"wires": [
|
||
[
|
||
"7f25bc1c56632834",
|
||
"9130d5e40d2e8b53"
|
||
],
|
||
[
|
||
"b32a8f9b0f2cd01b"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "dff6a9719efe6bb8",
|
||
"type": "function",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "判断创建结果",
|
||
"func": "try {\n const payloadObject = JSON.parse(msg.payload);\n msg.isCreated = (payloadObject.code === 0);\n\n} catch (e) {\n msg.isCreated = false;\n}\n\n// 返回更新后的 msg 对象\nreturn msg;",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 1120,
|
||
"y": 540,
|
||
"wires": [
|
||
[
|
||
"eae40b8b1740da9d"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "7f25bc1c56632834",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "debug 14",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "\"创建成功\"",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1390,
|
||
"y": 380,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "b32a8f9b0f2cd01b",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "debug 15",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "创建失败",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1410,
|
||
"y": 660,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "b18ba558db58cd2c",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "debug 16",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "msg",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1000,
|
||
"y": 600,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "1c609e3fa4938dc3",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "debug 17",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "false",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 800,
|
||
"y": 660,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "6b971983d782d570",
|
||
"type": "http request",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "删除",
|
||
"method": "use",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 1770,
|
||
"y": 580,
|
||
"wires": [
|
||
[
|
||
"449b2760f19c65ef",
|
||
"588b71ddf9f6ad2a"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "449b2760f19c65ef",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "删除结果",
|
||
"active": false,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1960,
|
||
"y": 460,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "8534db3f11cc613a",
|
||
"type": "inject",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "删除",
|
||
"props": [
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Authorization\":\"1\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "url",
|
||
"v": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "payload"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{\"version\":\"1.0.0\",\"data\":[\"testid2\"]}",
|
||
"payloadType": "json",
|
||
"x": 1710,
|
||
"y": 400,
|
||
"wires": [
|
||
[
|
||
"6b971983d782d570"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "87da5039b77ae777",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "debug 18",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "\"删除成功\"",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 2170,
|
||
"y": 460,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "466bd32fcc7164c2",
|
||
"type": "switch",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "",
|
||
"property": "isDeleted",
|
||
"propertyType": "msg",
|
||
"rules": [
|
||
{
|
||
"t": "eq",
|
||
"v": "true",
|
||
"vt": "jsonata"
|
||
},
|
||
{
|
||
"t": "eq",
|
||
"v": "false",
|
||
"vt": "jsonata"
|
||
}
|
||
],
|
||
"checkall": "true",
|
||
"repair": false,
|
||
"outputs": 2,
|
||
"x": 2110,
|
||
"y": 620,
|
||
"wires": [
|
||
[
|
||
"87da5039b77ae777",
|
||
"6ce191ad8e5a407e"
|
||
],
|
||
[
|
||
"b41e21ee453d29d7"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "b41e21ee453d29d7",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "debug 19",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": true,
|
||
"tostatus": false,
|
||
"complete": "删除失败",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 2190,
|
||
"y": 740,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "588b71ddf9f6ad2a",
|
||
"type": "function",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "function 8",
|
||
"func": "try {\n const payloadObject = JSON.parse(msg.payload);\n msg.isDeleted = (payloadObject.code === 0);\n\n} catch (e) {\n msg.isDeleted = false;\n}\n\n// 返回更新后的 msg 对象\nreturn msg;",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 1920,
|
||
"y": 620,
|
||
"wires": [
|
||
[
|
||
"466bd32fcc7164c2",
|
||
"5b1fd3727096c31d"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "5b1fd3727096c31d",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "debug 20",
|
||
"active": false,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "msg",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 2000,
|
||
"y": 680,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "cada8d4a6105ea2d",
|
||
"type": "function",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "function 9",
|
||
"func": "msg.headers = { \"Content-Type\": \"application/json\", \"Authorization\": \"1\", \"Dataregion\": \"ZZLH\" }\nmsg.body={}\nmsg.rejectUnauthorized=false\nmsg.url = \"https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well/1.0.0\"\nreturn msg;",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 100,
|
||
"y": 500,
|
||
"wires": [
|
||
[
|
||
"1c0decb86a5b16bc"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "21d47918f0e5a611",
|
||
"type": "http in",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "",
|
||
"url": "/test",
|
||
"method": "get",
|
||
"upload": false,
|
||
"skipBodyParsing": false,
|
||
"swaggerDoc": "",
|
||
"x": 80,
|
||
"y": 580,
|
||
"wires": [
|
||
[
|
||
"cada8d4a6105ea2d"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "608d38ac3750e9a9",
|
||
"type": "http response",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "返回",
|
||
"statusCode": "",
|
||
"headers": {},
|
||
"x": 2410,
|
||
"y": 700,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "6ce191ad8e5a407e",
|
||
"type": "function",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "function 10",
|
||
"func": "msg.payload = { \"code\": 0, \"message\": \"井创建删除流程测试成功\",}\nreturn msg;",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 2260,
|
||
"y": 560,
|
||
"wires": [
|
||
[
|
||
"ab1f2a98d3200f0d"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "954bc3b915b9ad93",
|
||
"type": "inject",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "创建",
|
||
"props": [
|
||
{
|
||
"p": "payload"
|
||
},
|
||
{
|
||
"p": "headers",
|
||
"v": "{\"Content-Type\":\"application/json\",\"Dataregion\":\"ZZLH\"}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "rejectUnauthorized",
|
||
"v": "false",
|
||
"vt": "str"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"payload": "{\"version\":\"1.0.0\",\"act\":-1,\"data\":[{\"dsid\":\"testid2\",\"wellId\":\"WELL-zzlhTEST-002\",\"wellCommonName\":\"zzlh测试用井名\",\"wellLegalName\":\"zzlh-test-01\",\"wellPurpose\":\"开发井\",\"wellType\":\"直井\",\"dataRegion\":\"ZZLH\",\"projectId\":\"PROJ-ZZLH-001\",\"projectName\":\"zzlh测试地质单元\",\"orgId\":\"ORG-ZZLH-01\",\"orgName\":\"zzlh采油厂\",\"bsflag\":1,\"wellState\":\"生产中\",\"spudDate\":\"2024-01-15\",\"completionDate\":\"2024-05-20\",\"prodDate\":\"2024-06-01\",\"egl\":145.5,\"kbd\":5.2,\"kb\":150.7,\"actualXAxis\":550123.45,\"actualYAxis\":4998765.32,\"coordinateSystemName\":\"zzlh测试坐标系\",\"geoDescription\":\"位于zzlh测试区域\",\"remarks\":\"这是一口用于系统测试的生产井。\",\"createUserId\":\"testuser001\",\"createDate\":\"2025-09-12T10:00:00Z\",\"updateUserId\":\"testuser001\",\"updateDate\":\"2025-09-12T10:00:00Z\"}]}",
|
||
"payloadType": "json",
|
||
"x": 850,
|
||
"y": 80,
|
||
"wires": [
|
||
[
|
||
"05975bc9c72aea39"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "05975bc9c72aea39",
|
||
"type": "http request",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "创建井",
|
||
"method": "POST",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1/cd_well",
|
||
"tls": "",
|
||
"persist": true,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 990,
|
||
"y": 20,
|
||
"wires": [
|
||
[
|
||
"0144f7e3d85b8e7e"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "0144f7e3d85b8e7e",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "创建结果",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1080,
|
||
"y": 140,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "6c06fbe60248c14c",
|
||
"type": "link in",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "crud子流程linkin",
|
||
"links": [
|
||
"80da1e9b993f73dc"
|
||
],
|
||
"x": 65,
|
||
"y": 340,
|
||
"wires": [
|
||
[
|
||
"b23f184ea63d57f8"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "b23f184ea63d57f8",
|
||
"type": "function",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "设置查询请求",
|
||
"func": "'use strict';\n\n/**\n * 根据 msg.crudFlow.list 设置查询请求,兼容旧的手工注入流程。\n */\n\nreturn configureListRequest(msg, node);\n\nfunction configureListRequest(message, node) {\n if (!message.crudFlow || !message.crudFlow.list) {\n return message;\n }\n\n const list = message.crudFlow.list;\n const baseUrl = message.crudFlow.baseUrl || '';\n\n message.method = (list.method || 'GET').toUpperCase();\n message.url = mergeUrl(baseUrl, list.path || '');\n message.headers = Object.assign({}, message.crudFlow.headers || {}, list.headers || {});\n\n if (list.payload !== undefined) {\n message.payload = clone(list.payload);\n }\n\n message.listRequestConfigured = true;\n return message;\n}\n\nfunction mergeUrl(base, path) {\n const prefix = (base || '').replace(/\\/+$/, '');\n const suffix = (path || '').replace(/^\\/+/, '');\n if (!prefix) {\n return `/${suffix}`;\n }\n if (!suffix) {\n return prefix;\n }\n return `${prefix}/${suffix}`;\n}\n\nfunction clone(value) {\n return value == null ? value : JSON.parse(JSON.stringify(value));\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 100,
|
||
"y": 660,
|
||
"wires": [
|
||
[
|
||
"bd042d824b60b5ac",
|
||
"091820a71299ef8e",
|
||
"b84b523c19c4662b"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "bd042d824b60b5ac",
|
||
"type": "http request",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "",
|
||
"method": "use",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "",
|
||
"tls": "",
|
||
"persist": false,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 280,
|
||
"y": 720,
|
||
"wires": [
|
||
[
|
||
"c226f4cb78c4eeda",
|
||
"c2abaaef33a4e83a"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "e9fc4a2ffe2cd375",
|
||
"type": "function",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "准备create",
|
||
"func": "'use strict';\n\n/**\n * 使用 msg.crudFlow.create 配置创建请求;若未提供则保持旧有注入负载。\n */\n\nreturn configureCreateRequest(msg, node);\n\nfunction configureCreateRequest(message, node) {\n if (!message.crudFlow || !message.crudFlow.create) {\n return message;\n }\n\n const create = message.crudFlow.create;\n const baseUrl = message.crudFlow.baseUrl || '';\n const payload = selectCreatePayload(create);\n\n message.method = (create.method || 'POST').toUpperCase();\n message.url = mergeUrl(baseUrl, create.path || '');\n message.headers = Object.assign({}, message.crudFlow.headers || {}, create.headers || {});\n\n if (payload !== undefined) {\n message.payload = clone(payload);\n }\n\n const identityField = message.crudFlow.identityField || message.identityField || 'dsid';\n const keyValue = payload && typeof payload === 'object'\n ? extractPrimaryKey(payload, identityField)\n : undefined;\n\n if (keyValue !== undefined) {\n message.primaryKeyValue = keyValue;\n message.crudFlow.delete = message.crudFlow.delete || {};\n message.crudFlow.delete.actualKey = keyValue;\n }\n\n return message;\n}\n\nfunction selectCreatePayload(create) {\n if (create.payload && Object.keys(create.payload).length > 0) {\n return create.payload;\n }\n if (create.samplePayload) {\n return create.samplePayload;\n }\n return undefined;\n}\n\nfunction extractPrimaryKey(payload, identityField) {\n if (!payload) {\n return undefined;\n }\n if (Object.prototype.hasOwnProperty.call(payload, identityField)) {\n return payload[identityField];\n }\n if (Array.isArray(payload.data) && payload.data.length > 0 && payload.data[0][identityField] !== undefined) {\n return payload.data[0][identityField];\n }\n return undefined;\n}\n\nfunction mergeUrl(base, path) {\n const prefix = (base || '').replace(/\\/+$/, '');\n const suffix = (path || '').replace(/^\\/+/, '');\n if (!prefix) {\n return `/${suffix}`;\n }\n if (!suffix) {\n return prefix;\n }\n return `${prefix}/${suffix}`;\n}\n\nfunction clone(value) {\n return value == null ? value : JSON.parse(JSON.stringify(value));\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 750,
|
||
"y": 480,
|
||
"wires": [
|
||
[
|
||
"6b82573f9ffd406c",
|
||
"b8109ec603f2ae35"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "9130d5e40d2e8b53",
|
||
"type": "function",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "准备delete",
|
||
"func": "'use strict';\n\n/**\n * 依据 msg.crudFlow.delete 生成删除请求体,支持占位符 {{primaryKey}}。\n */\n\nreturn configureDeleteRequest(msg, node);\n\nfunction configureDeleteRequest(message, node) {\n if (!message.crudFlow || !message.crudFlow.delete) {\n return message;\n }\n\n const del = message.crudFlow.delete;\n const baseUrl = message.crudFlow.baseUrl || '';\n const primaryKeyValue = del.actualKey || message.primaryKeyValue || del.primaryKey || 'testid2';\n const method = (del.method || 'POST').toUpperCase();\n\n let path = del.path || '';\n let payload = null;\n\n if (method === 'DELETE' && path.includes('{')) {\n path = path.replace(/\\{[^}]+\\}/g, encodeURIComponent(primaryKeyValue));\n } else {\n const template = del.payloadTemplate || { version: '1.0.0', data: ['{{primaryKey}}'] };\n payload = materialiseTemplate(template, primaryKeyValue);\n }\n\n message.method = method;\n message.url = mergeUrl(baseUrl, path);\n message.headers = Object.assign({}, message.crudFlow.headers || {}, del.headers || {});\n\n if (payload !== null) {\n message.payload = payload;\n } else if (message.payload !== undefined) {\n delete message.payload;\n }\n\n return message;\n}\n\nfunction materialiseTemplate(template, primaryKeyValue) {\n const cloned = clone(template);\n return replacePlaceholder(cloned, primaryKeyValue);\n}\n\nfunction replacePlaceholder(value, primaryKeyValue) {\n if (typeof value === 'string') {\n return value.replace(/\\{\\{\\s*primaryKey\\s*\\}\\}/g, primaryKeyValue);\n }\n if (Array.isArray(value)) {\n return value.map(item => replacePlaceholder(item, primaryKeyValue));\n }\n if (value && typeof value === 'object') {\n const result = {};\n for (const key of Object.keys(value)) {\n result[key] = replacePlaceholder(value[key], primaryKeyValue);\n }\n return result;\n }\n return value;\n}\n\nfunction mergeUrl(base, path) {\n const prefix = (base || '').replace(/\\/+$/, '');\n const suffix = (path || '').replace(/^\\/+/, '');\n if (!prefix) {\n return `/${suffix}`;\n }\n if (!suffix) {\n return prefix;\n }\n return `${prefix}/${suffix}`;\n}\n\nfunction clone(value) {\n return value == null ? value : JSON.parse(JSON.stringify(value));\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 1570,
|
||
"y": 560,
|
||
"wires": [
|
||
[
|
||
"6b971983d782d570"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "ab1f2a98d3200f0d",
|
||
"type": "function",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "汇总",
|
||
"func": "'use strict';\n\n/**\n * 汇总 CRUD 执行结果,生成统一响应结构。\n */\n\nreturn summariseCrudResult(msg, node);\n\nfunction summariseCrudResult(message, node) {\n const identityField = (message.crudFlow && message.crudFlow.identityField) ||\n message.identityField || 'dsid';\n const primaryKey = (message.crudFlow && message.crudFlow.delete && message.crudFlow.delete.actualKey) ||\n message.primaryKeyValue || null;\n\n const listOk = message.listError ? false : true;\n const createOk = message.isCreated === undefined ? true : !!message.isCreated;\n const deleteOk = message.isDeleted === undefined ? true : !!message.isDeleted;\n\n const success = listOk && createOk && deleteOk;\n\n message.payload = {\n code: success ? 0 : 1,\n message: success ? '井创建删除流程测试成功' : '井创建删除流程部分失败',\n details: {\n listOk,\n createOk,\n deleteOk,\n identityField,\n primaryKey,\n },\n };\n\n return message;\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 2310,
|
||
"y": 640,
|
||
"wires": [
|
||
[
|
||
"608d38ac3750e9a9",
|
||
"35caa921acd6676e"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "35caa921acd6676e",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "汇总结果",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 2500,
|
||
"y": 580,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "091820a71299ef8e",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "请求内容",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "msg",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 540,
|
||
"y": 680,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "c2abaaef33a4e83a",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "请求结果",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 460,
|
||
"y": 820,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "b8109ec603f2ae35",
|
||
"type": "debug",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "debug 21",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "false",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 880,
|
||
"y": 540,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "5bd9db9eda3b1538",
|
||
"type": "file",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "",
|
||
"filename": "C:\\workspace\\create.txt",
|
||
"filenameType": "str",
|
||
"appendNewline": true,
|
||
"createDir": false,
|
||
"overwriteFile": "true",
|
||
"encoding": "none",
|
||
"x": 1210,
|
||
"y": 720,
|
||
"wires": [
|
||
[]
|
||
]
|
||
},
|
||
{
|
||
"id": "b84b523c19c4662b",
|
||
"type": "file",
|
||
"z": "2bbc1a949095492c",
|
||
"name": "",
|
||
"filename": "D:\\workspace\\zzlh\\node-red\\compiliance-js\\list_request.txt",
|
||
"filenameType": "str",
|
||
"appendNewline": true,
|
||
"createDir": true,
|
||
"overwriteFile": "true",
|
||
"encoding": "none",
|
||
"x": 670,
|
||
"y": 740,
|
||
"wires": [
|
||
[]
|
||
]
|
||
},
|
||
{
|
||
"id": "fd3672b8ebb314ee",
|
||
"type": "http in",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "单接口测试",
|
||
"url": "/crud_test",
|
||
"method": "post",
|
||
"upload": false,
|
||
"skipBodyParsing": false,
|
||
"swaggerDoc": "",
|
||
"x": 60,
|
||
"y": 60,
|
||
"wires": [
|
||
[
|
||
"211c041ab58c88f2"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "d666851b16cf6482",
|
||
"type": "http response",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "",
|
||
"statusCode": "msg.testcase.results",
|
||
"headers": {},
|
||
"x": 1120,
|
||
"y": 280,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "f414ed448fcf544b",
|
||
"type": "function",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "dms转化为oas",
|
||
"func": "'use strict';\n\nconst DEFAULT_OPENAPI_VERSION = '3.0.1';\nconst DEFAULT_API_VERSION = '1.0.0';\nconst BASE_PREFIX = 'https://www.dev.ideas.cnpc/api/dms';\nconst DEFAULT_SERVICE_ID = 'well_kd_wellbore_ideas01';\nconst DEFAULT_API_SEGMENT = 'v1';\nconst FALLBACK_BASE_URL = `${BASE_PREFIX}/${DEFAULT_SERVICE_ID}/${DEFAULT_API_SEGMENT}`;\nconst FALLBACK_HEADERS = {\n 'Content-Type': 'application/json',\n 'Authorization': '1',\n 'Dataregion': 'ZZLH',\n};\nconst SERVICE_DOMAIN_CATALOG = {\n 'well_kd_wellbore_ideas01': {\n name: '井筒',\n id: 'well_kd_wellbore_ideas01',\n keywords: ['wb', 'wb_dr', 'wb_ml', 'wb_wl', 'wb_tp', 'wb_dh', 'wb_fr'],\n },\n 'geo_kd_res_ideas01': {\n name: '采油气',\n id: 'geo_kd_res_ideas01',\n keywords: ['pc', 'pc_op', 'pc_oe', 'pc_ge'],\n },\n 'kd_cr_ideas01': {\n name: '分析化验',\n id: 'kd_cr_ideas01',\n keywords: ['cr', 'cr_se'],\n },\n 'kd_rs_ideas01': {\n name: '油气藏',\n id: 'kd_rs_ideas01',\n keywords: ['rs', 'rs_rd', 'rs_rm', 'rs_gs', 'rs_in'],\n },\n};\n\nlet schema;\ntry {\n // 优先使用 HTTP In 提供的 req.body.schema,缺省时回退到 msg.payload。\n const schemaInput = extractSchemaInput(msg);\n schema = parseSchema(schemaInput);\n} catch (error) {\n node.error(`DMS -> Swagger 解析失败:${error.message}`, msg);\n msg.error = error.message;\n return msg;\n}\n\nconst resourceTitle = typeof schema.title === 'string' && schema.title.trim()\n ? schema.title.trim()\n : 'DMS Resource';\n\nconst resourceName = pascalCase(resourceTitle);\nconst collectionName = pluralize(kebabCase(resourceTitle));\nconst identityField = Array.isArray(schema.identityId) && schema.identityId.length > 0\n ? String(schema.identityId[0])\n : 'id';\n\nconst schemaComponent = buildComponentSchema(resourceName, schema);\nconst identitySchema = schemaComponent.properties && schemaComponent.properties[identityField]\n ? clone(schemaComponent.properties[identityField])\n : { type: 'string' };\n\nconst crudConfig = Object.assign({}, msg.crudConfig || {});\n\nconst dmsMeta = extractDmsMeta(msg);\nmsg.dms_meta = dmsMeta;\n\nconst serviceInfo = resolveServiceInfo(dmsMeta && dmsMeta.domain);\nconst apiVersionSegment = DEFAULT_API_SEGMENT;\nconst serviceId = serviceInfo.id;\n\nconst { resourceSegment, versionSegment } = deriveResourceSegments(dmsMeta, schema, collectionName);\nconst listPath = buildListPath(resourceSegment, versionSegment);\nconst singlePath = buildSinglePath(resourceSegment);\nconst detailPath = buildDetailPath(resourceSegment, versionSegment, identityField);\nconst createRequestSchema = buildCreateRequestSchema(resourceName);\nconst updateRequestSchema = buildCreateRequestSchema(resourceName);\nconst deleteRequestSchema = buildDeleteRequestSchema(identityField);\nconst listRequestSchema = buildListRequestSchema();\nconst listResponseSchema = buildListResponseSchema(resourceName);\n\nconst openApiDocument = {\n openapi: DEFAULT_OPENAPI_VERSION,\n info: {\n title: `${resourceTitle} API`,\n version: DEFAULT_API_VERSION,\n description: schema.description || `${schema.$id || ''}`.trim(),\n 'x-dms-sourceId': schema.$id || undefined,\n },\n paths: buildCrudPaths({\n resourceName,\n identityField,\n identitySchema,\n listPath,\n createPath: singlePath,\n detailPath,\n createRequestSchema,\n updateRequestSchema,\n deleteRequestSchema,\n listRequestSchema,\n listResponseSchema,\n }),\n components: {\n schemas: {\n [resourceName]: schemaComponent,\n },\n },\n};\n\nif (!crudConfig.baseUrl) {\n crudConfig.baseUrl = `${BASE_PREFIX}/${serviceId}/${apiVersionSegment}`;\n}\n\nconst headers = Object.assign({}, FALLBACK_HEADERS, crudConfig.headers || {});\nconst dataRegionValue = extractDataRegion(schema, headers.Dataregion);\n\nif (dataRegionValue) {\n headers.Dataregion = dataRegionValue;\n crudConfig.dataRegion = dataRegionValue;\n}\n\ncrudConfig.headers = headers;\ncrudConfig.identityField = crudConfig.identityField || identityField;\n\ncrudConfig.service = crudConfig.service || {\n id: serviceId,\n name: serviceInfo.name,\n};\n\ncrudConfig.list = crudConfig.list || {};\nif (!crudConfig.list.path) {\n crudConfig.list.path = listPath;\n}\ncrudConfig.list.method = crudConfig.list.method || 'POST';\n\ncrudConfig.create = crudConfig.create || {};\nif (!crudConfig.create.path) {\n crudConfig.create.path = singlePath;\n}\ncrudConfig.create.method = crudConfig.create.method || 'POST';\n\ncrudConfig.delete = crudConfig.delete || {};\nif (!crudConfig.delete.path) {\n crudConfig.delete.path = singlePath;\n}\ncrudConfig.delete.method = crudConfig.delete.method || 'POST';\n\ncrudConfig.detailPath = crudConfig.detailPath || detailPath;\ncrudConfig.version = crudConfig.version || versionSegment;\n\nmsg.crudConfig = crudConfig;\nmsg.headers = Object.assign({}, headers);\n\nmsg.oas_def = openApiDocument;\nmsg.payload = openApiDocument;\nreturn msg;\n\nfunction extractDataRegion(dmsSchema, fallback) {\n if (!dmsSchema || typeof dmsSchema !== 'object') {\n return fallback;\n }\n\n if (typeof dmsSchema.dataRegion === 'string' && dmsSchema.dataRegion.trim()) {\n return dmsSchema.dataRegion.trim();\n }\n\n if (dmsSchema.defaultShow && Array.isArray(dmsSchema.defaultShow)) {\n const candidate = dmsSchema.defaultShow.find(item => item && typeof item === 'string' && item.toLowerCase().includes('dataregion'));\n if (candidate) {\n return fallback;\n }\n }\n\n const props = dmsSchema.properties;\n if (props && typeof props === 'object' && props.dataRegion) {\n if (typeof props.dataRegion.default === 'string' && props.dataRegion.default.trim()) {\n return props.dataRegion.default.trim();\n }\n }\n\n return fallback;\n}\n\nfunction extractSchemaInput(message) {\n if (message && message.req && message.req.body && typeof message.req.body === 'object') {\n if (message.req.body.dms_schema !== undefined) {\n return message.req.body.dms_schema;\n }\n if (looksLikeDmsSchema(message.req.body)) {\n return message.req.body;\n }\n }\n\n if (message && message.payload !== undefined) {\n if (message.payload && typeof message.payload === 'object') {\n if (message.payload.dms_schema !== undefined) {\n return message.payload.dms_schema;\n }\n if (looksLikeDmsSchema(message.payload)) {\n return message.payload;\n }\n }\n return message.payload;\n }\n\n throw new Error('未找到schema,请在请求体的schema字段或msg.payload中提供');\n}\n\nfunction parseSchema(source) {\n if (typeof source === 'string') {\n try {\n return JSON.parse(source);\n } catch (error) {\n throw new Error(`JSON 解析失败:${error.message}`);\n }\n }\n\n if (!source || typeof source !== 'object') {\n throw new Error('schema 必须是 DMS 定义对象或 JSON 字符串');\n }\n\n return source;\n}\n\nfunction buildComponentSchema(resourceName, dmsSchema) {\n const { properties = {}, required = [], groupView, identityId, naturalKey, defaultShow } = dmsSchema;\n const openApiProps = {};\n\n for (const [propName, propSchema] of Object.entries(properties)) {\n openApiProps[propName] = mapProperty(propSchema);\n }\n\n return {\n type: 'object',\n required: Array.isArray(required) ? required.slice() : [],\n properties: openApiProps,\n description: dmsSchema.description || dmsSchema.title || resourceName,\n 'x-dms-groupView': groupView || undefined,\n 'x-dms-identityId': identityId || undefined,\n 'x-dms-naturalKey': naturalKey || undefined,\n 'x-dms-defaultShow': defaultShow || undefined,\n };\n}\n\nfunction mapProperty(propSchema) {\n if (!propSchema || typeof propSchema !== 'object') {\n return { type: 'string' };\n }\n\n const result = {};\n\n const type = normalizeType(propSchema.type);\n result.type = type.type;\n if (type.format) {\n result.format = type.format;\n }\n if (type.items) {\n result.items = type.items;\n }\n\n if (propSchema.description) {\n result.description = propSchema.description;\n } else if (propSchema.title) {\n result.description = propSchema.title;\n }\n\n if (propSchema.enum) {\n result.enum = propSchema.enum.slice();\n }\n\n if (propSchema.default !== undefined) {\n result.default = propSchema.default;\n }\n\n if (propSchema.mask) {\n result['x-dms-mask'] = propSchema.mask;\n }\n if (propSchema.geom) {\n result['x-dms-geom'] = propSchema.geom;\n }\n if (propSchema.title) {\n result['x-dms-title'] = propSchema.title;\n }\n if (propSchema.type) {\n result['x-dms-originalType'] = propSchema.type;\n }\n\n return result;\n}\n\nfunction normalizeType(typeValue) {\n const normalized = typeof typeValue === 'string' ? typeValue.toLowerCase() : undefined;\n\n switch (normalized) {\n case 'number':\n case 'integer':\n case 'long':\n case 'float':\n case 'double':\n return { type: 'number' };\n case 'boolean':\n return { type: 'boolean' };\n case 'array':\n return { type: 'array', items: { type: 'string' } };\n case 'date':\n return { type: 'string', format: 'date' };\n case 'date-time':\n return { type: 'string', format: 'date-time' };\n case 'object':\n return { type: 'object' };\n case 'string':\n default:\n return { type: 'string' };\n }\n}\n\nfunction buildCrudPaths({\n resourceName,\n identityField,\n identitySchema,\n listPath,\n createPath,\n detailPath,\n createRequestSchema,\n updateRequestSchema,\n deleteRequestSchema,\n listRequestSchema,\n listResponseSchema,\n}) {\n const ref = `#/components/schemas/${resourceName}`;\n const paths = {};\n\n const normalisedListPath = normalisePath(listPath);\n const normalisedCreatePath = normalisePath(createPath);\n const normalisedDetailPath = detailPath ? normalisePath(detailPath) : null;\n\n paths[normalisedListPath] = {\n post: {\n operationId: `list${resourceName}s`,\n summary: `List ${resourceName} resources`,\n requestBody: {\n required: false,\n content: {\n 'application/json': {\n schema: listRequestSchema,\n },\n },\n },\n responses: {\n 200: {\n description: 'Successful response',\n content: {\n 'application/json': {\n schema: listResponseSchema || {\n type: 'array',\n items: { $ref: ref },\n },\n },\n },\n },\n },\n },\n };\n\n paths[normalisedCreatePath] = {\n post: {\n operationId: `create${resourceName}`,\n summary: `Create a ${resourceName}`,\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: createRequestSchema,\n },\n },\n },\n responses: {\n 201: {\n description: 'Created',\n content: {\n 'application/json': {\n schema: { $ref: ref },\n },\n },\n },\n },\n },\n put: {\n operationId: `update${resourceName}`,\n summary: `Update a ${resourceName}`,\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: updateRequestSchema,\n },\n },\n },\n responses: {\n 200: {\n description: 'Successful update',\n content: {\n 'application/json': {\n schema: { $ref: ref },\n },\n },\n },\n },\n },\n delete: {\n operationId: `delete${resourceName}`,\n summary: `Delete ${resourceName} resources`,\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: deleteRequestSchema,\n },\n },\n },\n responses: {\n 200: { description: 'Deleted' },\n },\n },\n };\n\n if (normalisedDetailPath) {\n paths[normalisedDetailPath] = {\n parameters: [\n {\n name: identityField,\n in: 'path',\n required: true,\n schema: identitySchema,\n },\n ],\n get: {\n operationId: `get${resourceName}`,\n summary: `Get a single ${resourceName}`,\n responses: {\n 200: {\n description: 'Successful response',\n content: {\n 'application/json': {\n schema: { $ref: ref },\n },\n },\n },\n 404: { description: `${resourceName} not found` },\n },\n },\n };\n }\n\n return paths;\n}\n\nfunction normalisePath(path) {\n if (!path) {\n return '/';\n }\n return path.startsWith('/') ? path : `/${path}`;\n}\n\nfunction pascalCase(input) {\n return input\n .replace(/[^a-zA-Z0-9]+/g, ' ')\n .split(' ')\n .filter(Boolean)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())\n .join('') || 'Resource';\n}\n\nfunction kebabCase(input) {\n return input\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[^a-zA-Z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .toLowerCase() || 'resource';\n}\n\nfunction pluralize(word) {\n if (word.endsWith('s')) {\n return word;\n }\n if (word.endsWith('y')) {\n return word.slice(0, -1) + 'ies';\n }\n return `${word}s`;\n}\n\nfunction clone(value) {\n return JSON.parse(JSON.stringify(value));\n}\n\nfunction looksLikeDmsSchema(candidate) {\n if (!candidate || typeof candidate !== 'object') {\n return false;\n }\n if (candidate.properties && typeof candidate.properties === 'object' && candidate.title) {\n return true;\n }\n if (candidate.$id && candidate.type && candidate.type.toLowerCase && candidate.type.toLowerCase() === 'object') {\n return true;\n }\n return false;\n}\n\nfunction extractDmsMeta(message) {\n const candidateReq = message && message.req && message.req.body && message.req.body.dms_meta;\n const parsedReq = parseMetaCandidate(candidateReq);\n if (parsedReq) {\n return parsedReq;\n }\n\n const candidateMsg = message && message.dms_meta;\n const parsedMsg = parseMetaCandidate(candidateMsg);\n if (parsedMsg) {\n return parsedMsg;\n }\n\n return null;\n}\n\nfunction parseMetaCandidate(candidate) {\n if (!candidate) {\n return null;\n }\n if (typeof candidate === 'string') {\n try {\n const value = JSON.parse(candidate);\n return parseMetaCandidate(value);\n } catch (err) {\n return null;\n }\n }\n if (typeof candidate === 'object') {\n return clone(candidate);\n }\n return null;\n}\n\nfunction resolveServiceInfo(domain) {\n if (!domain || typeof domain !== 'string') {\n return SERVICE_DOMAIN_CATALOG[DEFAULT_SERVICE_ID];\n }\n const normalized = domain.toLowerCase();\n let best = null;\n for (const entry of Object.values(SERVICE_DOMAIN_CATALOG)) {\n const matched = entry.keywords.some(keyword => {\n if (typeof keyword !== 'string') return false;\n const normKeyword = keyword.toLowerCase();\n return normalized.includes(normKeyword) || normKeyword.includes(normalized);\n });\n if (matched) {\n best = entry;\n break;\n }\n }\n return best || SERVICE_DOMAIN_CATALOG[DEFAULT_SERVICE_ID];\n}\n\nfunction deriveResourceSegments(meta, schema, collectionName) {\n const defaultResource = (collectionName || '').replace(/^\\//, '');\n let resourceSegment = defaultResource;\n let versionSegment = null;\n\n if (meta && typeof meta === 'object') {\n if (typeof meta.id === 'string' && meta.id.trim()) {\n const parts = meta.id.trim().split('.');\n if (parts.length > 0 && parts[0]) {\n resourceSegment = parts[0];\n }\n if (parts.length > 1) {\n versionSegment = parts.slice(1).join('.');\n }\n } else if (typeof meta.name === 'string' && meta.name.trim()) {\n resourceSegment = meta.name.trim();\n }\n }\n\n if (!resourceSegment && schema && schema.title) {\n resourceSegment = kebabCase(schema.title);\n }\n\n return {\n resourceSegment: resourceSegment || defaultResource || 'resource',\n versionSegment: versionSegment,\n };\n}\n\nfunction buildListPath(resourceSegment, versionSegment) {\n if (versionSegment) {\n return `/${resourceSegment}/${versionSegment}`;\n }\n return `/${resourceSegment}`;\n}\n\nfunction buildSinglePath(resourceSegment) {\n return `/${resourceSegment}`;\n}\n\nfunction buildDetailPath(resourceSegment, versionSegment, identityField) {\n if (versionSegment) {\n return `/${resourceSegment}/${versionSegment}/{${identityField}}`;\n }\n return `/${resourceSegment}/{${identityField}}`;\n}\n\nfunction buildCreateRequestSchema(resourceName) {\n const ref = `#/components/schemas/${resourceName}`;\n return {\n type: 'object',\n required: ['version', 'act', 'data'],\n properties: {\n version: { type: 'string', default: '1.0.0' },\n act: { type: 'integer', default: -1 },\n data: {\n type: 'array',\n items: { $ref: ref },\n },\n },\n };\n}\n\nfunction buildDeleteRequestSchema(identityField) {\n return {\n type: 'object',\n required: ['version', 'data'],\n properties: {\n version: { type: 'string', default: '1.0.0' },\n data: {\n type: 'array',\n items: {\n type: 'string',\n description: `Value of ${identityField}`,\n },\n },\n },\n };\n}\n\nfunction buildListRequestSchema() {\n return {\n type: 'object',\n properties: {\n version: { type: 'string', default: '1.0.0' },\n data: {\n type: 'object',\n properties: {\n pageNo: { type: 'integer', default: 1 },\n pageSize: { type: 'integer', default: 20 },\n isSearchCount: { type: 'boolean', default: true },\n filters: {\n type: 'array',\n items: { type: 'object' },\n },\n },\n },\n },\n };\n}\n\nfunction buildListResponseSchema(resourceName) {\n const ref = `#/components/schemas/${resourceName}`;\n return {\n type: 'object',\n properties: {\n code: { type: 'integer' },\n message: { type: 'string' },\n data: {\n type: 'object',\n properties: {\n list: {\n type: 'array',\n items: { $ref: ref },\n },\n total: { type: 'integer' },\n },\n },\n },\n };\n}\n\nfunction looksLikeDmsSchema(candidate) {\n if (!candidate || typeof candidate !== 'object') {\n return false;\n }\n if (candidate.properties && typeof candidate.properties === 'object' && candidate.title) {\n return true;\n }\n if (candidate.$id && candidate.type && candidate.type.toLowerCase && candidate.type.toLowerCase() === 'object') {\n return true;\n }\n return false;\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 300,
|
||
"y": 40,
|
||
"wires": [
|
||
[
|
||
"dfe6ed572461c4a5",
|
||
"6ab9403df07fcefa",
|
||
"9c84414e55654e6a"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "ab16f5ccd9266e28",
|
||
"type": "inject",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "dms",
|
||
"props": [
|
||
{
|
||
"p": "req.body.dms_schema",
|
||
"v": "{\"$id\":\"https://schema.ideas.cnpc/json/core-data/cd_well.1.0.0.json\",\"type\":\"Object\",\"title\":\"井\",\"schema\":\"http://json-schema.org/draft-07/schema#\",\"required\":[\"bsflag\",\"dataRegion\",\"createUserId\",\"orgId\",\"wellPurpose\",\"createDate\",\"wellLegalName\",\"dsid\",\"wellType\",\"updateUserId\",\"updateDate\",\"wellId\",\"wellCommonName\",\"projectId\"],\"groupView\":[{\"name\":\"groupByWellCommonName\",\"group\":[\"dataRegion\",\"wellCommonName\",\"wellId\"]},{\"name\":\"groupByPlatformName\",\"group\":[\"dataRegion\",\"platformName\"]}],\"identityId\":[\"dsid\"],\"naturalKey\":[\"dataRegion\",\"wellCommonName\"],\"properties\":{\"kb\":{\"type\":\"number\",\"title\":\"补心海拔\",\"description\":\"海拔(高程),补心海拔=地面海拔+补心高\"},\"egl\":{\"type\":\"number\",\"title\":\"地面海拔\",\"description\":\"地面海拔\"},\"kbd\":{\"type\":\"number\",\"title\":\"补心高度\",\"description\":\"补心高度\"},\"dsid\":{\"type\":\"string\",\"title\":\"DSID\",\"description\":\"主键ID\"},\"orgId\":{\"type\":\"string\",\"title\":\"机构ID\",\"description\":\"单位唯一标识符,关联CD_ORGANIZATION的主键\"},\"bsflag\":{\"type\":\"number\",\"title\":\"删除标识\",\"description\":\"填写数据逻辑删除标识,1=在用,-5=废弃\"},\"canton\":{\"type\":\"string\",\"title\":\"行政区名称\",\"description\":\"填写行政区代码对应的行政区名称\"},\"siteId\":{\"type\":\"string\",\"title\":\"物探工区ID\",\"description\":\"物探工区ID\"},\"wellId\":{\"type\":\"string\",\"title\":\"井ID\",\"description\":\"井标识符,非限定唯一。由EPDM系统自动产生维护,无需人工干预,是用于唯一标识EPDM系统的每一口井的内部机器码\"},\"orgName\":{\"type\":\"string\",\"title\":\"机构名称\",\"description\":\"单位名称,必填\"},\"remarks\":{\"type\":\"string\",\"title\":\"备注\",\"description\":\"备注\"},\"prodDate\":{\"type\":\"date\",\"title\":\"投产日期\",\"description\":\"投产日期\"},\"siteName\":{\"type\":\"string\",\"title\":\"物探工区名称\",\"description\":\"物探工区名称\"},\"spudDate\":{\"type\":\"date\",\"title\":\"开钻日期\",\"description\":\"主井筒的开钻日期\"},\"wellDesc\":{\"type\":\"string\",\"title\":\"曾用名\",\"description\":\"填写这口井的曾用名\"},\"wellType\":{\"type\":\"string\",\"title\":\"井型\",\"description\":\"属性规范值字段,引用属性代码WELL_TYPE下的属性值\"},\"checkDate\":{\"type\":\"date\",\"title\":\"审核日期\",\"description\":\"记录数据在本系统的审核时间,需精确到时分秒\"},\"dataGroup\":{\"type\":\"string\",\"title\":\"数据分组\",\"description\":\"数据分组\"},\"projectId\":{\"type\":\"string\",\"title\":\"地质单元ID\",\"description\":\"地质单元唯一标识符,根据井别不同,选择关联构造单元(探井)还是油气田单元(开发井),关联CD_GEO_UNIT表的主键\"},\"stationId\":{\"type\":\"string\",\"title\":\"站库ID\",\"description\":\"站库ID\"},\"wellState\":{\"type\":\"string\",\"title\":\"井状态\",\"description\":\"井状态\"},\"activityId\":{\"type\":\"string\",\"title\":\"项目ID\",\"description\":\"项目唯一标示符,关联CD_ACTIVITY表的主键\"},\"cantonCode\":{\"type\":\"string\",\"title\":\"行政区代码\",\"description\":\"属性规范值字段,引用属性代码CANTON下的属性值\"},\"createDate\":{\"type\":\"date\",\"title\":\"创建日期\",\"description\":\"记录数据在本系统的创建时间,需精确到时分秒\"},\"dataRegion\":{\"type\":\"string\",\"title\":\"油田标识\",\"description\":\"油田标识\"},\"dataSource\":{\"type\":\"string\",\"title\":\"数据来源\",\"description\":\"填写数据来源的表CODE\"},\"desgWellId\":{\"type\":\"string\",\"title\":\"设计井ID\",\"description\":\"设计井的唯一标识\"},\"energyType\":{\"type\":\"string\",\"title\":\"能源类型\",\"description\":\"描述本井生产的油气资源类型,如煤层气、致密气、页岩气等\"},\"platformId\":{\"type\":\"string\",\"title\":\"平台ID\",\"description\":\"平台ID\"},\"updateDate\":{\"type\":\"date\",\"title\":\"更新日期\",\"description\":\"记录数据在本系统最新的更新时间,需精确到时分秒,默认=创建时间\"},\"wellTypeId\":{\"type\":\"string\",\"title\":\"井型ID\",\"description\":\"属性规范值字段,引用属性代码WELL_TYPE下的属性值\"},\"abandonDate\":{\"type\":\"date\",\"title\":\"报废日期\",\"description\":\"报废日期\"},\"abondonType\":{\"type\":\"string\",\"title\":\"报废类型\",\"description\":\"报废类型\"},\"actualXAxis\":{\"geom\":\"Point.x.actual\",\"mask\":\"coordinate\",\"type\":\"number\",\"title\":\"实际X坐标,实际X坐标\"},\"actualYAxis\":{\"geom\":\"Point.y.actual\",\"mask\":\"coordinate\",\"type\":\"number\",\"title\":\"实际Y坐标,实际Y坐标\"},\"checkUserId\":{\"type\":\"string\",\"title\":\"审核用户\",\"description\":\"记录数据在本系统的审核用户\"},\"createAppId\":{\"type\":\"string\",\"title\":\"创建应用\",\"description\":\"填写数据来源的系统名\"},\"designXAxis\":{\"geom\":\"Point.x.design\",\"mask\":\"coordinate\",\"type\":\"number\",\"title\":\"设计X坐标,设计X坐标\"},\"designYAxis\":{\"geom\":\"Point.y.design\",\"mask\":\"coordinate\",\"type\":\"number\",\"title\":\"设计Y坐标,设计Y坐标\"},\"projectName\":{\"type\":\"string\",\"title\":\"地质单元名称\",\"description\":\"地质单元名称,填写地质单元的中文名称,该名称在整个油田公司内不能重名,必填\"},\"stationName\":{\"type\":\"string\",\"title\":\"站库名称\",\"description\":\"站库名称,必填\"},\"wellPurpose\":{\"type\":\"string\",\"title\":\"井别\",\"description\":\"属性规范值字段,引用属性代码WELL_PURPOSE下的属性值\"},\"activityName\":{\"type\":\"string\",\"title\":\"项目名称\",\"description\":\"项目名称,必填\"},\"completionMd\":{\"type\":\"number\",\"title\":\"完钻井深\",\"description\":\"完钻井深\"},\"createUserId\":{\"type\":\"string\",\"title\":\"创建用户\",\"description\":\"记录数据在本系统的创建用户\"},\"keyWellLevel\":{\"type\":\"string\",\"title\":\"重点井级别\",\"description\":\"重点井级别\"},\"platformName\":{\"type\":\"string\",\"title\":\"平台名称\",\"description\":\"平台名称\"},\"sourceDataId\":{\"type\":\"string\",\"title\":\"源库ID标识\",\"description\":\"存储数据来源的主键信息\"},\"structurePos\":{\"type\":\"string\",\"title\":\"构造位置\",\"description\":\"构造位置的描述\"},\"updateUserId\":{\"type\":\"string\",\"title\":\"更新用户\",\"description\":\"记录数据在本系统最新的更新用户,默认=创建用户\"},\"geoOffsetEast\":{\"geom\":\"Point.x.wellhead\",\"mask\":\"coordinate\",\"type\":\"number\",\"title\":\"井口横坐标\"},\"seismicLineNo\":{\"type\":\"string\",\"title\":\"井旁地震测线号\",\"description\":\"井旁地震测线号(勘探井)\"},\"wellLegalName\":{\"type\":\"string\",\"title\":\"拼音井号\",\"description\":\"规范的井号名称,井号名称命名规范请参考《主数据库技术标准》\"},\"wellPurposeId\":{\"type\":\"string\",\"title\":\"井别ID\",\"description\":\"属性规范值字段,引用属性代码WELL_PURPOSE下的属性值\"},\"completionDate\":{\"type\":\"date\",\"title\":\"完井日期\",\"description\":\"完井日期\"},\"geoDescription\":{\"type\":\"string\",\"title\":\"地理位置\",\"description\":\"地理位置的描述\"},\"geoOffsetNorth\":{\"geom\":\"Point.y.wellhead\",\"mask\":\"coordinate\",\"type\":\"number\",\"title\":\"井口纵坐标\"},\"wellCommonName\":{\"type\":\"string\",\"title\":\"井名\",\"description\":\"通用井名,来源于钻井公报的汉字井名,必填\"},\"endDrillingDate\":{\"type\":\"date\",\"title\":\"完钻日期\",\"description\":\"最后一个井筒的完钻日期\"},\"targetFormation\":{\"type\":\"string\",\"title\":\"目的层\",\"description\":\"目的层\"},\"completionMethod\":{\"type\":\"string\",\"title\":\"完井方法\",\"description\":\"完井方法\"},\"registrationDate\":{\"type\":\"date\",\"title\":\"注册日期\",\"description\":\"井位通知单下达日期\"},\"sourceCreateDate\":{\"type\":\"date\",\"title\":\"源头数据采集时间\",\"description\":\"记录源头系统采集数据的时间\"},\"coordinateSystemId\":{\"geom\":\"Point.srid.wellhead,Point.srid.design,Point.srid.actual\",\"type\":\"string\",\"title\":\"坐标系统ID\",\"description\":\"坐标系统的唯一标示符\"},\"completionFormation\":{\"type\":\"string\",\"title\":\"钻井完钻层位\",\"description\":\"钻井完钻层位\"},\"coordinateSystemName\":{\"type\":\"string\",\"title\":\"坐标系名称\",\"description\":\"坐标系名称\"},\"wellbaseGeoOffsetEast\":{\"type\":\"number\",\"title\":\"井底横坐标\",\"description\":\"井底横坐标\"},\"wellbaseGeoOffsetNorth\":{\"type\":\"number\",\"title\":\"井底纵坐标\",\"description\":\"井底纵坐标\"}},\"defaultShow\":[\"wellCommonName\"]}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "topic",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"p": "req.body.dms_meta",
|
||
"v": "{\"flow_state\":\"5\",\"domain\":\"wb_cd\",\"name\":\"cd_well\",\"add_tree\":\"Y\",\"id\":\"cd_well.1.0.0\",\"create_user\":\"admin\",\"title\":\"井基本信息\",\"type\":\"master-data\",\"version\":\"1.0.0\",\"update_date\":\"2025-10-2110:45:38\",\"tags\":[\"主数据\"]}",
|
||
"vt": "json"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"x": 70,
|
||
"y": 280,
|
||
"wires": [
|
||
[
|
||
"211c041ab58c88f2"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "dfe6ed572461c4a5",
|
||
"type": "debug",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "转化结果",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "msg",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 740,
|
||
"y": 20,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "a252afceaa16c14c",
|
||
"type": "catch",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "",
|
||
"scope": null,
|
||
"uncaught": false,
|
||
"x": 80,
|
||
"y": 460,
|
||
"wires": [
|
||
[
|
||
"1496f4d2a070111c"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "1496f4d2a070111c",
|
||
"type": "debug",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "debug 11",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "true",
|
||
"targetType": "full",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 260,
|
||
"y": 400,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "9a020a2d438e0d5e",
|
||
"type": "file",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "",
|
||
"filename": "D:\\workspace\\zzlh\\node-red\\compiliance-js\\dms2oas.txt",
|
||
"filenameType": "str",
|
||
"appendNewline": true,
|
||
"createDir": false,
|
||
"overwriteFile": "false",
|
||
"encoding": "none",
|
||
"x": 850,
|
||
"y": 220,
|
||
"wires": [
|
||
[]
|
||
]
|
||
},
|
||
{
|
||
"id": "6ab9403df07fcefa",
|
||
"type": "function",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "参数生成器",
|
||
"func": "'use strict';\nmsg.operationId =\"createResource\"\n/**\n * 根据 OpenAPI (Swagger) 规范为指定接口生成示例请求参数。\n *\n * 输入:\n * - msg.oas_def / msg.payload / msg.swagger: OpenAPI 3.x 文档对象或 JSON 字符串\n * - msg.operationId / msg.req.body.operationId: 目标 operationId(优先级最高)\n * - msg.path + msg.method: 目标路径与方法(可选)\n * - 如果以上字段均缺失且文档仅包含一个 operation,则默认使用该 operation\n *\n * 输出:\n * - msg.mock: {\n * operationId,\n * method,\n * path,\n * mediaType,\n * pathParams: {},\n * query: {},\n * headers: {},\n * cookies: {},\n * body: <示例请求体或 undefined>\n * }\n * - msg.payload 同步为 msg.mock 便于调试\n */\n\nlet openApi;\ntry {\n openApi = extractOpenApiDocument(msg);\n} catch (error) {\n node.error(`Swagger 自动填充初始化失败:${error.message}`, msg);\n msg.error = error.message;\n return msg;\n}\n\nconst operationCtx = resolveOperationContext(msg, openApi);\nif (!operationCtx.operation) {\n const message = `未找到匹配的接口定义(operationId=${operationCtx.requestedOperationId || '未指定'}, path=${operationCtx.requestedPath || '未指定'}, method=${operationCtx.requestedMethod || '未指定'})`;\n node.error(message, msg);\n msg.error = message;\n return msg;\n}\n\nconst parameterSamples = buildParameterSamples(operationCtx, openApi);\nconst bodySample = buildRequestBodySample(operationCtx.operation, openApi);\n\nmsg.mock = Object.assign({}, msg.mock, {\n operationId: operationCtx.operation.operationId || operationCtx.requestedOperationId || '',\n method: (operationCtx.method || '').toUpperCase(),\n path: operationCtx.path || '',\n mediaType: bodySample.mediaType,\n pathParams: parameterSamples.path,\n query: parameterSamples.query,\n headers: parameterSamples.header,\n cookies: parameterSamples.cookie,\n body: bodySample.payload,\n});\n\nmsg.payload = msg.mock;\nmsg.mockCandidates = operationCtx.candidates;\nif (operationCtx.autoSelected) {\n msg.mockAutoSelected = true;\n}\nreturn msg;\n\nfunction extractOpenApiDocument(message) {\n const candidate =\n message && message.oas_def ? message.oas_def :\n message && message.swagger ? message.swagger :\n message && message.payload ? message.payload :\n null;\n\n if (!candidate) {\n throw new Error('未提供 OpenAPI 文档(需要 msg.oas_def / msg.swagger / msg.payload)');\n }\n\n if (typeof candidate === 'string') {\n try {\n return JSON.parse(candidate);\n } catch (error) {\n throw new Error(`OpenAPI JSON 解析失败:${error.message}`);\n }\n }\n\n if (typeof candidate !== 'object') {\n throw new Error('OpenAPI 文档必须是对象或 JSON 字符串');\n }\n\n if (!candidate.paths || typeof candidate.paths !== 'object') {\n throw new Error('OpenAPI 文档缺少 paths 字段');\n }\n\n return candidate;\n}\n\nfunction resolveOperationContext(message, apiDoc) {\n const requestedOperationId =\n (message && message.operationId) ||\n (message && message.req && message.req.body && message.req.body.operationId) ||\n null;\n\n const requestedPath =\n (message && message.path) ||\n (message && message.req && message.req.body && message.req.body.path) ||\n null;\n\n const requestedMethodRaw =\n (message && message.method) ||\n (message && message.req && message.req.body && message.req.body.method) ||\n null;\n const requestedMethod = requestedMethodRaw ? String(requestedMethodRaw).toLowerCase() : null;\n\n const operations = enumerateOperations(apiDoc);\n\n let matched = null;\n if (requestedOperationId) {\n matched = operations.find(op => op.operation.operationId === requestedOperationId);\n }\n\n if (!matched && requestedPath && requestedMethod) {\n matched = operations.find(op => op.path === requestedPath && op.method === requestedMethod);\n }\n\n let autoSelected = false;\n if (!matched && operations.length > 0) {\n matched = operations[0];\n autoSelected = true;\n }\n\n return {\n operation: matched ? matched.operation : undefined,\n method: matched ? matched.method : undefined,\n pathItem: matched ? matched.pathItem : undefined,\n path: matched ? matched.path : undefined,\n autoSelected,\n candidates: operations.map(op => ({\n operationId: op.operation && op.operation.operationId ? op.operation.operationId : '',\n method: op.method ? op.method.toUpperCase() : '',\n path: op.path,\n summary: op.operation && op.operation.summary ? op.operation.summary : '',\n })),\n requestedOperationId,\n requestedPath,\n requestedMethod,\n };\n}\n\nfunction enumerateOperations(apiDoc) {\n const results = [];\n const validMethods = ['get', 'put', 'post', 'delete', 'patch', 'options', 'head', 'trace'];\n for (const path of Object.keys(apiDoc.paths || {})) {\n const pathItem = apiDoc.paths[path];\n if (!pathItem || typeof pathItem !== 'object') {\n continue;\n }\n for (const method of validMethods) {\n if (pathItem[method] && typeof pathItem[method] === 'object') {\n results.push({\n path,\n method,\n operation: pathItem[method],\n pathItem,\n });\n }\n }\n }\n return results;\n}\n\nfunction buildParameterSamples(operationContext, apiDoc) {\n const aggregated = {\n path: {},\n query: {},\n header: {},\n cookie: {},\n };\n\n const seen = new Set();\n const paramSources = [];\n if (Array.isArray(operationContext.pathItem && operationContext.pathItem.parameters)) {\n paramSources.push(operationContext.pathItem.parameters);\n }\n if (Array.isArray(operationContext.operation.parameters)) {\n paramSources.push(operationContext.operation.parameters);\n }\n\n for (const source of paramSources) {\n for (const param of source) {\n if (!param || typeof param !== 'object') {\n continue;\n }\n const name = param.name || '';\n const location = (param.in || 'query').toLowerCase();\n const key = `${location}:${name}`;\n if (!name || seen.has(key)) {\n continue;\n }\n seen.add(key);\n\n const resolvedParam = resolveMaybeRef(param, apiDoc);\n const targetBucket = aggregated[location] || aggregated.query;\n targetBucket[name] = generateParameterSample(resolvedParam, apiDoc, location);\n }\n }\n\n return aggregated;\n}\n\nfunction generateParameterSample(param, apiDoc, location) {\n if (!param || typeof param !== 'object') {\n return 'sample';\n }\n\n const example = pickExample(param);\n if (example !== undefined) {\n return example;\n }\n\n const schema = resolveMaybeRef(param.schema, apiDoc);\n const sample = generateSampleValue(schema, apiDoc);\n if (sample !== undefined) {\n return sample;\n }\n\n // fallback\n switch (location) {\n case 'path':\n return 'sample-id';\n case 'header':\n return 'sample-header';\n case 'cookie':\n return 'sample-cookie';\n default:\n return 'sample';\n }\n}\n\nfunction buildRequestBodySample(operation, apiDoc) {\n const resolvedBody = resolveMaybeRef(operation.requestBody, apiDoc);\n if (!resolvedBody || typeof resolvedBody !== 'object') {\n return { payload: undefined, mediaType: undefined };\n }\n\n const content = resolvedBody.content;\n if (!content || typeof content !== 'object') {\n return { payload: undefined, mediaType: undefined };\n }\n\n const preferredMediaTypes = [\n 'application/json',\n 'application/*+json',\n 'application/x-www-form-urlencoded',\n 'multipart/form-data',\n 'text/plain',\n ];\n\n let chosenMediaType = null;\n for (const mediaType of preferredMediaTypes) {\n if (content[mediaType]) {\n chosenMediaType = mediaType;\n break;\n }\n }\n if (!chosenMediaType) {\n const mediaKeys = Object.keys(content);\n chosenMediaType = mediaKeys.length > 0 ? mediaKeys[0] : null;\n }\n if (!chosenMediaType) {\n return { payload: undefined, mediaType: undefined };\n }\n\n const mediaObject = content[chosenMediaType];\n if (!mediaObject || typeof mediaObject !== 'object') {\n return { payload: undefined, mediaType: chosenMediaType };\n }\n\n if (mediaObject.example !== undefined) {\n return { payload: clone(mediaObject.example), mediaType: chosenMediaType };\n }\n if (mediaObject.examples && typeof mediaObject.examples === 'object') {\n const firstExample = Object.values(mediaObject.examples)[0];\n if (firstExample && typeof firstExample === 'object' && firstExample.value !== undefined) {\n return { payload: clone(firstExample.value), mediaType: chosenMediaType };\n }\n }\n\n const schema = resolveMaybeRef(mediaObject.schema, apiDoc);\n const payload = generateSampleValue(schema, apiDoc);\n return { payload, mediaType: chosenMediaType };\n}\n\nfunction pickExample(node) {\n if (!node || typeof node !== 'object') {\n return undefined;\n }\n if (node.example !== undefined) {\n return clone(node.example);\n }\n if (node.examples && typeof node.examples === 'object') {\n const first = Object.values(node.examples)[0];\n if (first && typeof first === 'object' && first.value !== undefined) {\n return clone(first.value);\n }\n }\n if (node.default !== undefined) {\n return clone(node.default);\n }\n return undefined;\n}\n\nfunction generateSampleValue(schema, apiDoc, depth = 0, seenRefs = new Set()) {\n if (!schema || typeof schema !== 'object') {\n return undefined;\n }\n if (depth > 8) {\n return undefined;\n }\n\n if (schema.example !== undefined) {\n return clone(schema.example);\n }\n if (schema.default !== undefined) {\n return clone(schema.default);\n }\n if (schema.const !== undefined) {\n return clone(schema.const);\n }\n if (Array.isArray(schema.enum) && schema.enum.length > 0) {\n return clone(schema.enum[0]);\n }\n\n if (schema.$ref) {\n if (seenRefs.has(schema.$ref)) {\n return undefined;\n }\n seenRefs.add(schema.$ref);\n const resolved = resolveMaybeRef(schema, apiDoc, seenRefs);\n return generateSampleValue(resolved, apiDoc, depth + 1, seenRefs);\n }\n\n if (Array.isArray(schema.oneOf) && schema.oneOf.length > 0) {\n return generateSampleValue(schema.oneOf[0], apiDoc, depth + 1, seenRefs);\n }\n if (Array.isArray(schema.anyOf) && schema.anyOf.length > 0) {\n return generateSampleValue(schema.anyOf[0], apiDoc, depth + 1, seenRefs);\n }\n if (Array.isArray(schema.allOf) && schema.allOf.length > 0) {\n const merged = schema.allOf\n .map(part => resolveMaybeRef(part, apiDoc, seenRefs))\n .filter(part => part && typeof part === 'object')\n .reduce((acc, part) => Object.assign(acc, part), {});\n return generateSampleValue(merged, apiDoc, depth + 1, seenRefs);\n }\n\n const type = inferSchemaType(schema);\n switch (type) {\n case 'object': {\n const result = {};\n if (schema.properties && typeof schema.properties === 'object') {\n for (const [key, value] of Object.entries(schema.properties)) {\n const resolvedChild = resolveMaybeRef(value, apiDoc, seenRefs);\n const childSample = generateSampleValue(resolvedChild, apiDoc, depth + 1, seenRefs);\n if (childSample !== undefined) {\n result[key] = childSample;\n }\n }\n }\n const required = Array.isArray(schema.required) ? schema.required : [];\n for (const propertyName of required) {\n if (!Object.prototype.hasOwnProperty.call(result, propertyName)) {\n result[propertyName] = pickFallbackByFormat({ type: 'string' });\n }\n }\n return Object.keys(result).length > 0 ? result : {};\n }\n case 'array': {\n const itemSchema = resolveMaybeRef(schema.items, apiDoc, seenRefs) || { type: 'string' };\n const itemSample = generateSampleValue(itemSchema, apiDoc, depth + 1, seenRefs);\n return itemSample !== undefined ? [itemSample] : [];\n }\n case 'integer':\n return Number.isInteger(schema.minimum) ? schema.minimum : 1;\n case 'number':\n if (schema.minimum !== undefined) {\n return typeof schema.minimum === 'number' ? schema.minimum : 0;\n }\n return 1;\n case 'boolean':\n return true;\n case 'string':\n default:\n return pickFallbackByFormat(schema);\n }\n}\n\nfunction inferSchemaType(schema) {\n if (!schema || typeof schema !== 'object') {\n return 'string';\n }\n if (schema.type) {\n return schema.type;\n }\n if (schema.properties) {\n return 'object';\n }\n if (schema.items) {\n return 'array';\n }\n return 'string';\n}\n\nfunction pickFallbackByFormat(schema) {\n const format = schema && schema.format ? schema.format.toLowerCase() : null;\n switch (format) {\n case 'date':\n return '2025-01-01';\n case 'date-time':\n return '2025-01-01T00:00:00Z';\n case 'email':\n return 'user@example.com';\n case 'uuid':\n return '00000000-0000-4000-8000-000000000000';\n case 'uri':\n case 'url':\n return 'https://example.com/resource';\n case 'byte':\n return Buffer.from('sample').toString('base64');\n case 'binary':\n return '<binary>';\n default:\n break;\n }\n\n const pattern = schema && schema.pattern;\n if (pattern && /[0-9]{4}-[0-9]{2}-[0-9]{2}/.test(pattern)) {\n return '2025-01-01';\n }\n\n return 'sample';\n}\n\nfunction resolveMaybeRef(node, apiDoc, seenRefs = new Set()) {\n if (!node || typeof node !== 'object') {\n return node;\n }\n if (!node.$ref) {\n return node;\n }\n\n const ref = node.$ref;\n if (seenRefs.has(ref)) {\n return {};\n }\n seenRefs.add(ref);\n\n const resolved = resolveRef(ref, apiDoc);\n if (!resolved || typeof resolved !== 'object') {\n return node;\n }\n\n const remainder = Object.assign({}, node);\n delete remainder.$ref;\n\n return Object.assign({}, clone(resolved), remainder);\n}\n\nfunction resolveRef(ref, apiDoc) {\n if (typeof ref !== 'string' || !ref.startsWith('#/')) {\n return null;\n }\n\n const tokens = ref.slice(2).split('/').map(unescapeRefToken);\n let current = apiDoc;\n for (const token of tokens) {\n if (current && typeof current === 'object' && Object.prototype.hasOwnProperty.call(current, token)) {\n current = current[token];\n } else {\n return null;\n }\n }\n return current;\n}\n\nfunction unescapeRefToken(token) {\n return token.replace(/~1/g, '/').replace(/~0/g, '~');\n}\n\nfunction clone(value) {\n return value == null ? value : JSON.parse(JSON.stringify(value));\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 570,
|
||
"y": 320,
|
||
"wires": [
|
||
[
|
||
"6e56a2ccd9fcaacc"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "6e56a2ccd9fcaacc",
|
||
"type": "debug",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "参数展示",
|
||
"active": false,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "mock",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 700,
|
||
"y": 360,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "60ce224bd2ed8e69",
|
||
"type": "function",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "准备llm输入",
|
||
"func": "'use strict';\n\n/**\n * Node-RED Function 节点脚本:准备大模型 HTTP 请求。\n * 将生成 prompt、HTTP 请求参数,并把上下文写入 msg.llmContext。\n */\n\nconst DEFAULT_API_KEY = 'sk-lbGrsUPL1iby86h554FaE536C343435dAa9bA65967A840B2';\nconst DEFAULT_BASE_URL = 'https://aiproxy.petrotech.cnpc/v1';\nconst DEFAULT_ENDPOINT_PATH = '/chat/completions';\nconst DEFAULT_MODEL = 'deepseek-v3';\n\nfunction prepareLlmRequest(msg, node) {\n if (!msg.operationId) {\n msg.operationId = 'createResource';\n }\n\n try {\n const apiDoc = extractOpenApiDocument(msg);\n const operationCtx = resolveOperationContext(msg, apiDoc);\n\n if (!operationCtx.operation) {\n const err = `未找到匹配的接口定义(operationId=${operationCtx.requestedOperationId || '未指定'}, path=${operationCtx.requestedPath || '未指定'}, method=${operationCtx.requestedMethod || '未指定'})`;\n node.error(err, msg);\n msg.error = err;\n return null;\n }\n\n const prompt = buildPrompt(operationCtx, apiDoc, msg);\n const requestPayload = buildRequestPayload(prompt, msg);\n\n const apiKey = (msg.llm && msg.llm.apiKey) || DEFAULT_API_KEY;\n const baseUrl = (msg.llm && msg.llm.baseUrl) || DEFAULT_BASE_URL;\n const endpointPath = (msg.llm && msg.llm.endpoint) || DEFAULT_ENDPOINT_PATH;\n\n const url = buildUrl(baseUrl, endpointPath);\n msg.method = 'POST';\n msg.url = url;\n msg.headers = Object.assign({}, msg.headers, {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n });\n msg.payload = requestPayload;\n msg.rejectUnauthorized = false;\n\n msg.llmContext = {\n prompt,\n operationCtx,\n provider: 'dashscope',\n requestConfig: {\n baseUrl,\n endpointPath,\n model: requestPayload.model,\n temperature: requestPayload.temperature,\n },\n };\n delete msg.error;\n return msg;\n } catch (error) {\n node.error(`LLM 请求准备失败:${error.message}`, msg);\n msg.error = error.message;\n return null;\n }\n}\n\nfunction extractOpenApiDocument(message) {\n const candidate =\n message && message.oas_def ? message.oas_def :\n message && message.swagger ? message.swagger :\n message && message.payload ? message.payload :\n null;\n\n if (!candidate) {\n throw new Error('未提供 OpenAPI 文档(需要 msg.oas_def / msg.swagger / msg.payload)');\n }\n\n if (typeof candidate === 'string') {\n try {\n return JSON.parse(candidate);\n } catch (error) {\n throw new Error(`OpenAPI JSON 解析失败:${error.message}`);\n }\n }\n\n if (typeof candidate !== 'object') {\n throw new Error('OpenAPI 文档必须是对象或 JSON 字符串');\n }\n\n if (!candidate.paths || typeof candidate.paths !== 'object') {\n throw new Error('OpenAPI 文档缺少 paths 字段');\n }\n\n return candidate;\n}\n\nfunction resolveOperationContext(message, apiDoc) {\n const requestedOperationId =\n (message && message.operationId) ||\n (message && message.req && message.req.body && message.req.body.operationId) ||\n null;\n\n const requestedPath =\n (message && message.path) ||\n (message && message.req && message.req.body && message.req.body.path) ||\n null;\n\n const requestedMethodRaw =\n (message && message.method) ||\n (message && message.req && message.req.body && message.req.body.method) ||\n null;\n const requestedMethod = requestedMethodRaw ? String(requestedMethodRaw).toLowerCase() : null;\n\n const operations = enumerateOperations(apiDoc);\n\n let matched = null;\n if (requestedOperationId) {\n matched = operations.find(op => op.operation.operationId === requestedOperationId);\n }\n\n if (!matched && requestedPath && requestedMethod) {\n matched = operations.find(op => op.path === requestedPath && op.method === requestedMethod);\n }\n\n let autoSelected = false;\n if (!matched && operations.length > 0) {\n matched = operations[0];\n autoSelected = true;\n }\n\n return {\n operation: matched ? matched.operation : undefined,\n method: matched ? matched.method : undefined,\n pathItem: matched ? matched.pathItem : undefined,\n path: matched ? matched.path : undefined,\n autoSelected,\n candidates: operations.map(op => ({\n operationId: op.operation && op.operation.operationId ? op.operation.operationId : '',\n method: op.method ? op.method.toUpperCase() : '',\n path: op.path,\n summary: op.operation && op.operation.summary ? op.operation.summary : '',\n })),\n requestedOperationId,\n requestedPath,\n requestedMethod,\n };\n}\n\nfunction enumerateOperations(apiDoc) {\n const results = [];\n const validMethods = ['get', 'put', 'post', 'delete', 'patch', 'options', 'head', 'trace'];\n for (const path of Object.keys(apiDoc.paths || {})) {\n const pathItem = apiDoc.paths[path];\n if (!pathItem || typeof pathItem !== 'object') {\n continue;\n }\n for (const method of validMethods) {\n if (pathItem[method] && typeof pathItem[method] === 'object') {\n results.push({\n path,\n method,\n operation: pathItem[method],\n pathItem,\n });\n }\n }\n }\n return results;\n}\n\nfunction buildPrompt(operationCtx, apiDoc, message) {\n const operation = operationCtx.operation;\n const method = (operationCtx.method || '').toUpperCase();\n const path = operationCtx.path || '';\n const summary = operation.summary || '';\n const description = operation.description || '';\n\n const parameters = gatherParameters(operationCtx, apiDoc);\n const requestBodySchema = gatherRequestBodySchema(operation, apiDoc);\n\n const userNotes = message && message.prompt ? String(message.prompt) : '';\n const requiredFields = extractRequiredFields(requestBodySchema, apiDoc);\n const requiredFieldsText = requiredFields.length > 0\n ? requiredFields.map(field => `- ${field}`).join('\\n')\n : '';\n\n const lines = [];\n lines.push('你是一名负责生成 HTTP 接口测试参数的助手,请严格输出以下结构的 JSON:');\n lines.push('{');\n lines.push(' \"pathParams\": { ... },');\n lines.push(' \"query\": { ... },');\n lines.push(' \"headers\": { ... },');\n lines.push(' \"cookies\": { ... },');\n lines.push(' \"body\": { ... },');\n lines.push(' \"notes\": \"...\"');\n lines.push('}');\n lines.push('未用到的分区请返回空对象,不要在 JSON 外输出任何文字。');\n lines.push('');\n lines.push('body.data[0] 必须遵守:');\n lines.push('- 覆盖 schema 中声明的全部必填字段,并给出符合字段类型/格式的真实感样例值。');\n lines.push('- 非必填字段也尽可能全覆盖');\n lines.push('- 不得遗漏必填字段;若 schema 内有嵌套对象/数组的必填字段,同样要补齐。');\n lines.push('- 保持数值、日期、字符串等格式,只在 schema 无提示时使用 \"sample\"、\"2025-01-01\" 等占位值。');\n if (requiredFieldsText) {\n lines.push('必填字段清单(必须全部出现在 body.data[0] 中):');\n lines.push(requiredFieldsText);\n }\n lines.push('');\n lines.push('若 schema 中存在数组元素或复合结构的必填字段,也要为这些子字段提供值。');\n lines.push('');\n lines.push(`Target operation: ${method} ${path}`);\n\n if (summary) {\n lines.push(`Summary: ${summary}`);\n }\n if (description) {\n lines.push(`Description: ${description}`);\n }\n if (parameters.length > 0) {\n lines.push('Parameters:');\n for (const param of parameters) {\n lines.push(`- [${param.in}] ${param.name}: ${param.type || 'any'}${param.required ? ' (required)' : ''}${param.description ? ` - ${param.description}` : ''}`);\n }\n } else {\n lines.push('Parameters: none defined.');\n }\n\n if (requestBodySchema) {\n lines.push('Request body schema (JSON Schema excerpt):');\n lines.push(indentSnippet(JSON.stringify(requestBodySchema, null, 2), 2));\n } else {\n lines.push('Request body: not defined.');\n }\n\n if (userNotes) {\n lines.push('');\n lines.push('User notes:');\n lines.push(userNotes);\n }\n\n return lines.join('\\n');\n}\n\nfunction gatherParameters(operationCtx, apiDoc) {\n const aggregated = [];\n const seen = new Set();\n\n const sources = [];\n if (Array.isArray(operationCtx.pathItem && operationCtx.pathItem.parameters)) {\n sources.push(operationCtx.pathItem.parameters);\n }\n if (Array.isArray(operationCtx.operation.parameters)) {\n sources.push(operationCtx.operation.parameters);\n }\n\n for (const list of sources) {\n for (const param of list) {\n if (!param || typeof param !== 'object') {\n continue;\n }\n const resolved = resolveMaybeRef(param, apiDoc);\n const key = `${resolved.in}:${resolved.name}`;\n if (!resolved.name || seen.has(key)) {\n continue;\n }\n seen.add(key);\n aggregated.push({\n name: resolved.name,\n in: resolved.in || 'query',\n required: !!resolved.required,\n description: resolved.description || '',\n type: resolved.schema ? inferFriendlyType(resolved.schema, apiDoc) : '',\n });\n }\n }\n\n return aggregated;\n}\n\nfunction gatherRequestBodySchema(operation, apiDoc) {\n const requestBody = resolveMaybeRef(operation.requestBody, apiDoc);\n if (!requestBody || typeof requestBody !== 'object' || !requestBody.content) {\n return null;\n }\n\n const content = requestBody.content;\n const mediaType = Object.keys(content).find(key => key.includes('json')) || Object.keys(content)[0];\n if (!mediaType) {\n return null;\n }\n\n const mediaObject = resolveMaybeRef(content[mediaType], apiDoc);\n if (!mediaObject || typeof mediaObject !== 'object' || !mediaObject.schema) {\n return null;\n }\n\n const schema = resolveMaybeRef(mediaObject.schema, apiDoc);\n return resolveSchemaDeep(schema, apiDoc);\n}\n\nfunction inferFriendlyType(schema, apiDoc) {\n if (!schema) {\n return '';\n }\n const resolved = resolveMaybeRef(schema, apiDoc);\n if (!resolved || typeof resolved !== 'object') {\n return '';\n }\n\n if (resolved.type) {\n if (resolved.type === 'array' && resolved.items) {\n const itemType = inferFriendlyType(resolved.items, apiDoc) || 'any';\n return `[${itemType}]`;\n }\n return resolved.type;\n }\n\n if (resolved.enum && Array.isArray(resolved.enum)) {\n return `enum(${resolved.enum.slice(0, 3).join(', ')}${resolved.enum.length > 3 ? ', …' : ''})`;\n }\n\n if (resolved.properties) {\n return 'object';\n }\n\n return '';\n}\n\nfunction buildRequestPayload(prompt, message) {\n const systemPrompt = (message.llm && message.llm.systemPrompt) ||\n 'You write JSON only. Focus on realistic values for testing HTTP APIs.';\n\n const model = (message.llm && message.llm.model) || DEFAULT_MODEL;\n const temperature = (message.llm && typeof message.llm.temperature === 'number')\n ? message.llm.temperature : 0.2;\n\n return {\n model,\n temperature,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: prompt },\n ],\n };\n}\n\nfunction buildUrl(baseUrl, endpointPath) {\n const normalizedBase = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;\n const path = endpointPath.startsWith('/') ? endpointPath.slice(1) : endpointPath;\n return `${normalizedBase}${path}`;\n}\n\nfunction resolveMaybeRef(node, apiDoc) {\n if (!node || typeof node !== 'object') {\n return node;\n }\n if (!node.$ref) {\n return node;\n }\n\n const resolved = resolveRef(node.$ref, apiDoc);\n if (!resolved || typeof resolved !== 'object') {\n return node;\n }\n\n const remainder = Object.assign({}, node);\n delete remainder.$ref;\n return Object.assign({}, clone(resolved), remainder);\n}\n\nfunction resolveRef(ref, apiDoc) {\n if (typeof ref !== 'string' || !ref.startsWith('#/')) {\n return null;\n }\n\n const tokens = ref.slice(2).split('/').map(unescapeRefToken);\n let current = apiDoc;\n for (const token of tokens) {\n if (current && typeof current === 'object' && Object.prototype.hasOwnProperty.call(current, token)) {\n current = current[token];\n } else {\n return null;\n }\n }\n return current;\n}\n\nfunction unescapeRefToken(token) {\n return token.replace(/~1/g, '/').replace(/~0/g, '~');\n}\n\nfunction indentSnippet(text, indentLevel, maxLength) {\n const trimmed = maxLength && text.length > maxLength ? `${text.slice(0, maxLength)}…` : text;\n const indent = ' '.repeat(indentLevel * 2);\n return trimmed.split('\\n').map(line => `${indent}${line}`).join('\\n');\n}\n\nfunction clone(value) {\n return value == null ? value : JSON.parse(JSON.stringify(value));\n}\n\nfunction extractRequiredFields(schema, apiDoc) {\n const result = new Set();\n collectRequiredFields(schema, apiDoc, result, '');\n return Array.from(result);\n}\n\nfunction collectRequiredFields(schema, apiDoc, result, pathPrefix) {\n if (!schema || typeof schema !== 'object') {\n return;\n }\n\n if (schema.$ref) {\n const resolved = resolveRef(schema.$ref, apiDoc);\n if (!resolved) {\n return;\n }\n collectRequiredFields(resolveSchemaDeep(resolved, apiDoc), apiDoc, result, pathPrefix);\n return;\n }\n\n if (Array.isArray(schema.required) && schema.properties && typeof schema.properties === 'object') {\n for (const key of schema.required) {\n const nextPath = pathPrefix ? `${pathPrefix}.${key}` : key;\n result.add(nextPath);\n collectRequiredFields(schema.properties[key], apiDoc, result, nextPath);\n }\n }\n\n if (schema.type === 'array' && schema.items) {\n const nextPrefix = pathPrefix ? `${pathPrefix}[]` : '[]';\n collectRequiredFields(schema.items, apiDoc, result, nextPrefix);\n }\n\n if (schema.allOf && Array.isArray(schema.allOf)) {\n for (const part of schema.allOf) {\n collectRequiredFields(part, apiDoc, result, pathPrefix);\n }\n }\n}\n\nfunction resolveSchemaDeep(schema, apiDoc, seen = new Set()) {\n if (!schema || typeof schema !== 'object') {\n return schema;\n }\n\n if (schema.$ref) {\n const ref = schema.$ref;\n if (seen.has(ref)) {\n return {};\n }\n seen.add(ref);\n const resolved = resolveRef(ref, apiDoc);\n if (!resolved) {\n return schema;\n }\n const merged = Object.assign({}, clone(resolved), clone(schema));\n delete merged.$ref;\n return resolveSchemaDeep(merged, apiDoc, seen);\n }\n\n const cloned = clone(schema);\n\n if (cloned.properties && typeof cloned.properties === 'object') {\n for (const key of Object.keys(cloned.properties)) {\n cloned.properties[key] = resolveSchemaDeep(cloned.properties[key], apiDoc, new Set(seen));\n }\n }\n\n if (cloned.items) {\n cloned.items = resolveSchemaDeep(cloned.items, apiDoc, new Set(seen));\n }\n\n if (cloned.allOf && Array.isArray(cloned.allOf)) {\n cloned.allOf = cloned.allOf.map(item => resolveSchemaDeep(item, apiDoc, new Set(seen)));\n }\n\n if (cloned.oneOf && Array.isArray(cloned.oneOf)) {\n cloned.oneOf = cloned.oneOf.map(item => resolveSchemaDeep(item, apiDoc, new Set(seen)));\n }\n\n if (cloned.anyOf && Array.isArray(cloned.anyOf)) {\n cloned.anyOf = cloned.anyOf.map(item => resolveSchemaDeep(item, apiDoc, new Set(seen)));\n }\n\n return cloned;\n}\n\nreturn prepareLlmRequest(msg, node);\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 410,
|
||
"y": 560,
|
||
"wires": [
|
||
[
|
||
"540a5771113ca5dd",
|
||
"d87dfcf0db518c75",
|
||
"2634bd2864185df0"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "e87c2057c28d5eee",
|
||
"type": "debug",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "llm参数展示",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "mock",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 790,
|
||
"y": 420,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "20818ab4243934bf",
|
||
"type": "function",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "处理llm返回",
|
||
"func": "'use strict';\n\n/**\n * Node-RED Function 节点脚本:处理大模型 HTTP 响应。\n * 将响应解析为 mock 数据并写回 msg。\n */\n\nfunction processLlmResponse(msg, node) {\n const context = msg.llmContext || {};\n if (!context.prompt || !context.operationCtx) {\n const err = 'LLM 上下文缺失,无法解析响应';\n node.error(err, msg);\n msg.error = err;\n return msg;\n }\n\n if (msg.statusCode && (msg.statusCode < 200 || msg.statusCode >= 300)) {\n const err = `大模型接口返回状态码 ${msg.statusCode}`;\n node.error(err, msg);\n msg.error = err;\n return msg;\n }\n\n let raw = msg.payload;\n if (Buffer.isBuffer(raw)) {\n raw = raw.toString('utf8');\n }\n if (typeof raw === 'string') {\n try {\n raw = JSON.parse(raw);\n } catch (error) {\n node.error(`无法解析大模型响应:${error.message}`, msg);\n msg.error = error.message;\n return msg;\n }\n }\n\n try {\n const parsed = parseModelResponse(raw);\n\n msg.llmRaw = raw;\n msg.mock = Object.assign({}, msg.mock, parsed.mock);\n msg.mockSource = context.provider || 'dashscope';\n msg.mockPrompt = context.prompt;\n msg.mockCandidates = context.operationCtx.candidates;\n msg.mockAutoSelected = !!context.operationCtx.autoSelected;\n msg.payload = msg.mock;\n\n delete msg.llmContext;\n if (msg.headers && msg.headers.Authorization) {\n delete msg.headers.Authorization;\n }\n\n delete msg.error;\n return msg;\n } catch (error) {\n node.error(`大模型参数生成失败:${error.message}`, msg);\n msg.error = error.message;\n return msg;\n }\n}\n\nfunction parseModelResponse(response) {\n if (!response || typeof response !== 'object') {\n throw new Error('大模型响应为空或不是对象');\n }\n\n if (response.mock && typeof response.mock === 'object') {\n return { mock: response.mock };\n }\n\n const choices = Array.isArray(response.choices) ? response.choices : [];\n const firstChoice = choices[0];\n const message = firstChoice && firstChoice.message ? firstChoice.message : null;\n const content = message && typeof message === 'object' ? message.content : null;\n\n if (!content) {\n throw new Error('响应中缺少 choices[0].message.content');\n }\n\n let mockObject;\n if (typeof content === 'string') {\n try {\n mockObject = JSON.parse(content);\n } catch (err) {\n throw new Error(`无法解析模型返回的 JSON:${err.message},原始文本:${content}`);\n }\n } else if (Array.isArray(content)) {\n const jsonPart = content.find(part => part.type === 'output_text' || part.type === 'text' || part.type === 'json');\n const text = jsonPart && jsonPart.text ? jsonPart.text : null;\n if (!text) {\n throw new Error('响应内容不是字符串,且未找到可解析的文本段');\n }\n try {\n mockObject = JSON.parse(text);\n } catch (err) {\n throw new Error(`无法解析模型返回的 JSON:${err.message},原始文本:${text}`);\n }\n } else {\n throw new Error('模型返回的 message.content 既不是字符串也不是文本片段数组');\n }\n\n if (!mockObject || typeof mockObject !== 'object') {\n throw new Error('模型返回的 JSON 不是对象');\n }\n\n const normalisedMock = {\n pathParams: mockObject.pathParams || mockObject.path_parameters || {},\n query: mockObject.query || mockObject.query_params || {},\n headers: mockObject.headers || {},\n cookies: mockObject.cookies || {},\n body: mockObject.body || {},\n notes: mockObject.notes || '',\n };\n\n return { mock: normalisedMock };\n}\n\nreturn processLlmResponse(msg, node);\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 730,
|
||
"y": 520,
|
||
"wires": [
|
||
[
|
||
"e87c2057c28d5eee",
|
||
"31d5f84e4c6f69f6",
|
||
"541979a1c635ec37"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "540a5771113ca5dd",
|
||
"type": "http request",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "",
|
||
"method": "POST",
|
||
"ret": "txt",
|
||
"paytoqs": "ignore",
|
||
"url": "",
|
||
"tls": "",
|
||
"persist": false,
|
||
"proxy": "",
|
||
"insecureHTTPParser": false,
|
||
"authType": "",
|
||
"senderr": false,
|
||
"headers": [],
|
||
"x": 580,
|
||
"y": 600,
|
||
"wires": [
|
||
[
|
||
"20818ab4243934bf",
|
||
"44fc6e2d405c2a78"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "44fc6e2d405c2a78",
|
||
"type": "debug",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "请求",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 710,
|
||
"y": 720,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "9c84414e55654e6a",
|
||
"type": "function",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "构建CRUD计划",
|
||
"func": "'use strict';\n\n/**\n * 构建 CRUD 执行动作所需的指令集合,保存于 msg.crudFlow。\n * 约定 operationId 采用 dms→oas 转换后默认的命名:\n * list<ResourceName>s / create<ResourceName> / delete<ResourceName>\n * 允许使用 msg.crudConfig.* 进行覆盖。\n */\nconst FALLBACK_BASE_URL = 'https://www.dev.ideas.cnpc/api/dms/well_kd_wellbore_ideas01/v1';\nconst DEFAULT_LIST_PAYLOAD = {\n version: '1.0.0',\n data: [],\n pageNo: 1,\n pageSize: 20,\n isSearchCount: true,\n};\nconst DEFAULT_CREATE_SAMPLE = {\n version: '1.0.0',\n act: -1,\n data: [\n {\n dsid: 'testid2',\n wellId: 'WELL-zzlhTEST-002',\n wellCommonName: 'zzlh测试用井名',\n wellLegalName: 'zzlh-test-01',\n wellPurpose: '开发井',\n wellType: '直井',\n dataRegion: 'ZZLH',\n projectId: 'PROJ-ZZLH-001',\n projectName: 'zzlh测试地质单元',\n orgId: 'ORG-ZZLH-01',\n orgName: 'zzlh采油厂',\n bsflag: 1,\n wellState: '生产中',\n spudDate: '2024-01-15',\n completionDate: '2024-05-20',\n prodDate: '2024-06-01',\n egl: 145.5,\n kbd: 5.2,\n kb: 150.7,\n actualXAxis: 550123.45,\n actualYAxis: 4998765.32,\n coordinateSystemName: 'zzlh测试坐标系',\n geoDescription: '位于zzlh测试区域',\n remarks: '这是一口用于系统测试的生产井。',\n createUserId: 'testuser001',\n createDate: '2025-09-12T10:00:00Z',\n updateUserId: 'testuser001',\n updateDate: '2025-09-12T10:00:00Z',\n },\n ],\n};\nconst DEFAULT_DELETE_TEMPLATE = {\n version: '1.0.0',\n data: ['{{primaryKey}}'],\n};\n\nreturn buildCrudPlan(msg, node);\n\nfunction buildCrudPlan(message, node) {\n const requestBody = getRequestBody(message);\n const crudConfig = mergeDeep({}, requestBody.crudConfig || {}, message.crudConfig || {});\n const apiDoc = normaliseOpenApi(message, crudConfig, requestBody, node);\n const operations = collectOperations(apiDoc);\n\n const listOp = pickOperation('list', operations, crudConfig);\n const createOp = pickOperation('create', operations, crudConfig);\n const deleteOp = pickOperation('delete', operations, crudConfig);\n\n if (!listOp || !createOp || !deleteOp) {\n const missing = [\n !listOp ? 'list' : null,\n !createOp ? 'create' : null,\n !deleteOp ? 'delete' : null,\n ].filter(Boolean).join(', ');\n const errMsg = `未能在 OpenAPI 文档中找到必要的 CRUD 操作:${missing}`;\n node.error(errMsg, message);\n message.error = errMsg;\n return null;\n }\n\n const resourceName = determineResourceName([createOp, listOp, deleteOp]) || 'Resource';\n const identityField = crudConfig.identityField ||\n requestBody.identityField ||\n selectIdentityField(apiDoc, crudConfig) ||\n 'dsid';\n const baseUrl = trimTrailingSlash(\n crudConfig.baseUrl ||\n requestBody.baseUrl ||\n message.baseUrl ||\n FALLBACK_BASE_URL\n );\n const headers = mergeDeep({}, requestBody.headers || {}, crudConfig.headers || {});\n\n const listConfig = mergeDeep(\n {},\n { payload: clone(DEFAULT_LIST_PAYLOAD) },\n requestBody.list || {},\n crudConfig.list || {}\n );\n const createConfig = mergeDeep(\n {},\n { payload: clone(DEFAULT_CREATE_SAMPLE) },\n requestBody.create || {},\n crudConfig.create || {}\n );\n const deleteConfig = mergeDeep(\n {},\n { payloadTemplate: clone(DEFAULT_DELETE_TEMPLATE) },\n requestBody.delete || {},\n crudConfig.delete || {}\n );\n\n if (requestBody.dataRegion) {\n headers.Dataregion = requestBody.dataRegion;\n }\n\n message.crudFlow = {\n resourceName,\n identityField,\n baseUrl,\n headers,\n list: Object.assign({\n operationId: listOp.operationId,\n method: listOp.method,\n path: listOp.path,\n }, listConfig),\n create: Object.assign({\n operationId: createOp.operationId,\n method: createOp.method,\n path: createOp.path,\n samplePayload: clone(DEFAULT_CREATE_SAMPLE),\n }, createConfig),\n delete: Object.assign({\n operationId: deleteOp.operationId,\n method: deleteOp.method,\n path: deleteOp.path,\n }, deleteConfig),\n openapi: apiDoc,\n };\n\n delete message.crudConfig;\n if (message.baseUrl) {\n delete message.baseUrl;\n }\n if (message.headers && !Object.keys(message.headers).length) {\n delete message.headers;\n }\n\n message.oas_def = apiDoc;\n delete message.error;\n return message;\n}\n\nfunction normaliseOpenApi(message, crudConfig, requestBody, node) {\n let candidate =\n crudConfig.openapi ||\n requestBody.openapi ||\n message.oas_def ||\n message.swagger ||\n message.payload;\n if (typeof candidate === 'string') {\n try {\n candidate = JSON.parse(candidate);\n } catch (err) {\n throw new Error(`OpenAPI JSON 解析失败:${err.message}`);\n }\n }\n if (!candidate || typeof candidate !== 'object') {\n throw new Error('未提供合法的 OpenAPI 文档');\n }\n if (!candidate.paths || typeof candidate.paths !== 'object' || Object.keys(candidate.paths).length === 0) {\n throw new Error('OpenAPI 文档缺少 paths 定义');\n }\n return candidate;\n}\n\nfunction collectOperations(apiDoc) {\n const operations = [];\n const allowed = ['get', 'post', 'put', 'delete', 'patch', 'options', 'head', 'trace'];\n for (const path of Object.keys(apiDoc.paths)) {\n const pathItem = apiDoc.paths[path];\n if (!pathItem || typeof pathItem !== 'object') {\n continue;\n }\n for (const method of Object.keys(pathItem)) {\n if (!allowed.includes(method.toLowerCase())) {\n continue;\n }\n const operation = pathItem[method];\n if (!operation || typeof operation !== 'object') {\n continue;\n }\n operations.push({\n operationId: operation.operationId || '',\n summary: operation.summary || '',\n description: operation.description || '',\n method: method.toUpperCase(),\n path,\n operation,\n });\n }\n }\n return operations;\n}\n\nfunction pickOperation(kind, operations, crudConfig) {\n const overrideId =\n (crudConfig[kind] && crudConfig[kind].operationId) ||\n crudConfig[`${kind}OperationId`];\n if (overrideId) {\n return operations.find(op => op.operationId === overrideId);\n }\n\n const matcher = getDefaultMatcher(kind);\n let matched = operations.find(op => matcher.test(op.operationId));\n if (matched) {\n return matched;\n }\n\n // 兜底策略\n switch (kind) {\n case 'list':\n return operations.find(op => op.method === 'GET') || null;\n case 'create':\n return operations.find(op => op.method === 'POST') || null;\n case 'delete':\n return operations.find(op => op.method === 'DELETE') ||\n operations.find(op => op.method === 'POST' && /delete/i.test(op.operationId)) ||\n null;\n default:\n return null;\n }\n}\n\nfunction getDefaultMatcher(kind) {\n switch (kind) {\n case 'list':\n return /^list[A-Z].*s$/;\n case 'create':\n return /^create[A-Z].*/;\n case 'delete':\n return /^delete[A-Z].*/;\n default:\n return /^$/;\n }\n}\n\nfunction determineResourceName(candidates) {\n for (const item of candidates) {\n const opId = item && item.operationId ? item.operationId : '';\n let match;\n if ((match = opId.match(/^list([A-Z].*)s$/))) {\n return match[1];\n }\n if ((match = opId.match(/^create([A-Z].*)$/))) {\n return match[1];\n }\n if ((match = opId.match(/^delete([A-Z].*)$/))) {\n return match[1];\n }\n }\n return null;\n}\n\nfunction selectIdentityField(apiDoc, crudConfig) {\n if (crudConfig.identityField) {\n return crudConfig.identityField;\n }\n if (apiDoc.components && apiDoc.components.schemas) {\n for (const schemaName of Object.keys(apiDoc.components.schemas)) {\n const schema = apiDoc.components.schemas[schemaName];\n if (!schema || typeof schema !== 'object') {\n continue;\n }\n const identity = Array.isArray(schema['x-dms-identityId']) ? schema['x-dms-identityId'][0] : null;\n if (identity) {\n return identity;\n }\n }\n }\n if (apiDoc.components && apiDoc.components.schemas) {\n for (const schemaName of Object.keys(apiDoc.components.schemas)) {\n const schema = apiDoc.components.schemas[schemaName];\n if (schema && schema.properties && Object.prototype.hasOwnProperty.call(schema.properties, 'dsid')) {\n return 'dsid';\n }\n }\n }\n return 'dsid';\n}\n\nfunction trimTrailingSlash(url) {\n if (!url) {\n return '';\n }\n return url.replace(/\\/+$/, '');\n}\n\nfunction getRequestBody(message) {\n if (message && message.req && message.req.body && typeof message.req.body === 'object') {\n return message.req.body;\n }\n return {};\n}\n\nfunction clone(value) {\n return value == null ? value : JSON.parse(JSON.stringify(value));\n}\n\nfunction mergeDeep(target, ...sources) {\n for (const source of sources) {\n if (!isPlainObject(source)) {\n continue;\n }\n for (const key of Object.keys(source)) {\n const value = source[key];\n if (value === undefined) {\n continue;\n }\n if (isPlainObject(value)) {\n const base = isPlainObject(target[key]) ? target[key] : {};\n target[key] = mergeDeep({}, base, value);\n } else {\n target[key] = clone(value);\n }\n }\n }\n return target;\n}\n\nfunction isPlainObject(value) {\n return Object.prototype.toString.call(value) === '[object Object]';\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 480,
|
||
"y": 380,
|
||
"wires": [
|
||
[
|
||
"76358bc84ac6e3e1"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "76358bc84ac6e3e1",
|
||
"type": "function",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "设置create上下文",
|
||
"func": "'use strict';\n\n/**\n * 为参数生成器 / LLM 设定目标的 operationId/path,并清理 msg.mock。\n */\n\nreturn setCreateContext(msg, node);\n\nfunction setCreateContext(message, node) {\n if (!message.crudFlow || !message.crudFlow.create) {\n const err = '缺少 crudFlow.create 配置,无法准备创建操作';\n node.error(err, message);\n message.error = err;\n return null;\n }\n\n const create = message.crudFlow.create;\n message.operationId = create.operationId;\n message.method = create.method || 'POST';\n message.path = create.path;\n message.mock = {};\n\n if (create.prompt) {\n message.prompt = create.prompt;\n }\n if (!message.oas_def && message.crudFlow.openapi) {\n message.oas_def = message.crudFlow.openapi;\n }\n\n delete message.error;\n return message;\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 170,
|
||
"y": 580,
|
||
"wires": [
|
||
[
|
||
"4b89069c3dcb949e"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "31d5f84e4c6f69f6",
|
||
"type": "function",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "缓存create结果",
|
||
"func": "'use strict';\n\n/**\n * 将 LLM 生成的创建请求体写回 crudFlow,提取主键供删除步骤使用。\n */\n\nreturn storeCreateResult(msg, node);\n\nfunction storeCreateResult(message, node) {\n if (!message.crudFlow || !message.crudFlow.create) {\n return message;\n }\n\n const identityField = message.crudFlow.identityField || message.identityField || 'dsid';\n const mockBody = extractMockBody(message);\n\n if (mockBody) {\n message.crudFlow.create.payload = clone(mockBody);\n } else if (!message.crudFlow.create.payload) {\n node.warn('未从模型生成创建参数,继续使用默认样例', message);\n message.crudFlow.create.payload = clone(message.crudFlow.create.samplePayload || {});\n }\n\n const payload = message.crudFlow.create.payload || {};\n const primaryKeyValue = payload[identityField];\n if (primaryKeyValue !== undefined) {\n message.primaryKeyValue = primaryKeyValue;\n message.crudFlow.delete = message.crudFlow.delete || {};\n message.crudFlow.delete.actualKey = primaryKeyValue;\n }\n\n // delete message.mock;\n // delete message.mockCandidates;\n // delete message.mockPrompt;\n // delete message.mockSource;\n // delete message.mockAutoSelected;\n // delete message.llmContext;\n // delete message.llmRaw;\n // delete message.prompt;\n // delete message.method;\n // delete message.path;\n // delete message.url;\n // delete message.headers;\n // delete message.statusCode;\n // delete message.payload;\n\n return message;\n}\n\nfunction extractMockBody(message) {\n if (message && message.mock && typeof message.mock === 'object' && message.mock.body && typeof message.mock.body === 'object') {\n return message.mock.body;\n }\n if (message && message.payload && typeof message.payload === 'object' && !Array.isArray(message.payload)) {\n return message.payload;\n }\n return null;\n}\n\nfunction clone(value) {\n return value == null ? value : JSON.parse(JSON.stringify(value));\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 1160,
|
||
"y": 700,
|
||
"wires": [
|
||
[
|
||
"cfa3c9b06af4840e",
|
||
"80da1e9b993f73dc"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "cfa3c9b06af4840e",
|
||
"type": "debug",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "缓存结果",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 1060,
|
||
"y": 460,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "80da1e9b993f73dc",
|
||
"type": "link out",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "link out 1",
|
||
"mode": "link",
|
||
"links": [
|
||
"6c06fbe60248c14c"
|
||
],
|
||
"x": 1215,
|
||
"y": 520,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "d87dfcf0db518c75",
|
||
"type": "debug",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "llm请求内容",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "payload",
|
||
"targetType": "msg",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 510,
|
||
"y": 680,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "2634bd2864185df0",
|
||
"type": "file",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "",
|
||
"filename": "C:\\workspace\\llmin.txt",
|
||
"filenameType": "str",
|
||
"appendNewline": true,
|
||
"createDir": false,
|
||
"overwriteFile": "true",
|
||
"encoding": "none",
|
||
"x": 500,
|
||
"y": 740,
|
||
"wires": [
|
||
[]
|
||
]
|
||
},
|
||
{
|
||
"id": "541979a1c635ec37",
|
||
"type": "file",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "",
|
||
"filename": "C:\\workspace\\llmout.txt",
|
||
"filenameType": "str",
|
||
"appendNewline": true,
|
||
"createDir": false,
|
||
"overwriteFile": "true",
|
||
"encoding": "none",
|
||
"x": 970,
|
||
"y": 640,
|
||
"wires": [
|
||
[]
|
||
]
|
||
},
|
||
{
|
||
"id": "5231ed8a796d6f17",
|
||
"type": "function",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "分页参数检查",
|
||
"func": "'use strict';\n\nconst TEST_METADATA = {\n id: 'TC-DMS-PAGINATION-001',\n name: '分页参数检查',\n description: \"检查API请求参数中是否包含标准分页参数:pageNo、pageSize和isSearchCount。只有名称含有'查询'、'列表'等并且不含有'详情'一类的API才应用此验证。\",\n severity: 'MEDIUM',\n tags: ['pagination', 'params', 'backend-guide'],\n skip_execution: true,\n};\n\nconst globalApiSpec = msg && msg.oas_def ? msg.oas_def : null;\n\nif (!globalApiSpec || typeof globalApiSpec !== 'object' || !globalApiSpec.paths) {\n const errorMessage = '分页参数检查无法运行:msg.oas_def 缺失、不是对象或不包含 paths';\n node.error(errorMessage, msg);\n msg.error = errorMessage;\n msg.testcase = {\n metadata: TEST_METADATA,\n applies: false,\n skipped: true,\n results: [],\n };\n return msg;\n}\n\nconst allResults = [];\nlet testApplied = false;\n\n// 遍历所有路径和方法\nfor (const path in globalApiSpec.paths) {\n if (Object.prototype.hasOwnProperty.call(globalApiSpec.paths, path)) {\n const pathItem = globalApiSpec.paths[path];\n for (const method in pathItem) {\n if (Object.prototype.hasOwnProperty.call(pathItem, method) && ['get', 'post', 'put', 'delete', 'patch', 'options', 'head'].includes(method.toLowerCase())) {\n\n const operation = pathItem[method];\n if (!operation || typeof operation !== 'object') continue;\n\n // 构建 endpointSpec\n const endpointSpec = buildEndpointSpec(path, method, pathItem, operation);\n\n // 检查测试是否适用\n if (shouldApplyTest(endpointSpec)) {\n testApplied = true;\n const evaluation = runPaginationCheck(endpointSpec, globalApiSpec);\n allResults.push(...evaluation.results);\n }\n }\n }\n }\n}\n\nmsg.testcase = {\n metadata: TEST_METADATA,\n applies: testApplied,\n skipped: !testApplied,\n results: allResults,\n};\nmsg.payload = msg.testcase;\nreturn msg;\n\nfunction buildEndpointSpec(path, method, pathItem, operation) {\n const combinedParameters = [];\n if (Array.isArray(pathItem.parameters)) {\n combinedParameters.push(...pathItem.parameters);\n }\n if (Array.isArray(operation.parameters)) {\n combinedParameters.push(...operation.parameters);\n }\n\n const spec = deepClone(operation);\n spec.method = method;\n spec.path = path;\n if (combinedParameters.length > 0) {\n spec.parameters = mergeParameters(combinedParameters);\n }\n return spec;\n}\n\nfunction shouldApplyTest(spec) {\n const method = (spec.method || '').toLowerCase();\n if (method !== 'get' && method !== 'post') {\n return false;\n }\n\n const path = spec.path || '';\n const summary = spec.summary || '';\n const description = spec.description || '';\n const operationId = spec.operationId || '';\n\n const includeKeywords = ['查询', '列表', '分页', 'page', 'list', 'query', 'search', 'find'];\n const excludeKeywords = ['详情', '明细', 'detail', 'info', 'get by id', 'getbyid', '查看'];\n\n const apiDescriptionText = `${summary} ${description} ${operationId} ${path}`.toLowerCase();\n\n const containsInclude = includeKeywords.some(keyword => apiDescriptionText.includes(keyword.toLowerCase()));\n if (!containsInclude) {\n return false;\n }\n\n // 检查是否是获取单个资源的 'get'\n if (method === 'get' && path.includes('{') && path.includes('}')) {\n const getByIdHints = ['get', 'detail', 'info', '查看'];\n const isGetById = getByIdHints.some(hint => apiDescriptionText.includes(hint));\n if (isGetById) return false;\n }\n\n const containsExclude = excludeKeywords.some(keyword => apiDescriptionText.includes(keyword.toLowerCase()));\n return !containsExclude;\n}\n\nfunction extractEndpointSpec(message, apiSpec) {\n // This function is no longer needed as we iterate through all paths and methods.\n // Kept for potential future use if a single endpoint needs to be targeted.\n return null;\n}\n\nfunction resolveEndpointInfo(message) {\n // This function is no longer needed as we iterate through all paths and methods.\n return null;\n}\n\nfunction mergeParameters(parameters) {\n const merged = new Map();\n for (const param of parameters) {\n if (!param || typeof param !== 'object') {\n continue;\n }\n const name = param.name || '';\n const location = param.in || '';\n const key = `${location}:${name}`;\n if (!merged.has(key)) {\n merged.set(key, deepClone(param));\n }\n }\n return Array.from(merged.values());\n}\n\nfunction runPaginationCheck(spec, apiSpec) {\n const results = [];\n const path = spec.path || '';\n const method = (spec.method || '').toLowerCase();\n\n let foundPageNo = false;\n let foundPageSize = false;\n let foundIsSearchCount = false;\n\n if (Array.isArray(spec.parameters)) {\n for (const param of spec.parameters) {\n if (!param || typeof param !== 'object') {\n continue;\n }\n const location = (param.in || '').toLowerCase();\n if (location !== 'query') {\n continue;\n }\n\n if (param.name === 'pageNo') {\n foundPageNo = true;\n } else if (param.name === 'pageSize') {\n foundPageSize = true;\n } else if (param.name === 'isSearchCount') {\n foundIsSearchCount = true;\n }\n }\n }\n\n if (method === 'post' && spec.requestBody && typeof spec.requestBody === 'object') {\n const { content } = spec.requestBody;\n if (content && typeof content === 'object') {\n for (const mediaType of Object.keys(content)) {\n const mediaDefinition = content[mediaType];\n if (!mediaDefinition || typeof mediaDefinition !== 'object') {\n continue;\n }\n const resolvedSchema = resolveSchema(mediaDefinition.schema, apiSpec);\n const properties = resolvedSchema && resolvedSchema.properties;\n if (!properties || typeof properties !== 'object') {\n continue;\n }\n\n if (Object.prototype.hasOwnProperty.call(properties, 'pageNo')) {\n foundPageNo = true;\n }\n if (Object.prototype.hasOwnProperty.call(properties, 'pageSize')) {\n foundPageSize = true;\n }\n if (Object.prototype.hasOwnProperty.call(properties, 'isSearchCount')) {\n foundIsSearchCount = true;\n }\n }\n }\n }\n\n if (foundPageNo && foundPageSize && foundIsSearchCount) {\n results.push(makePassedResult('API请求包含所有标准分页参数:pageNo、pageSize和isSearchCount', {\n path,\n method: method.toUpperCase(),\n }));\n } else {\n const missingParams = [];\n if (!foundPageNo) missingParams.push('pageNo');\n if (!foundPageSize) missingParams.push('pageSize');\n if (!foundIsSearchCount) missingParams.push('isSearchCount');\n\n results.push(makeFailedResult(`API请求缺少标准分页参数:${missingParams.join(', ')}`, {\n path,\n method: method.toUpperCase(),\n missing_params: missingParams,\n found_params: {\n pageNo: foundPageNo,\n pageSize: foundPageSize,\n isSearchCount: foundIsSearchCount,\n },\n }));\n }\n\n return { results };\n}\n\nfunction deepClone(value) {\n return value == null ? value : JSON.parse(JSON.stringify(value));\n}\n\nfunction makePassedResult(message, details) {\n return {\n passed: true,\n message,\n details: details || {},\n };\n}\n\nfunction makeFailedResult(message, details) {\n return {\n passed: false,\n message,\n details: details || {},\n };\n}\n\nfunction resolveSchema(schemaOrRef, apiSpec) {\n if (!schemaOrRef || typeof schemaOrRef !== 'object') {\n return {};\n }\n\n if (!schemaOrRef.$ref) {\n return schemaOrRef;\n }\n\n if (!apiSpec || typeof apiSpec !== 'object') {\n return schemaOrRef;\n }\n\n const refPath = schemaOrRef.$ref;\n if (typeof refPath !== 'string' || !refPath.startsWith('#/')) {\n return schemaOrRef;\n }\n\n const tokens = refPath.slice(2).split('/').map(unescapeRefToken);\n let current = apiSpec;\n for (const token of tokens) {\n if (current && Object.prototype.hasOwnProperty.call(current, token)) {\n current = current[token];\n } else {\n return schemaOrRef;\n }\n }\n\n return current && typeof current === 'object' ? current : schemaOrRef;\n}\n\nfunction unescapeRefToken(token) {\n return token.replace(/~1/g, '/').replace(/~0/g, '~');\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 720,
|
||
"y": 160,
|
||
"wires": [
|
||
[
|
||
"d666851b16cf6482"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "ad6438b1a6e76365",
|
||
"type": "inject",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "schema",
|
||
"props": [
|
||
{
|
||
"p": "req.body.schema",
|
||
"v": "{\"openapi\":\"3.0.1\",\"info\":{\"title\":\"井 API\",\"version\":\"1.0.0\",\"description\":\"https://schema.ideas.cnpc/json/core-data/cd_well.1.0.0.json\",\"x-dms-sourceId\":\"https://schema.ideas.cnpc/json/core-data/cd_well.1.0.0.json\"},\"paths\":{\"/cd_well/1.0.0\":{\"post\":{\"operationId\":\"listResources\",\"summary\":\"List Resource resources\",\"requestBody\":{\"required\":false,\"content\":{\"application/json\":{\"schema\":{\"type\":\"object\",\"properties\":{\"version\":{\"type\":\"string\",\"default\":\"1.0.0\"},\"data\":{\"type\":\"object\",\"properties\":{\"pageNo\":{\"type\":\"integer\",\"default\":1},\"pageSize\":{\"type\":\"integer\",\"default\":20},\"isSearchCount\":{\"type\":\"boolean\",\"default\":true},\"filters\":{\"type\":\"array\",\"items\":{\"type\":\"object\"}}}}}}}}},\"responses\":{\"200\":{\"description\":\"Successful response\",\"content\":{\"application/json\":{\"schema\":{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"integer\"},\"message\":{\"type\":\"string\"},\"data\":{\"type\":\"object\",\"properties\":{\"list\":{\"type\":\"array\",\"items\":{\"$ref\":\"#/components/schemas/Resource\"}},\"total\":{\"type\":\"integer\"}}}}}}}}}}},\"/cd_well\":{\"post\":{\"operationId\":\"createResource\",\"summary\":\"Create a Resource\",\"requestBody\":{\"required\":true,\"content\":{\"application/json\":{\"schema\":{\"type\":\"object\",\"required\":[\"version\",\"act\",\"data\"],\"properties\":{\"version\":{\"type\":\"string\",\"default\":\"1.0.0\"},\"act\":{\"type\":\"integer\",\"default\":-1},\"data\":{\"type\":\"array\",\"items\":{\"$ref\":\"#/components/schemas/Resource\"}}}}}}},\"responses\":{\"201\":{\"description\":\"Created\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/Resource\"}}}}}},\"put\":{\"operationId\":\"updateResource\",\"summary\":\"Update a Resource\",\"requestBody\":{\"required\":true,\"content\":{\"application/json\":{\"schema\":{\"type\":\"object\",\"required\":[\"version\",\"act\",\"data\"],\"properties\":{\"version\":{\"type\":\"string\",\"default\":\"1.0.0\"},\"act\":{\"type\":\"integer\",\"default\":-1},\"data\":{\"type\":\"array\",\"items\":{\"$ref\":\"#/components/schemas/Resource\"}}}}}}},\"responses\":{\"200\":{\"description\":\"Successful update\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/Resource\"}}}}}},\"delete\":{\"operationId\":\"deleteResource\",\"summary\":\"Delete Resource resources\",\"requestBody\":{\"required\":true,\"content\":{\"application/json\":{\"schema\":{\"type\":\"object\",\"required\":[\"version\",\"data\"],\"properties\":{\"version\":{\"type\":\"string\",\"default\":\"1.0.0\"},\"data\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"description\":\"Value of dsid\"}}}}}}},\"responses\":{\"200\":{\"description\":\"Deleted\"}}}},\"/cd_well/1.0.0/{dsid}\":{\"parameters\":[{\"name\":\"dsid\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\",\"description\":\"主键ID\",\"x-dms-title\":\"DSID\",\"x-dms-originalType\":\"string\"}}],\"get\":{\"operationId\":\"getResource\",\"summary\":\"Get a single Resource\",\"responses\":{\"200\":{\"description\":\"Successful response\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/Resource\"}}}},\"404\":{\"description\":\"Resource not found\"}}}}},\"components\":{\"schemas\":{\"Resource\":{\"type\":\"object\",\"required\":[\"bsflag\",\"dataRegion\",\"createUserId\",\"orgId\",\"wellPurpose\",\"createDate\",\"wellLegalName\",\"dsid\",\"wellType\",\"updateUserId\",\"updateDate\",\"wellId\",\"wellCommonName\",\"projectId\"],\"properties\":{\"kb\":{\"type\":\"number\",\"description\":\"海拔(高程),补心海拔=地面海拔+补心高\",\"x-dms-title\":\"补心海拔\",\"x-dms-originalType\":\"number\"},\"egl\":{\"type\":\"number\",\"description\":\"地面海拔\",\"x-dms-title\":\"地面海拔\",\"x-dms-originalType\":\"number\"},\"kbd\":{\"type\":\"number\",\"description\":\"补心高度\",\"x-dms-title\":\"补心高度\",\"x-dms-originalType\":\"number\"},\"dsid\":{\"type\":\"string\",\"description\":\"主键ID\",\"x-dms-title\":\"DSID\",\"x-dms-originalType\":\"string\"},\"orgId\":{\"type\":\"string\",\"description\":\"单位唯一标识符,关联CD_ORGANIZATION的主键\",\"x-dms-title\":\"机构ID\",\"x-dms-originalType\":\"string\"},\"bsflag\":{\"type\":\"number\",\"description\":\"填写数据逻辑删除标识,1=在用,-5=废弃\",\"x-dms-title\":\"删除标识\",\"x-dms-originalType\":\"number\"},\"canton\":{\"type\":\"string\",\"description\":\"填写行政区代码对应的行政区名称\",\"x-dms-title\":\"行政区名称\",\"x-dms-originalType\":\"string\"},\"siteId\":{\"type\":\"string\",\"description\":\"物探工区ID\",\"x-dms-title\":\"物探工区ID\",\"x-dms-originalType\":\"string\"},\"wellId\":{\"type\":\"string\",\"description\":\"井标识符,非限定唯一。由EPDM系统自动产生维护,无需人工干预,是用于唯一标识EPDM系统的每一口井的内部机器码\",\"x-dms-title\":\"井ID\",\"x-dms-originalType\":\"string\"},\"orgName\":{\"type\":\"string\",\"description\":\"单位名称,必填\",\"x-dms-title\":\"机构名称\",\"x-dms-originalType\":\"string\"},\"remarks\":{\"type\":\"string\",\"description\":\"备注\",\"x-dms-title\":\"备注\",\"x-dms-originalType\":\"string\"},\"prodDate\":{\"type\":\"string\",\"format\":\"date\",\"description\":\"投产日期\",\"x-dms-title\":\"投产日期\",\"x-dms-originalType\":\"date\"},\"siteName\":{\"type\":\"string\",\"description\":\"物探工区名称\",\"x-dms-title\":\"物探工区名称\",\"x-dms-originalType\":\"string\"},\"spudDate\":{\"type\":\"string\",\"format\":\"date\",\"description\":\"主井筒的开钻日期\",\"x-dms-title\":\"开钻日期\",\"x-dms-originalType\":\"date\"},\"wellDesc\":{\"type\":\"string\",\"description\":\"填写这口井的曾用名\",\"x-dms-title\":\"曾用名\",\"x-dms-originalType\":\"string\"},\"wellType\":{\"type\":\"string\",\"description\":\"属性规范值字段,引用属性代码WELL_TYPE下的属性值\",\"x-dms-title\":\"井型\",\"x-dms-originalType\":\"string\"},\"checkDate\":{\"type\":\"string\",\"format\":\"date\",\"description\":\"记录数据在本系统的审核时间,需精确到时分秒\",\"x-dms-title\":\"审核日期\",\"x-dms-originalType\":\"date\"},\"dataGroup\":{\"type\":\"string\",\"description\":\"数据分组\",\"x-dms-title\":\"数据分组\",\"x-dms-originalType\":\"string\"},\"projectId\":{\"type\":\"string\",\"description\":\"地质单元唯一标识符,根据井别不同,选择关联构造单元(探井)还是油气田单元(开发井),关联CD_GEO_UNIT表的主键\",\"x-dms-title\":\"地质单元ID\",\"x-dms-originalType\":\"string\"},\"stationId\":{\"type\":\"string\",\"description\":\"站库ID\",\"x-dms-title\":\"站库ID\",\"x-dms-originalType\":\"string\"},\"wellState\":{\"type\":\"string\",\"description\":\"井状态\",\"x-dms-title\":\"井状态\",\"x-dms-originalType\":\"string\"},\"activityId\":{\"type\":\"string\",\"description\":\"项目唯一标示符,关联CD_ACTIVITY表的主键\",\"x-dms-title\":\"项目ID\",\"x-dms-originalType\":\"string\"},\"cantonCode\":{\"type\":\"string\",\"description\":\"属性规范值字段,引用属性代码CANTON下的属性值\",\"x-dms-title\":\"行政区代码\",\"x-dms-originalType\":\"string\"},\"createDate\":{\"type\":\"string\",\"format\":\"date\",\"description\":\"记录数据在本系统的创建时间,需精确到时分秒\",\"x-dms-title\":\"创建日期\",\"x-dms-originalType\":\"date\"},\"dataRegion\":{\"type\":\"string\",\"description\":\"油田标识\",\"x-dms-title\":\"油田标识\",\"x-dms-originalType\":\"string\"},\"dataSource\":{\"type\":\"string\",\"description\":\"填写数据来源的表CODE\",\"x-dms-title\":\"数据来源\",\"x-dms-originalType\":\"string\"},\"desgWellId\":{\"type\":\"string\",\"description\":\"设计井的唯一标识\",\"x-dms-title\":\"设计井ID\",\"x-dms-originalType\":\"string\"},\"energyType\":{\"type\":\"string\",\"description\":\"描述本井生产的油气资源类型,如煤层气、致密气、页岩气等\",\"x-dms-title\":\"能源类型\",\"x-dms-originalType\":\"string\"},\"platformId\":{\"type\":\"string\",\"description\":\"平台ID\",\"x-dms-title\":\"平台ID\",\"x-dms-originalType\":\"string\"},\"updateDate\":{\"type\":\"string\",\"format\":\"date\",\"description\":\"记录数据在本系统最新的更新时间,需精确到时分秒,默认=创建时间\",\"x-dms-title\":\"更新日期\",\"x-dms-originalType\":\"date\"},\"wellTypeId\":{\"type\":\"string\",\"description\":\"属性规范值字段,引用属性代码WELL_TYPE下的属性值\",\"x-dms-title\":\"井型ID\",\"x-dms-originalType\":\"string\"},\"abandonDate\":{\"type\":\"string\",\"format\":\"date\",\"description\":\"报废日期\",\"x-dms-title\":\"报废日期\",\"x-dms-originalType\":\"date\"},\"abondonType\":{\"type\":\"string\",\"description\":\"报废类型\",\"x-dms-title\":\"报废类型\",\"x-dms-originalType\":\"string\"},\"actualXAxis\":{\"type\":\"number\",\"description\":\"实际X坐标,实际X坐标\",\"x-dms-mask\":\"coordinate\",\"x-dms-geom\":\"Point.x.actual\",\"x-dms-title\":\"实际X坐标,实际X坐标\",\"x-dms-originalType\":\"number\"},\"actualYAxis\":{\"type\":\"number\",\"description\":\"实际Y坐标,实际Y坐标\",\"x-dms-mask\":\"coordinate\",\"x-dms-geom\":\"Point.y.actual\",\"x-dms-title\":\"实际Y坐标,实际Y坐标\",\"x-dms-originalType\":\"number\"},\"checkUserId\":{\"type\":\"string\",\"description\":\"记录数据在本系统的审核用户\",\"x-dms-title\":\"审核用户\",\"x-dms-originalType\":\"string\"},\"createAppId\":{\"type\":\"string\",\"description\":\"填写数据来源的系统名\",\"x-dms-title\":\"创建应用\",\"x-dms-originalType\":\"string\"},\"designXAxis\":{\"type\":\"number\",\"description\":\"设计X坐标,设计X坐标\",\"x-dms-mask\":\"coordinate\",\"x-dms-geom\":\"Point.x.design\",\"x-dms-title\":\"设计X坐标,设计X坐标\",\"x-dms-originalType\":\"number\"},\"designYAxis\":{\"type\":\"number\",\"description\":\"设计Y坐标,设计Y坐标\",\"x-dms-mask\":\"coordinate\",\"x-dms-geom\":\"Point.y.design\",\"x-dms-title\":\"设计Y坐标,设计Y坐标\",\"x-dms-originalType\":\"number\"},\"projectName\":{\"type\":\"string\",\"description\":\"地质单元名称,填写地质单元的中文名称,该名称在整个油田公司内不能重名,必填\",\"x-dms-title\":\"地质单元名称\",\"x-dms-originalType\":\"string\"},\"stationName\":{\"type\":\"string\",\"description\":\"站库名称,必填\",\"x-dms-title\":\"站库名称\",\"x-dms-originalType\":\"string\"},\"wellPurpose\":{\"type\":\"string\",\"description\":\"属性规范值字段,引用属性代码WELL_PURPOSE下的属性值\",\"x-dms-title\":\"井别\",\"x-dms-originalType\":\"string\"},\"activityName\":{\"type\":\"string\",\"description\":\"项目名称,必填\",\"x-dms-title\":\"项目名称\",\"x-dms-originalType\":\"string\"},\"completionMd\":{\"type\":\"number\",\"description\":\"完钻井深\",\"x-dms-title\":\"完钻井深\",\"x-dms-originalType\":\"number\"},\"createUserId\":{\"type\":\"string\",\"description\":\"记录数据在本系统的创建用户\",\"x-dms-title\":\"创建用户\",\"x-dms-originalType\":\"string\"},\"keyWellLevel\":{\"type\":\"string\",\"description\":\"重点井级别\",\"x-dms-title\":\"重点井级别\",\"x-dms-originalType\":\"string\"},\"platformName\":{\"type\":\"string\",\"description\":\"平台名称\",\"x-dms-title\":\"平台名称\",\"x-dms-originalType\":\"string\"},\"sourceDataId\":{\"type\":\"string\",\"description\":\"存储数据来源的主键信息\",\"x-dms-title\":\"源库ID标识\",\"x-dms-originalType\":\"string\"},\"structurePos\":{\"type\":\"string\",\"description\":\"构造位置的描述\",\"x-dms-title\":\"构造位置\",\"x-dms-originalType\":\"string\"},\"updateUserId\":{\"type\":\"string\",\"description\":\"记录数据在本系统最新的更新用户,默认=创建用户\",\"x-dms-title\":\"更新用户\",\"x-dms-originalType\":\"string\"},\"geoOffsetEast\":{\"type\":\"number\",\"description\":\"井口横坐标\",\"x-dms-mask\":\"coordinate\",\"x-dms-geom\":\"Point.x.wellhead\",\"x-dms-title\":\"井口横坐标\",\"x-dms-originalType\":\"number\"},\"seismicLineNo\":{\"type\":\"string\",\"description\":\"井旁地震测线号(勘探井)\",\"x-dms-title\":\"井旁地震测线号\",\"x-dms-originalType\":\"string\"},\"wellLegalName\":{\"type\":\"string\",\"description\":\"规范的井号名称,井号名称命名规范请参考《主数据库技术标准》\",\"x-dms-title\":\"拼音井号\",\"x-dms-originalType\":\"string\"},\"wellPurposeId\":{\"type\":\"string\",\"description\":\"属性规范值字段,引用属性代码WELL_PURPOSE下的属性值\",\"x-dms-title\":\"井别ID\",\"x-dms-originalType\":\"string\"},\"completionDate\":{\"type\":\"string\",\"format\":\"date\",\"description\":\"完井日期\",\"x-dms-title\":\"完井日期\",\"x-dms-originalType\":\"date\"},\"geoDescription\":{\"type\":\"string\",\"description\":\"地理位置的描述\",\"x-dms-title\":\"地理位置\",\"x-dms-originalType\":\"string\"},\"geoOffsetNorth\":{\"type\":\"number\",\"description\":\"井口纵坐标\",\"x-dms-mask\":\"coordinate\",\"x-dms-geom\":\"Point.y.wellhead\",\"x-dms-title\":\"井口纵坐标\",\"x-dms-originalType\":\"number\"},\"wellCommonName\":{\"type\":\"string\",\"description\":\"通用井名,来源于钻井公报的汉字井名,必填\",\"x-dms-title\":\"井名\",\"x-dms-originalType\":\"string\"},\"endDrillingDate\":{\"type\":\"string\",\"format\":\"date\",\"description\":\"最后一个井筒的完钻日期\",\"x-dms-title\":\"完钻日期\",\"x-dms-originalType\":\"date\"},\"targetFormation\":{\"type\":\"string\",\"description\":\"目的层\",\"x-dms-title\":\"目的层\",\"x-dms-originalType\":\"string\"},\"completionMethod\":{\"type\":\"string\",\"description\":\"完井方法\",\"x-dms-title\":\"完井方法\",\"x-dms-originalType\":\"string\"},\"registrationDate\":{\"type\":\"string\",\"format\":\"date\",\"description\":\"井位通知单下达日期\",\"x-dms-title\":\"注册日期\",\"x-dms-originalType\":\"date\"},\"sourceCreateDate\":{\"type\":\"string\",\"format\":\"date\",\"description\":\"记录源头系统采集数据的时间\",\"x-dms-title\":\"源头数据采集时间\",\"x-dms-originalType\":\"date\"},\"coordinateSystemId\":{\"type\":\"string\",\"description\":\"坐标系统的唯一标示符\",\"x-dms-geom\":\"Point.srid.wellhead,Point.srid.design,Point.srid.actual\",\"x-dms-title\":\"坐标系统ID\",\"x-dms-originalType\":\"string\"},\"completionFormation\":{\"type\":\"string\",\"description\":\"钻井完钻层位\",\"x-dms-title\":\"钻井完钻层位\",\"x-dms-originalType\":\"string\"},\"coordinateSystemName\":{\"type\":\"string\",\"description\":\"坐标系名称\",\"x-dms-title\":\"坐标系名称\",\"x-dms-originalType\":\"string\"},\"wellbaseGeoOffsetEast\":{\"type\":\"number\",\"description\":\"井底横坐标\",\"x-dms-title\":\"井底横坐标\",\"x-dms-originalType\":\"number\"},\"wellbaseGeoOffsetNorth\":{\"type\":\"number\",\"description\":\"井底纵坐标\",\"x-dms-title\":\"井底纵坐标\",\"x-dms-originalType\":\"number\"}},\"description\":\"井\",\"x-dms-groupView\":[{\"name\":\"groupByWellCommonName\",\"group\":[\"dataRegion\",\"wellCommonName\",\"wellId\"]},{\"name\":\"groupByPlatformName\",\"group\":[\"dataRegion\",\"platformName\"]}],\"x-dms-identityId\":[\"dsid\"],\"x-dms-naturalKey\":[\"dataRegion\",\"wellCommonName\"],\"x-dms-defaultShow\":[\"wellCommonName\"]}}}}",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "topic",
|
||
"vt": "str"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"x": 70,
|
||
"y": 160,
|
||
"wires": [
|
||
[
|
||
"211c041ab58c88f2"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "41818dbfe05be246",
|
||
"type": "function",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "直接处理oas",
|
||
"func": "'use strict';\n\ntry {\n const rawOas = extractOasInput(msg);\n const oasDef = typeof rawOas === 'string' ? JSON.parse(rawOas) : rawOas;\n\n validateOas(oasDef);\n\n const incomingCrudConfig = msg.req && msg.req.body && msg.req.body.crudConfig;\n if (isPlainObject(incomingCrudConfig)) {\n msg.crudConfig = mergeDeep({}, msg.crudConfig || {}, incomingCrudConfig);\n }\n\n if ((!msg.crudConfig || !msg.crudConfig.baseUrl) && Array.isArray(oasDef.servers) && oasDef.servers.length) {\n const validServer = oasDef.servers.find(server => server && typeof server.url === 'string' && server.url.trim());\n if (validServer) {\n msg.crudConfig = msg.crudConfig || {};\n msg.crudConfig.baseUrl = trimTrailingSlash(validServer.url.trim());\n }\n }\n\n msg.oas_def = oasDef;\n msg.payload = oasDef;\n delete msg.error;\n return msg;\n} catch (err) {\n node.error(`读取 OAS 定义失败: ${err.message}`, msg);\n if (node.warn) {\n node.warn(`Debug - msg keys: ${Object.keys(msg).join(',')}`);\n if (msg.req) node.warn(`Debug - msg.req.body type: ${typeof msg.req.body}`);\n node.warn(`Debug - msg.payload type: ${typeof msg.payload}`);\n }\n msg.error = err.message;\n return msg;\n}\n\nfunction extractOasInput(message) {\n // Helper to check if an object looks like OAS\n const isOas = (obj) => obj && (obj.openapi || obj.swagger);\n\n // 1. Check msg.req.body\n let reqBody = message.req && message.req.body;\n // If body is string, try to parse it (common in some HTTP inputs)\n if (typeof reqBody === 'string') {\n try { reqBody = JSON.parse(reqBody); } catch(e) {}\n }\n \n if (reqBody && typeof reqBody === 'object') {\n if (reqBody.schema !== undefined) return reqBody.schema;\n if (isOas(reqBody)) return reqBody;\n }\n\n // 2. Check msg.payload\n let payload = message.payload;\n // If payload is string, try to parse it\n if (typeof payload === 'string') {\n try { payload = JSON.parse(payload); } catch(e) {}\n }\n\n if (payload && typeof payload === 'object') {\n if (payload.schema !== undefined) return payload.schema;\n if (isOas(payload)) return payload;\n }\n\n // 3. Check msg.schema (direct injection fallback)\n if (message.schema !== undefined) return message.schema;\n\n // 4. Fallback: if original payload was a string and we haven't found anything yet, return it \n // (The main logic will try to parse it as OAS)\n if (typeof message.payload === 'string') return message.payload;\n\n throw new Error('未找到 OAS 定义。已检查: msg.req.body.schema, msg.req.body, msg.payload.schema, msg.payload, msg.schema');\n}\n\nfunction validateOas(oas) {\n if (!oas || typeof oas !== 'object') {\n throw new Error('OAS 文档必须是对象或 JSON 字符串');\n }\n\n if (!oas.openapi && !oas.swagger) {\n throw new Error('OpenAPI 文档缺少 openapi/swagger 字段');\n }\n\n if (!oas.paths || typeof oas.paths !== 'object' || Object.keys(oas.paths).length === 0) {\n throw new Error('OpenAPI 文档缺少 paths 定义');\n }\n}\n\nfunction mergeDeep(target, ...sources) {\n for (const source of sources) {\n if (!isPlainObject(source)) {\n continue;\n }\n\n for (const key of Object.keys(source)) {\n const value = source[key];\n if (value === undefined) {\n continue;\n }\n\n if (isPlainObject(value)) {\n const base = isPlainObject(target[key]) ? target[key] : {};\n target[key] = mergeDeep({}, base, value);\n } else {\n target[key] = clone(value);\n }\n }\n }\n\n return target;\n}\n\nfunction isPlainObject(candidate) {\n return Object.prototype.toString.call(candidate) === '[object Object]';\n}\n\nfunction clone(value) {\n return value == null ? value : JSON.parse(JSON.stringify(value));\n}\n\nfunction trimTrailingSlash(url) {\n return !url ? '' : url.replace(/\\/+$/, '');\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 270,
|
||
"y": 140,
|
||
"wires": [
|
||
[
|
||
"9c84414e55654e6a",
|
||
"b8560bc29323d193"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "211c041ab58c88f2",
|
||
"type": "switch",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "",
|
||
"property": "req.body",
|
||
"propertyType": "msg",
|
||
"rules": [
|
||
{
|
||
"t": "hask",
|
||
"v": "schema",
|
||
"vt": "str"
|
||
},
|
||
{
|
||
"t": "else"
|
||
}
|
||
],
|
||
"checkall": "true",
|
||
"repair": false,
|
||
"outputs": 2,
|
||
"x": 170,
|
||
"y": 100,
|
||
"wires": [
|
||
[
|
||
"41818dbfe05be246"
|
||
],
|
||
[
|
||
"f414ed448fcf544b"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "b8560bc29323d193",
|
||
"type": "debug",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "直接输出结果",
|
||
"active": true,
|
||
"tosidebar": true,
|
||
"console": false,
|
||
"tostatus": false,
|
||
"complete": "msg",
|
||
"targetType": "jsonata",
|
||
"statusVal": "",
|
||
"statusType": "auto",
|
||
"x": 280,
|
||
"y": 300,
|
||
"wires": []
|
||
},
|
||
{
|
||
"id": "1d3dbb308f165b24",
|
||
"type": "file",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "",
|
||
"filename": "D:\\workspace\\zzlh\\node-red\\compiliance-js\\directoas.txt",
|
||
"filenameType": "str",
|
||
"appendNewline": true,
|
||
"createDir": false,
|
||
"overwriteFile": "false",
|
||
"encoding": "none",
|
||
"x": 750,
|
||
"y": 260,
|
||
"wires": [
|
||
[]
|
||
]
|
||
},
|
||
{
|
||
"id": "83f73f7e3191d853",
|
||
"type": "function",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "根据 OpenAPI Schema 生成默认请求参数",
|
||
"func": "'use strict';\n\n/**\n * Node-RED Function 节点脚本:根据 OpenAPI Schema 生成默认请求参数。\n * 作为 LLM 生成的兜底或并行方案,不调用 LLM,直接根据 Schema 定义生成示例值。\n * \n * 输入:\n * - msg.oas_def / msg.payload / msg.swagger: OpenAPI 3.x 文档对象或 JSON 字符串\n * - msg.operationId: 目标 operationId (通常由上游 \"设置create上下文\" 节点设置)\n * \n * 输出:\n * - msg.mock: { \n * operationId, method, path, mediaType, \n * pathParams, query, headers, cookies, body \n * }\n * - msg.payload: 同 msg.mock\n * - msg.mockSource: 'rule-based'\n */\n\nlet openApi;\ntry {\n openApi = extractOpenApiDocument(msg);\n} catch (error) {\n node.error(`Swagger 自动填充初始化失败:${error.message}`, msg);\n msg.error = error.message;\n return msg;\n}\n\nconst operationCtx = resolveOperationContext(msg, openApi);\nif (!operationCtx.operation) {\n const message = `未找到匹配的接口定义(operationId=${operationCtx.requestedOperationId || '未指定'}, path=${operationCtx.requestedPath || '未指定'}, method=${operationCtx.requestedMethod || '未指定'})`;\n node.error(message, msg);\n msg.error = message;\n return msg;\n}\n\nconst parameterSamples = buildParameterSamples(operationCtx, openApi);\nconst bodySample = buildRequestBodySample(operationCtx.operation, openApi);\n\nmsg.mock = Object.assign({}, msg.mock, {\n operationId: operationCtx.operation.operationId || operationCtx.requestedOperationId || '',\n method: (operationCtx.method || '').toUpperCase(),\n path: operationCtx.path || '',\n mediaType: bodySample.mediaType,\n pathParams: parameterSamples.path,\n query: parameterSamples.query,\n headers: parameterSamples.header,\n cookies: parameterSamples.cookie,\n body: bodySample.payload,\n});\n\nmsg.payload = msg.mock;\nmsg.mockCandidates = operationCtx.candidates;\nif (operationCtx.autoSelected) {\n msg.mockAutoSelected = true;\n}\n\n// 标记来源为规则生成,以便后续区分\nmsg.mockSource = 'rule-based';\n\nreturn msg;\n\n// --------------------------------------------------------------------------------\n// Helper Functions\n// --------------------------------------------------------------------------------\n\nfunction extractOpenApiDocument(message) {\n const candidate =\n message && message.oas_def ? message.oas_def :\n message && message.swagger ? message.swagger :\n message && message.payload ? message.payload :\n null;\n\n if (!candidate) {\n throw new Error('未提供 OpenAPI 文档(需要 msg.oas_def / msg.swagger / msg.payload)');\n }\n\n if (typeof candidate === 'string') {\n try {\n return JSON.parse(candidate);\n } catch (error) {\n throw new Error(`OpenAPI JSON 解析失败:${error.message}`);\n }\n }\n\n if (typeof candidate !== 'object') {\n throw new Error('OpenAPI 文档必须是对象或 JSON 字符串');\n }\n\n if (!candidate.paths || typeof candidate.paths !== 'object') {\n throw new Error('OpenAPI 文档缺少 paths 字段');\n }\n\n return candidate;\n}\n\nfunction resolveOperationContext(message, apiDoc) {\n const requestedOperationId =\n (message && message.operationId) ||\n (message && message.req && message.req.body && message.req.body.operationId) ||\n null;\n\n const requestedPath =\n (message && message.path) ||\n (message && message.req && message.req.body && message.req.body.path) ||\n null;\n\n const requestedMethodRaw =\n (message && message.method) ||\n (message && message.req && message.req.body && message.req.body.method) ||\n null;\n const requestedMethod = requestedMethodRaw ? String(requestedMethodRaw).toLowerCase() : null;\n\n const operations = enumerateOperations(apiDoc);\n\n let matched = null;\n if (requestedOperationId) {\n matched = operations.find(op => op.operation.operationId === requestedOperationId);\n }\n\n if (!matched && requestedPath && requestedMethod) {\n matched = operations.find(op => op.path === requestedPath && op.method === requestedMethod);\n }\n\n let autoSelected = false;\n if (!matched && operations.length > 0) {\n matched = operations[0];\n autoSelected = true;\n }\n\n return {\n operation: matched ? matched.operation : undefined,\n method: matched ? matched.method : undefined,\n pathItem: matched ? matched.pathItem : undefined,\n path: matched ? matched.path : undefined,\n autoSelected,\n candidates: operations.map(op => ({\n operationId: op.operation && op.operation.operationId ? op.operation.operationId : '',\n method: op.method ? op.method.toUpperCase() : '',\n path: op.path,\n summary: op.operation && op.operation.summary ? op.operation.summary : '',\n })),\n requestedOperationId,\n requestedPath,\n requestedMethod,\n };\n}\n\nfunction enumerateOperations(apiDoc) {\n const results = [];\n const validMethods = ['get', 'put', 'post', 'delete', 'patch', 'options', 'head', 'trace'];\n for (const path of Object.keys(apiDoc.paths || {})) {\n const pathItem = apiDoc.paths[path];\n if (!pathItem || typeof pathItem !== 'object') {\n continue;\n }\n for (const method of validMethods) {\n if (pathItem[method] && typeof pathItem[method] === 'object') {\n results.push({\n path,\n method,\n operation: pathItem[method],\n pathItem,\n });\n }\n }\n }\n return results;\n}\n\nfunction buildParameterSamples(operationContext, apiDoc) {\n const aggregated = {\n path: {},\n query: {},\n header: {},\n cookie: {},\n };\n\n const seen = new Set();\n const paramSources = [];\n if (Array.isArray(operationContext.pathItem && operationContext.pathItem.parameters)) {\n paramSources.push(operationContext.pathItem.parameters);\n }\n if (Array.isArray(operationContext.operation.parameters)) {\n paramSources.push(operationContext.operation.parameters);\n }\n\n for (const source of paramSources) {\n for (const param of source) {\n if (!param || typeof param !== 'object') {\n continue;\n }\n const name = param.name || '';\n const location = (param.in || 'query').toLowerCase();\n const key = `${location}:${name}`;\n if (!name || seen.has(key)) {\n continue;\n }\n seen.add(key);\n\n const resolvedParam = resolveMaybeRef(param, apiDoc);\n const targetBucket = aggregated[location] || aggregated.query;\n targetBucket[name] = generateParameterSample(resolvedParam, apiDoc, location);\n }\n }\n\n return aggregated;\n}\n\nfunction generateParameterSample(param, apiDoc, location) {\n if (!param || typeof param !== 'object') {\n return 'sample';\n }\n\n const example = pickExample(param);\n if (example !== undefined) {\n return example;\n }\n\n const schema = resolveMaybeRef(param.schema, apiDoc);\n const sample = generateSampleValue(schema, apiDoc, 0, new Set(), param.name);\n if (sample !== undefined) {\n return sample;\n }\n\n // fallback\n switch (location) {\n case 'path':\n return 'sample-id';\n case 'header':\n return 'sample-header';\n case 'cookie':\n return 'sample-cookie';\n default:\n return 'sample';\n }\n}\n\nfunction buildRequestBodySample(operation, apiDoc) {\n const resolvedBody = resolveMaybeRef(operation.requestBody, apiDoc);\n if (!resolvedBody || typeof resolvedBody !== 'object') {\n return { payload: undefined, mediaType: undefined };\n }\n\n const content = resolvedBody.content;\n if (!content || typeof content !== 'object') {\n return { payload: undefined, mediaType: undefined };\n }\n\n const preferredMediaTypes = [\n 'application/json',\n 'application/*+json',\n 'application/x-www-form-urlencoded',\n 'multipart/form-data',\n 'text/plain',\n ];\n\n let chosenMediaType = null;\n for (const mediaType of preferredMediaTypes) {\n if (content[mediaType]) {\n chosenMediaType = mediaType;\n break;\n }\n }\n if (!chosenMediaType) {\n const mediaKeys = Object.keys(content);\n chosenMediaType = mediaKeys.length > 0 ? mediaKeys[0] : null;\n }\n if (!chosenMediaType) {\n return { payload: undefined, mediaType: undefined };\n }\n\n const mediaObject = content[chosenMediaType];\n if (!mediaObject || typeof mediaObject !== 'object') {\n return { payload: undefined, mediaType: chosenMediaType };\n }\n\n if (mediaObject.example !== undefined) {\n return { payload: clone(mediaObject.example), mediaType: chosenMediaType };\n }\n if (mediaObject.examples && typeof mediaObject.examples === 'object') {\n const firstExample = Object.values(mediaObject.examples)[0];\n if (firstExample && typeof firstExample === 'object' && firstExample.value !== undefined) {\n return { payload: clone(firstExample.value), mediaType: chosenMediaType };\n }\n }\n\n const schema = resolveMaybeRef(mediaObject.schema, apiDoc);\n const payload = generateSampleValue(schema, apiDoc);\n return { payload, mediaType: chosenMediaType };\n}\n\nfunction pickExample(node) {\n if (!node || typeof node !== 'object') {\n return undefined;\n }\n if (node.example !== undefined) {\n return clone(node.example);\n }\n if (node.examples && typeof node.examples === 'object') {\n const first = Object.values(node.examples)[0];\n if (first && typeof first === 'object' && first.value !== undefined) {\n return clone(first.value);\n }\n }\n if (node.default !== undefined) {\n return clone(node.default);\n }\n return undefined;\n}\n\nfunction generateSampleValue(schema, apiDoc, depth = 0, seenRefs = new Set(), fieldName = null) {\n if (!schema || typeof schema !== 'object') {\n return undefined;\n }\n\n // Special handling for identity field\n if (fieldName) {\n const identityField = (msg.crudFlow && msg.crudFlow.identityField) || 'dsid';\n if (fieldName === identityField) {\n return `zzlh_testkey${Date.now()}`;\n }\n }\n\n if (depth > 8) {\n return undefined;\n }\n\n if (schema.example !== undefined) {\n return clone(schema.example);\n }\n if (schema.default !== undefined) {\n return clone(schema.default);\n }\n if (schema.const !== undefined) {\n return clone(schema.const);\n }\n if (Array.isArray(schema.enum) && schema.enum.length > 0) {\n return clone(schema.enum[0]);\n }\n\n if (schema.$ref) {\n if (seenRefs.has(schema.$ref)) {\n return undefined;\n }\n seenRefs.add(schema.$ref);\n const resolved = resolveMaybeRef(schema, apiDoc, seenRefs);\n return generateSampleValue(resolved, apiDoc, depth + 1, seenRefs, fieldName);\n }\n\n if (Array.isArray(schema.oneOf) && schema.oneOf.length > 0) {\n return generateSampleValue(schema.oneOf[0], apiDoc, depth + 1, seenRefs, fieldName);\n }\n if (Array.isArray(schema.anyOf) && schema.anyOf.length > 0) {\n return generateSampleValue(schema.anyOf[0], apiDoc, depth + 1, seenRefs, fieldName);\n }\n if (Array.isArray(schema.allOf) && schema.allOf.length > 0) {\n const merged = schema.allOf\n .map(part => resolveMaybeRef(part, apiDoc, seenRefs))\n .filter(part => part && typeof part === 'object')\n .reduce((acc, part) => Object.assign(acc, part), {});\n return generateSampleValue(merged, apiDoc, depth + 1, seenRefs, fieldName);\n }\n\n const type = inferSchemaType(schema);\n switch (type) {\n case 'object': {\n const result = {};\n if (schema.properties && typeof schema.properties === 'object') {\n for (const [key, value] of Object.entries(schema.properties)) {\n const resolvedChild = resolveMaybeRef(value, apiDoc, seenRefs);\n const childSample = generateSampleValue(resolvedChild, apiDoc, depth + 1, seenRefs, key);\n if (childSample !== undefined) {\n result[key] = childSample;\n }\n }\n }\n const required = Array.isArray(schema.required) ? schema.required : [];\n for (const propertyName of required) {\n if (!Object.prototype.hasOwnProperty.call(result, propertyName)) {\n result[propertyName] = pickFallbackByFormat({ type: 'string' });\n }\n }\n return Object.keys(result).length > 0 ? result : {};\n }\n case 'array': {\n const itemSchema = resolveMaybeRef(schema.items, apiDoc, seenRefs) || { type: 'string' };\n const itemSample = generateSampleValue(itemSchema, apiDoc, depth + 1, seenRefs);\n return itemSample !== undefined ? [itemSample] : [];\n }\n case 'integer':\n return Number.isInteger(schema.minimum) ? schema.minimum : 1;\n case 'number':\n if (schema.minimum !== undefined) {\n return typeof schema.minimum === 'number' ? schema.minimum : 0;\n }\n return 1;\n case 'boolean':\n return true;\n case 'string':\n default:\n return pickFallbackByFormat(schema);\n }\n}\n\nfunction inferSchemaType(schema) {\n if (!schema || typeof schema !== 'object') {\n return 'string';\n }\n if (schema.type) {\n return schema.type;\n }\n if (schema.properties) {\n return 'object';\n }\n if (schema.items) {\n return 'array';\n }\n return 'string';\n}\n\nfunction pickFallbackByFormat(schema) {\n const format = schema && schema.format ? schema.format.toLowerCase() : null;\n switch (format) {\n case 'date':\n return '2025-01-01';\n case 'date-time':\n return '2025-01-01T00:00:00Z';\n case 'email':\n return 'user@example.com';\n case 'uuid':\n return '00000000-0000-4000-8000-000000000000';\n case 'uri':\n case 'url':\n return 'https://example.com/resource';\n case 'byte':\n return Buffer.from('sample').toString('base64');\n case 'binary':\n return '<binary>';\n default:\n break;\n }\n\n const pattern = schema && schema.pattern;\n if (pattern && /[0-9]{4}-[0-9]{2}-[0-9]{2}/.test(pattern)) {\n return '2025-01-01';\n }\n\n return 'sample';\n}\n\nfunction resolveMaybeRef(node, apiDoc, seenRefs = new Set()) {\n if (!node || typeof node !== 'object') {\n return node;\n }\n if (!node.$ref) {\n return node;\n }\n\n const ref = node.$ref;\n if (seenRefs.has(ref)) {\n return {};\n }\n seenRefs.add(ref);\n\n const resolved = resolveRef(ref, apiDoc);\n if (!resolved || typeof resolved !== 'object') {\n return node;\n }\n\n const remainder = Object.assign({}, node);\n delete remainder.$ref;\n\n return Object.assign({}, clone(resolved), remainder);\n}\n\nfunction resolveRef(ref, apiDoc) {\n if (typeof ref !== 'string' || !ref.startsWith('#/')) {\n return null;\n }\n\n const tokens = ref.slice(2).split('/').map(unescapeRefToken);\n let current = apiDoc;\n for (const token of tokens) {\n if (current && typeof current === 'object' && Object.prototype.hasOwnProperty.call(current, token)) {\n current = current[token];\n } else {\n return null;\n }\n }\n return current;\n}\n\nfunction unescapeRefToken(token) {\n return token.replace(/~1/g, '/').replace(/~0/g, '~');\n}\n\nfunction clone(value) {\n return value == null ? value : JSON.parse(JSON.stringify(value));\n}\n",
|
||
"outputs": 1,
|
||
"timeout": 0,
|
||
"noerr": 0,
|
||
"initialize": "",
|
||
"finalize": "",
|
||
"libs": [],
|
||
"x": 540,
|
||
"y": 820,
|
||
"wires": [
|
||
[
|
||
"31d5f84e4c6f69f6"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "4b89069c3dcb949e",
|
||
"type": "switch",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "",
|
||
"property": "req.body.useLLM",
|
||
"propertyType": "msg",
|
||
"rules": [
|
||
{
|
||
"t": "true"
|
||
},
|
||
{
|
||
"t": "else"
|
||
}
|
||
],
|
||
"checkall": "true",
|
||
"repair": false,
|
||
"outputs": 2,
|
||
"x": 230,
|
||
"y": 720,
|
||
"wires": [
|
||
[
|
||
"60ce224bd2ed8e69"
|
||
],
|
||
[
|
||
"83f73f7e3191d853"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "b841424fab1ca095",
|
||
"type": "inject",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "schema_MOCK",
|
||
"props": [
|
||
{
|
||
"p": "req.body.schema",
|
||
"v": "{ \"openapi\": \"3.0.1\", \"info\": { \"title\": \"井 API\", \"version\": \"1.0.0\", \"description\": \"https://schema.ideas.cnpc/json/core-data/cd_well.1.0.0.json\", \"x-dms-sourceId\": \"https://schema.ideas.cnpc/json/core-data/cd_well.1.0.0.json\" }, \"paths\": { \"/cd_well/1.0.0\": { \"post\": { \"operationId\": \"listResources\", \"summary\": \"List Resource resources\", \"requestBody\": { \"required\": false, \"content\": { \"application/json\": { \"schema\": { \"type\": \"object\", \"properties\": { \"version\": { \"type\": \"string\", \"default\": \"1.0.0\" }, \"data\": { \"type\": \"object\", \"properties\": { \"pageNo\": { \"type\": \"integer\", \"default\": 1 }, \"pageSize\": { \"type\": \"integer\", \"default\": 20 }, \"isSearchCount\": { \"type\": \"boolean\", \"default\": true }, \"filters\": { \"type\": \"array\", \"items\": { \"type\": \"object\" } } } } } } } } }, \"responses\": { \"200\": { \"description\": \"Successful response\", \"content\": { \"application/json\": { \"schema\": { \"type\": \"object\", \"properties\": { \"code\": { \"type\": \"integer\" }, \"message\": { \"type\": \"string\" }, \"data\": { \"type\": \"object\", \"properties\": { \"list\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/components/schemas/Resource\" } }, \"total\": { \"type\": \"integer\" } } } } } } } } } } }, \"/cd_well\": { \"post\": { \"operationId\": \"createResource\", \"summary\": \"Create a Resource\", \"requestBody\": { \"required\": true, \"content\": { \"application/json\": { \"schema\": { \"type\": \"object\", \"required\": [ \"version\", \"act\", \"data\" ], \"properties\": { \"version\": { \"type\": \"string\", \"default\": \"1.0.0\" }, \"act\": { \"type\": \"integer\", \"default\": -1 }, \"data\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/components/schemas/Resource\" } } } } } } }, \"responses\": { \"201\": { \"description\": \"Created\", \"content\": { \"application/json\": { \"schema\": { \"$ref\": \"#/components/schemas/Resource\" } } } } } }, \"put\": { \"operationId\": \"updateResource\", \"summary\": \"Update a Resource\", \"requestBody\": { \"required\": true, \"content\": { \"application/json\": { \"schema\": { \"type\": \"object\", \"required\": [ \"version\", \"act\", \"data\" ], \"properties\": { \"version\": { \"type\": \"string\", \"default\": \"1.0.0\" }, \"act\": { \"type\": \"integer\", \"default\": -1 }, \"data\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/components/schemas/Resource\" } } } } } } }, \"responses\": { \"200\": { \"description\": \"Successful update\", \"content\": { \"application/json\": { \"schema\": { \"$ref\": \"#/components/schemas/Resource\" } } } } } }, \"delete\": { \"operationId\": \"deleteResource\", \"summary\": \"Delete Resource resources\", \"requestBody\": { \"required\": true, \"content\": { \"application/json\": { \"schema\": { \"type\": \"object\", \"required\": [ \"version\", \"data\" ], \"properties\": { \"version\": { \"type\": \"string\", \"default\": \"1.0.0\" }, \"data\": { \"type\": \"array\", \"items\": { \"type\": \"string\", \"description\": \"Value of dsid\" } } } } } } }, \"responses\": { \"200\": { \"description\": \"Deleted\" } } } }, \"/cd_well/1.0.0/{dsid}\": { \"parameters\": [ { \"name\": \"dsid\", \"in\": \"path\", \"required\": true, \"schema\": { \"type\": \"string\", \"description\": \"主键ID\", \"x-dms-title\": \"DSID\", \"x-dms-originalType\": \"string\" } } ], \"get\": { \"operationId\": \"getResource\", \"summary\": \"Get a single Resource\", \"responses\": { \"200\": { \"description\": \"Successful response\", \"content\": { \"application/json\": { \"schema\": { \"$ref\": \"#/components/schemas/Resource\" } } } }, \"404\": { \"description\": \"Resource not found\" } } } } }, \"components\": { \"schemas\": { \"Resource\": { \"type\": \"object\", \"required\": [ \"bsflag\", \"dataRegion\", \"createUserId\", \"orgId\", \"wellPurpose\", \"createDate\", \"wellLegalName\", \"dsid\", \"wellType\", \"updateUserId\", \"updateDate\", \"wellId\", \"wellCommonName\", \"projectId\" ], \"properties\": { \"kb\": { \"type\": \"number\", \"description\": \"海拔(高程),补心海拔=地面海拔+补心高\", \"x-dms-title\": \"补心海拔\", \"x-dms-originalType\": \"number\" }, \"egl\": { \"type\": \"number\", \"description\": \"地面海拔\", \"x-dms-title\": \"地面海拔\", \"x-dms-originalType\": \"number\" }, \"kbd\": { \"type\": \"number\", \"description\": \"补心高度\", \"x-dms-title\": \"补心高度\", \"x-dms-originalType\": \"number\" }, \"dsid\": { \"type\": \"string\", \"description\": \"主键ID\", \"x-dms-title\": \"DSID\", \"x-dms-originalType\": \"string\" }, \"orgId\": { \"type\": \"string\", \"description\": \"单位唯一标识符,关联CD_ORGANIZATION的主键\", \"x-dms-title\": \"机构ID\", \"x-dms-originalType\": \"string\" }, \"bsflag\": { \"type\": \"number\", \"description\": \"填写数据逻辑删除标识,1=在用,-5=废弃\", \"x-dms-title\": \"删除标识\", \"x-dms-originalType\": \"number\" }, \"canton\": { \"type\": \"string\", \"description\": \"填写行政区代码对应的行政区名称\", \"x-dms-title\": \"行政区名称\", \"x-dms-originalType\": \"string\" }, \"siteId\": { \"type\": \"string\", \"description\": \"物探工区ID\", \"x-dms-title\": \"物探工区ID\", \"x-dms-originalType\": \"string\" }, \"wellId\": { \"type\": \"string\", \"description\": \"井标识符,非限定唯一。由EPDM系统自动产生维护,无需人工干预,是用于唯一标识EPDM系统的每一口井的内部机器码\", \"x-dms-title\": \"井ID\", \"x-dms-originalType\": \"string\" }, \"orgName\": { \"type\": \"string\", \"description\": \"单位名称,必填\", \"x-dms-title\": \"机构名称\", \"x-dms-originalType\": \"string\" }, \"remarks\": { \"type\": \"string\", \"description\": \"备注\", \"x-dms-title\": \"备注\", \"x-dms-originalType\": \"string\" }, \"prodDate\": { \"type\": \"string\", \"format\": \"date\", \"description\": \"投产日期\", \"x-dms-title\": \"投产日期\", \"x-dms-originalType\": \"date\" }, \"siteName\": { \"type\": \"string\", \"description\": \"物探工区名称\", \"x-dms-title\": \"物探工区名称\", \"x-dms-originalType\": \"string\" }, \"spudDate\": { \"type\": \"string\", \"format\": \"date\", \"description\": \"主井筒的开钻日期\", \"x-dms-title\": \"开钻日期\", \"x-dms-originalType\": \"date\" }, \"wellDesc\": { \"type\": \"string\", \"description\": \"填写这口井的曾用名\", \"x-dms-title\": \"曾用名\", \"x-dms-originalType\": \"string\" }, \"wellType\": { \"type\": \"string\", \"description\": \"属性规范值字段,引用属性代码WELL_TYPE下的属性值\", \"x-dms-title\": \"井型\", \"x-dms-originalType\": \"string\" }, \"checkDate\": { \"type\": \"string\", \"format\": \"date\", \"description\": \"记录数据在本系统的审核时间,需精确到时分秒\", \"x-dms-title\": \"审核日期\", \"x-dms-originalType\": \"date\" }, \"dataGroup\": { \"type\": \"string\", \"description\": \"数据分组\", \"x-dms-title\": \"数据分组\", \"x-dms-originalType\": \"string\" }, \"projectId\": { \"type\": \"string\", \"description\": \"地质单元唯一标识符,根据井别不同,选择关联构造单元(探井)还是油气田单元(开发井),关联CD_GEO_UNIT表的主键\", \"x-dms-title\": \"地质单元ID\", \"x-dms-originalType\": \"string\" }, \"stationId\": { \"type\": \"string\", \"description\": \"站库ID\", \"x-dms-title\": \"站库ID\", \"x-dms-originalType\": \"string\" }, \"wellState\": { \"type\": \"string\", \"description\": \"井状态\", \"x-dms-title\": \"井状态\", \"x-dms-originalType\": \"string\" }, \"activityId\": { \"type\": \"string\", \"description\": \"项目唯一标示符,关联CD_ACTIVITY表的主键\", \"x-dms-title\": \"项目ID\", \"x-dms-originalType\": \"string\" }, \"cantonCode\": { \"type\": \"string\", \"description\": \"属性规范值字段,引用属性代码CANTON下的属性值\", \"x-dms-title\": \"行政区代码\", \"x-dms-originalType\": \"string\" }, \"createDate\": { \"type\": \"string\", \"format\": \"date\", \"description\": \"记录数据在本系统的创建时间,需精确到时分秒\", \"x-dms-title\": \"创建日期\", \"x-dms-originalType\": \"date\" }, \"dataRegion\": { \"type\": \"string\", \"description\": \"油田标识\", \"x-dms-title\": \"油田标识\", \"x-dms-originalType\": \"string\" }, \"dataSource\": { \"type\": \"string\", \"description\": \"填写数据来源的表CODE\", \"x-dms-title\": \"数据来源\", \"x-dms-originalType\": \"string\" }, \"desgWellId\": { \"type\": \"string\", \"description\": \"设计井的唯一标识\", \"x-dms-title\": \"设计井ID\", \"x-dms-originalType\": \"string\" }, \"energyType\": { \"type\": \"string\", \"description\": \"描述本井生产的油气资源类型,如煤层气、致密气、页岩气等\", \"x-dms-title\": \"能源类型\", \"x-dms-originalType\": \"string\" }, \"platformId\": { \"type\": \"string\", \"description\": \"平台ID\", \"x-dms-title\": \"平台ID\", \"x-dms-originalType\": \"string\" }, \"updateDate\": { \"type\": \"string\", \"format\": \"date\", \"description\": \"记录数据在本系统最新的更新时间,需精确到时分秒,默认=创建时间\", \"x-dms-title\": \"更新日期\", \"x-dms-originalType\": \"date\" }, \"wellTypeId\": { \"type\": \"string\", \"description\": \"属性规范值字段,引用属性代码WELL_TYPE下的属性值\", \"x-dms-title\": \"井型ID\", \"x-dms-originalType\": \"string\" }, \"abandonDate\": { \"type\": \"string\", \"format\": \"date\", \"description\": \"报废日期\", \"x-dms-title\": \"报废日期\", \"x-dms-originalType\": \"date\" }, \"abondonType\": { \"type\": \"string\", \"description\": \"报废类型\", \"x-dms-title\": \"报废类型\", \"x-dms-originalType\": \"string\" }, \"actualXAxis\": { \"type\": \"number\", \"description\": \"实际X坐标,实际X坐标\", \"x-dms-mask\": \"coordinate\", \"x-dms-geom\": \"Point.x.actual\", \"x-dms-title\": \"实际X坐标,实际X坐标\", \"x-dms-originalType\": \"number\" }, \"actualYAxis\": { \"type\": \"number\", \"description\": \"实际Y坐标,实际Y坐标\", \"x-dms-mask\": \"coordinate\", \"x-dms-geom\": \"Point.y.actual\", \"x-dms-title\": \"实际Y坐标,实际Y坐标\", \"x-dms-originalType\": \"number\" }, \"checkUserId\": { \"type\": \"string\", \"description\": \"记录数据在本系统的审核用户\", \"x-dms-title\": \"审核用户\", \"x-dms-originalType\": \"string\" }, \"createAppId\": { \"type\": \"string\", \"description\": \"填写数据来源的系统名\", \"x-dms-title\": \"创建应用\", \"x-dms-originalType\": \"string\" }, \"designXAxis\": { \"type\": \"number\", \"description\": \"设计X坐标,设计X坐标\", \"x-dms-mask\": \"coordinate\", \"x-dms-geom\": \"Point.x.design\", \"x-dms-title\": \"设计X坐标,设计X坐标\", \"x-dms-originalType\": \"number\" }, \"designYAxis\": { \"type\": \"number\", \"description\": \"设计Y坐标,设计Y坐标\", \"x-dms-mask\": \"coordinate\", \"x-dms-geom\": \"Point.y.design\", \"x-dms-title\": \"设计Y坐标,设计Y坐标\", \"x-dms-originalType\": \"number\" }, \"projectName\": { \"type\": \"string\", \"description\": \"地质单元名称,填写地质单元的中文名称,该名称在整个油田公司内不能重名,必填\", \"x-dms-title\": \"地质单元名称\", \"x-dms-originalType\": \"string\" }, \"stationName\": { \"type\": \"string\", \"description\": \"站库名称,必填\", \"x-dms-title\": \"站库名称\", \"x-dms-originalType\": \"string\" }, \"wellPurpose\": { \"type\": \"string\", \"description\": \"属性规范值字段,引用属性代码WELL_PURPOSE下的属性值\", \"x-dms-title\": \"井别\", \"x-dms-originalType\": \"string\" }, \"activityName\": { \"type\": \"string\", \"description\": \"项目名称,必填\", \"x-dms-title\": \"项目名称\", \"x-dms-originalType\": \"string\" }, \"completionMd\": { \"type\": \"number\", \"description\": \"完钻井深\", \"x-dms-title\": \"完钻井深\", \"x-dms-originalType\": \"number\" }, \"createUserId\": { \"type\": \"string\", \"description\": \"记录数据在本系统的创建用户\", \"x-dms-title\": \"创建用户\", \"x-dms-originalType\": \"string\" }, \"keyWellLevel\": { \"type\": \"string\", \"description\": \"重点井级别\", \"x-dms-title\": \"重点井级别\", \"x-dms-originalType\": \"string\" }, \"platformName\": { \"type\": \"string\", \"description\": \"平台名称\", \"x-dms-title\": \"平台名称\", \"x-dms-originalType\": \"string\" }, \"sourceDataId\": { \"type\": \"string\", \"description\": \"存储数据来源的主键信息\", \"x-dms-title\": \"源库ID标识\", \"x-dms-originalType\": \"string\" }, \"structurePos\": { \"type\": \"string\", \"description\": \"构造位置的描述\", \"x-dms-title\": \"构造位置\", \"x-dms-originalType\": \"string\" }, \"updateUserId\": { \"type\": \"string\", \"description\": \"记录数据在本系统最新的更新用户,默认=创建用户\", \"x-dms-title\": \"更新用户\", \"x-dms-originalType\": \"string\" }, \"geoOffsetEast\": { \"type\": \"number\", \"description\": \"井口横坐标\", \"x-dms-mask\": \"coordinate\", \"x-dms-geom\": \"Point.x.wellhead\", \"x-dms-title\": \"井口横坐标\", \"x-dms-originalType\": \"number\" }, \"seismicLineNo\": { \"type\": \"string\", \"description\": \"井旁地震测线号(勘探井)\", \"x-dms-title\": \"井旁地震测线号\", \"x-dms-originalType\": \"string\" }, \"wellLegalName\": { \"type\": \"string\", \"description\": \"规范的井号名称,井号名称命名规范请参考《主数据库技术标准》\", \"x-dms-title\": \"拼音井号\", \"x-dms-originalType\": \"string\" }, \"wellPurposeId\": { \"type\": \"string\", \"description\": \"属性规范值字段,引用属性代码WELL_PURPOSE下的属性值\", \"x-dms-title\": \"井别ID\", \"x-dms-originalType\": \"string\" }, \"completionDate\": { \"type\": \"string\", \"format\": \"date\", \"description\": \"完井日期\", \"x-dms-title\": \"完井日期\", \"x-dms-originalType\": \"date\" }, \"geoDescription\": { \"type\": \"string\", \"description\": \"地理位置的描述\", \"x-dms-title\": \"地理位置\", \"x-dms-originalType\": \"string\" }, \"geoOffsetNorth\": { \"type\": \"number\", \"description\": \"井口纵坐标\", \"x-dms-mask\": \"coordinate\", \"x-dms-geom\": \"Point.y.wellhead\", \"x-dms-title\": \"井口纵坐标\", \"x-dms-originalType\": \"number\" }, \"wellCommonName\": { \"type\": \"string\", \"description\": \"通用井名,来源于钻井公报的汉字井名,必填\", \"x-dms-title\": \"井名\", \"x-dms-originalType\": \"string\" }, \"endDrillingDate\": { \"type\": \"string\", \"format\": \"date\", \"description\": \"最后一个井筒的完钻日期\", \"x-dms-title\": \"完钻日期\", \"x-dms-originalType\": \"date\" }, \"targetFormation\": { \"type\": \"string\", \"description\": \"目的层\", \"x-dms-title\": \"目的层\", \"x-dms-originalType\": \"string\" }, \"completionMethod\": { \"type\": \"string\", \"description\": \"完井方法\", \"x-dms-title\": \"完井方法\", \"x-dms-originalType\": \"string\" }, \"registrationDate\": { \"type\": \"string\", \"format\": \"date\", \"description\": \"井位通知单下达日期\", \"x-dms-title\": \"注册日期\", \"x-dms-originalType\": \"date\" }, \"sourceCreateDate\": { \"type\": \"string\", \"format\": \"date\", \"description\": \"记录源头系统采集数据的时间\", \"x-dms-title\": \"源头数据采集时间\", \"x-dms-originalType\": \"date\" }, \"coordinateSystemId\": { \"type\": \"string\", \"description\": \"坐标系统的唯一标示符\", \"x-dms-geom\": \"Point.srid.wellhead,Point.srid.design,Point.srid.actual\", \"x-dms-title\": \"坐标系统ID\", \"x-dms-originalType\": \"string\" }, \"completionFormation\": { \"type\": \"string\", \"description\": \"钻井完钻层位\", \"x-dms-title\": \"钻井完钻层位\", \"x-dms-originalType\": \"string\" }, \"coordinateSystemName\": { \"type\": \"string\", \"description\": \"坐标系名称\", \"x-dms-title\": \"坐标系名称\", \"x-dms-originalType\": \"string\" }, \"wellbaseGeoOffsetEast\": { \"type\": \"number\", \"description\": \"井底横坐标\", \"x-dms-title\": \"井底横坐标\", \"x-dms-originalType\": \"number\" }, \"wellbaseGeoOffsetNorth\": { \"type\": \"number\", \"description\": \"井底纵坐标\", \"x-dms-title\": \"井底纵坐标\", \"x-dms-originalType\": \"number\" } }, \"description\": \"井\", \"x-dms-groupView\": [ { \"name\": \"groupByWellCommonName\", \"group\": [ \"dataRegion\", \"wellCommonName\", \"wellId\" ] }, { \"name\": \"groupByPlatformName\", \"group\": [ \"dataRegion\", \"platformName\" ] } ], \"x-dms-identityId\": [ \"dsid\" ], \"x-dms-naturalKey\": [ \"dataRegion\", \"wellCommonName\" ], \"x-dms-defaultShow\": [ \"wellCommonName\" ] } } }, \"servers\": [ { \"url\": \"http://localhost:3000\", \"description\": \"Local Mock Server\" } ] }",
|
||
"vt": "json"
|
||
},
|
||
{
|
||
"p": "useLLM",
|
||
"v": "false",
|
||
"vt": "bool"
|
||
}
|
||
],
|
||
"repeat": "",
|
||
"crontab": "",
|
||
"once": false,
|
||
"onceDelay": 0.1,
|
||
"topic": "",
|
||
"x": 120,
|
||
"y": 220,
|
||
"wires": [
|
||
[
|
||
"211c041ab58c88f2"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "28e4a92525245f3d",
|
||
"type": "http in",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "单接口测试",
|
||
"url": "/health",
|
||
"method": "get",
|
||
"upload": false,
|
||
"skipBodyParsing": false,
|
||
"swaggerDoc": "",
|
||
"x": 180,
|
||
"y": 920,
|
||
"wires": [
|
||
[
|
||
"aa142468c6f487d6"
|
||
]
|
||
]
|
||
},
|
||
{
|
||
"id": "aa142468c6f487d6",
|
||
"type": "http response",
|
||
"z": "78d15f59dee4b6d8",
|
||
"name": "",
|
||
"statusCode": "0",
|
||
"headers": {},
|
||
"x": 340,
|
||
"y": 960,
|
||
"wires": []
|
||
}
|
||
] |