sks-maximo-utils 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # SKS Maximo Utils
2
+
3
+ Maximo 表结构和数据更新工具集。
4
+
5
+ ## 安装
6
+
7
+ ### 全局安装(CLI 使用)
8
+ ```bash
9
+ npm install -g sks-maximo-utils
10
+ ```
11
+
12
+ ### 项目中安装(引用使用)
13
+ ```bash
14
+ npm install sks-maximo-utils
15
+ ```
16
+
17
+ ## 使用方式
18
+
19
+ ### CLI 命令
20
+
21
+ 不带参数运行,打开配置目录:
22
+ ```bash
23
+ sks-maximo
24
+ ```
25
+
26
+ 带参数运行,加载指定环境配置:
27
+ ```bash
28
+ sks-maximo local # 使用 local 环境配置
29
+ sks-maximo dev # 使用 dev 环境配置
30
+ sks-maximo hd # 使用 hd 环境配置
31
+ ```
32
+
33
+ ### 在脚本中引用
34
+
35
+ ```javascript
36
+ import {
37
+ importMaxObject,
38
+ importMaxPresentation,
39
+ importMaxDomain,
40
+ loadConfig,
41
+ logger
42
+ } from 'sks-maximo-utils';
43
+
44
+ // 加载配置(可选,默认 local)
45
+ loadConfig('local');
46
+
47
+ // 导入 Maximo 对象配置
48
+ importMaxObject({ fileName: "DBCONFIGJSON/test.json", logname: "测试" });
49
+
50
+ // 导入应用 XML 配置
51
+ importMaxPresentation({ fileName: "SCREENSXML/test.xml", logname: "应用" });
52
+
53
+ // 导入域配置
54
+ importMaxDomain({ fileName: "DBDOMAINJSON/domain.json", logname: "域" });
55
+ ```
56
+
57
+ ## 配置文件
58
+
59
+ 首次运行时,会自动在 `~/.sks/nodeutils/` 目录创建 `config.json` 配置文件。
60
+
61
+ 配置结构:
62
+ ```json
63
+ {
64
+ "logLevel": "INFO",
65
+ "envs": {
66
+ "local": {
67
+ "baseUrl": "http://127.0.0.1:9080/maximo",
68
+ "apiKey": "your-api-key",
69
+ "maxauth": "",
70
+ "authType": "apiKey"
71
+ },
72
+ "dev": {
73
+ "baseUrl": "https://mdev/maximo",
74
+ "apiKey": "your-api-key",
75
+ "maxauth": "",
76
+ "authType": "apiKey"
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ ### 配置说明
83
+
84
+ | 字段 | 说明 |
85
+ |------|------|
86
+ | logLevel | 全局日志级别:TRACE, DEBUG, INFO, WARN, ERROR, FATAL |
87
+ | baseUrl | Maximo 服务地址 |
88
+ | apiKey | API 认证密钥 |
89
+ | maxauth | Maxauth 认证值 |
90
+ | authType | 认证类型:apiKey 或 maxauth |
91
+
92
+ 环境配置中可单独设置 `logLevel`,优先级高于全局配置。
93
+
94
+ ## API
95
+
96
+ ### 导入方法
97
+
98
+ | 方法 | 说明 |
99
+ |------|------|
100
+ | importMaxObject | 导入 Maximo 对象配置(JSON) |
101
+ | importMaxPresentation | 导入应用 XML 配置 |
102
+ | importMaxDomain | 导入域配置 |
103
+
104
+ ### 其他方法
105
+
106
+ | 方法 | 说明 |
107
+ |------|------|
108
+ | loadConfig(env) | 加载指定环境配置 |
109
+ | readFileContent(fileName) | 读取文件内容 |
110
+ | readJsonFile(fileName) | 读取并解析 JSON 文件 |
111
+ | fileExists(fileName) | 检查文件是否存在 |
112
+
113
+ ## 开发
114
+
115
+ ```bash
116
+ # 安装依赖
117
+ npm install
118
+
119
+ # 本地链接(测试)
120
+ npm link
121
+
122
+ # 在其他项目中链接测试
123
+ npm link sks-maximo-utils
124
+ ```
125
+
126
+ ## License
127
+
128
+ ISC
package/TASK/TASK01.md ADDED
@@ -0,0 +1,13 @@
1
+ # TASK01
2
+ nodeutils 目录是我用来编写 js 类的脚本进行更新maximo表结构等操作的
3
+
4
+ 现在我想将这几个包装成全局函数,方便调用
5
+
6
+
7
+ 配置文件我想使用json放到 ~/.sks/nodeutils 目录下
8
+ 文件名是 config.json
9
+
10
+ 配置中的 filePath 我想在配置中去掉,获取node执行的目录
11
+
12
+
13
+ 参考 E:\devwork\ideawork\maximo91_soloncode\nodetasks\0626001.js
package/cli.js ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ import { exec } from 'child_process';
3
+ import { checkAndCreateConfig, getConfigDir, loadConfig } from './nodeutils/config.js';
4
+
5
+ const args = process.argv.slice(2);
6
+ const env = args[0];
7
+
8
+ if (!env) {
9
+ // 不带参数:检查配置并打开目录
10
+ const configPath = checkAndCreateConfig();
11
+ const configDir = getConfigDir();
12
+
13
+ console.log(`配置文件位置: ${configPath}`);
14
+ console.log(`正在打开配置目录...`);
15
+
16
+ // 根据操作系统打开目录
17
+ const platform = process.platform;
18
+ let command;
19
+
20
+ if (platform === 'win32') {
21
+ command = `explorer "${configDir}"`;
22
+ } else if (platform === 'darwin') {
23
+ command = `open "${configDir}"`;
24
+ } else {
25
+ command = `xdg-open "${configDir}"`;
26
+ }
27
+
28
+ exec(command, (error) => {
29
+ if (error) {
30
+ console.error(`打开目录失败: ${error.message}`);
31
+ console.log(`请手动打开: ${configDir}`);
32
+ }
33
+ });
34
+ } else {
35
+ // 带参数:加载配置
36
+ loadConfig(env);
37
+ }
38
+
39
+ export * from './nodeutils/maximoTools.js';
40
+ export * from './nodeutils/mockdataTools.js';
41
+ export * from './nodeutils/fileUtils.js';
42
+ export { default as logger } from './nodeutils/logger.js';
43
+ export { default as request } from './nodeutils/requestHttp.js';
package/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // 主入口文件 - 导出所有工具函数
2
+ export { loadConfig, getConfig, getWorkDir } from './nodeutils/config.js';
3
+ export { default as logger } from './nodeutils/logger.js';
4
+ export { default as request } from './nodeutils/requestHttp.js';
5
+ export { readFileContent, readJsonFile, readXmlFile, fileExists } from './nodeutils/fileUtils.js';
6
+ export { importMaxObject, importMaxPresentation, importMaxDomain } from './nodeutils/maximoTools.js';
7
+ export { sendMockData, sendMockDataBatch, sendPOMockData, sendPOMockDataBatch, sendRlImportMockDataBatch } from './nodeutils/mockdataTools.js';
@@ -0,0 +1,154 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import logger from './logger.js';
6
+
7
+ // 获取当前模块目录
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+
10
+ let globalConfig = null;
11
+
12
+ function getConfigPath() {
13
+ const homeDir = os.homedir();
14
+ return path.join(homeDir, '.sks', 'nodeutils', 'config.json');
15
+ }
16
+
17
+ export function getConfigDir() {
18
+ return path.dirname(getConfigPath());
19
+ }
20
+
21
+ // 检查配置文件是否存在,不存在则创建
22
+ export function checkAndCreateConfig() {
23
+ const configPath = getConfigPath();
24
+ const configDir = getConfigDir();
25
+
26
+ if (!fs.existsSync(configDir)) {
27
+ fs.mkdirSync(configDir, { recursive: true });
28
+ }
29
+
30
+ if (!fs.existsSync(configPath)) {
31
+ copySampleConfig(configPath);
32
+ }
33
+
34
+ return configPath;
35
+ }
36
+
37
+ export function loadConfig(env = null) {
38
+ const configPath = getConfigPath();
39
+
40
+ if (!env) {
41
+ const args = process.argv.slice(2);
42
+ env = args[0] || 'local';
43
+ }
44
+
45
+ try {
46
+ const configDir = path.dirname(configPath);
47
+ if (!fs.existsSync(configDir)) {
48
+ fs.mkdirSync(configDir, { recursive: true });
49
+ }
50
+
51
+ if (!fs.existsSync(configPath)) {
52
+ logger.warn(`配置文件不存在: ${configPath},将复制示例配置`);
53
+ copySampleConfig(configPath);
54
+ }
55
+
56
+ const configData = fs.readFileSync(configPath, 'utf-8');
57
+ const allConfigs = JSON.parse(configData);
58
+
59
+ // 获取全局配置
60
+ const globalLogLevel = allConfigs.logLevel || 'INFO';
61
+
62
+ // 获取环境配置
63
+ const envs = allConfigs.envs || {};
64
+ const envConfig = envs[env] || envs.local || envs.dev;
65
+
66
+ if (!envConfig) {
67
+ throw new Error(`未找到环境 ${env} 的配置`);
68
+ }
69
+
70
+ // 合并配置:环境配置优先于全局配置
71
+ globalConfig = { ...envConfig };
72
+
73
+ // 设置默认 authType
74
+ if (!globalConfig.authType) {
75
+ globalConfig.authType = 'apiKey';
76
+ }
77
+
78
+ // logLevel 优先使用环境配置,否则使用全局配置
79
+ const logLevel = envConfig.logLevel || globalLogLevel;
80
+
81
+ // 规范化 baseUrl
82
+ if (globalConfig.baseUrl) {
83
+ globalConfig.baseUrl = globalConfig.baseUrl.replace(/\\/g, '/');
84
+ if (!globalConfig.baseUrl.endsWith('/')) {
85
+ globalConfig.baseUrl += '/';
86
+ }
87
+ }
88
+
89
+ logger.setLevel(logLevel);
90
+ logger.info(`[Config] 已加载配置,环境: ${env}, 日志级别: ${logLevel}`);
91
+
92
+ return globalConfig;
93
+ } catch (error) {
94
+ logger.error(`[Config] 加载配置文件失败: ${error.message}`);
95
+ throw error;
96
+ }
97
+ }
98
+
99
+ function copySampleConfig(configPath) {
100
+ const sampleConfigPath = path.join(__dirname, 'sample.config.json');
101
+
102
+ const configDir = path.dirname(configPath);
103
+ if (!fs.existsSync(configDir)) {
104
+ fs.mkdirSync(configDir, { recursive: true });
105
+ }
106
+
107
+ if (fs.existsSync(sampleConfigPath)) {
108
+ fs.copyFileSync(sampleConfigPath, configPath);
109
+ logger.info(`[Config] 已复制示例配置文件到: ${configPath}`);
110
+ } else {
111
+ // 如果 sample.config.json 不存在,创建默认配置
112
+ const defaultConfig = {
113
+ "logLevel": "INFO",
114
+ "envs": {
115
+ "local": {
116
+ "baseUrl": "http://127.0.0.1:9080/maximo",
117
+ "apiKey": "your-api-key-here",
118
+ "maxauth": ""
119
+ },
120
+ "dev": {
121
+ "baseUrl": "https://mdev/maximo",
122
+ "apiKey": "your-api-key-here",
123
+ "maxauth": ""
124
+ },
125
+ "hd": {
126
+ "baseUrl": "http://xxxxx:9080/maximo",
127
+ "apiKey": "your-api-key-here",
128
+ "maxauth": ""
129
+ }
130
+ }
131
+ };
132
+ fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2), 'utf-8');
133
+ logger.info(`[Config] 已创建默认配置文件: ${configPath}`);
134
+ }
135
+ }
136
+
137
+ export function getConfig() {
138
+ if (!globalConfig) {
139
+ return loadConfig();
140
+ }
141
+ return globalConfig;
142
+ }
143
+
144
+ export function getWorkDir() {
145
+ return process.cwd();
146
+ }
147
+
148
+ export default {
149
+ loadConfig,
150
+ getConfig,
151
+ getWorkDir,
152
+ getConfigDir,
153
+ checkAndCreateConfig
154
+ };
@@ -0,0 +1,96 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { getWorkDir } from './config.js';
4
+
5
+ /**
6
+ * 根据文件名获取文件路径
7
+ * @param {string} fileNameIn - 文件名(可以是相对路径或绝对路径)
8
+ * @returns {string} 文件路径
9
+ */
10
+ function filePathForName(fileNameIn) {
11
+ var fileName = fileNameIn.replace(/\\/g, '/');
12
+
13
+ // 如果是绝对路径,直接返回
14
+ if (path.isAbsolute(fileName)) {
15
+ return fileName;
16
+ }
17
+
18
+ // 如果是相对路径,使用当前工作目录
19
+ const workDir = getWorkDir();
20
+ return path.join(workDir, fileName);
21
+ }
22
+
23
+ /**
24
+ * 根据文件名读取文件内容
25
+ * @param {string} fileName - 文件名(可以是相对路径或绝对路径)
26
+ * @param {string} encoding - 文件编码,默认 'utf-8'
27
+ * @returns {string|null} 文件内容,如果文件不存在返回 null
28
+ */
29
+ function readFileContent(fileName, encoding = 'utf-8') {
30
+ const filePath = filePathForName(fileName);
31
+ try {
32
+ // 读取文件内容
33
+ const content = fs.readFileSync(filePath, encoding);
34
+ return content;
35
+ } catch (error) {
36
+ console.error(`读取文件失败: ${fileName}`, error.message);
37
+ return null;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * 根据文件名读取JSON文件并解析
43
+ * @param {string} fileName - JSON文件名
44
+ * @returns {object|null} 解析后的JSON对象,如果失败返回 null
45
+ */
46
+ function readJsonFile(fileName) {
47
+ const content = readFileContent(fileName);
48
+ try {
49
+ if (!content) {
50
+ return null;
51
+ }
52
+ const jsonData = JSON.parse(content);
53
+ return jsonData;
54
+ } catch (error) {
55
+ console.error(`解析JSON文件失败: ${fileName}`, error.message);
56
+ return null;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * 根据文件名读取XML文件
62
+ * @param {string} fileName - XML文件名
63
+ * @returns {string|null} XML文件内容
64
+ */
65
+ function readXmlFile(fileName) {
66
+ return readFileContent(fileName, 'utf-8');
67
+ }
68
+
69
+ /**
70
+ * 检查文件是否存在
71
+ * @param {string} fileNameIn - 文件名
72
+ * @returns {boolean} 文件是否存在
73
+ */
74
+ function fileExists(fileNameIn) {
75
+ try {
76
+ var fileName = fileNameIn.replace(/\\/g, '/');
77
+
78
+ // 如果是绝对路径,直接检查
79
+ if (path.isAbsolute(fileName)) {
80
+ return fs.existsSync(fileName);
81
+ }
82
+
83
+ // 如果是相对路径,使用当前工作目录
84
+ const filePath = path.join(getWorkDir(), fileName);
85
+ return fs.existsSync(filePath);
86
+ } catch (error) {
87
+ return false;
88
+ }
89
+ }
90
+
91
+ export {
92
+ readFileContent,
93
+ readJsonFile,
94
+ readXmlFile,
95
+ fileExists
96
+ };
@@ -0,0 +1,57 @@
1
+ // logger.js
2
+ const LEVELS = {
3
+ TRACE: 0,
4
+ DEBUG: 1,
5
+ INFO: 2,
6
+ WARN: 3,
7
+ ERROR: 4,
8
+ FATAL: 5
9
+ };
10
+
11
+ class ConsoleLogger {
12
+ constructor(level = 'INFO') {
13
+ this.level = LEVELS[level] !== undefined ? LEVELS[level] : LEVELS.INFO;
14
+ }
15
+
16
+ // 设置日志级别
17
+ setLevel(level) {
18
+ if (LEVELS[level] !== undefined) {
19
+ this.level = LEVELS[level];
20
+ } else {
21
+ console.warn(`无效的日志级别: ${level},保持当前级别`);
22
+ }
23
+ }
24
+
25
+ // 核心打印方法
26
+ _print(levelName, levelValue, args) {
27
+ if (levelValue < this.level) return;
28
+
29
+ const timestamp = new Date().toLocaleTimeString();
30
+ const prefix = `[${timestamp}] [${levelName}]`;
31
+
32
+ switch (levelValue) {
33
+ case LEVELS.TRACE:
34
+ case LEVELS.DEBUG:
35
+ case LEVELS.INFO:
36
+ console.log(prefix, ...args);
37
+ break;
38
+ case LEVELS.WARN:
39
+ console.warn(prefix, ...args); // 输出到 stderr,但通常显示为黄色警告
40
+ break;
41
+ case LEVELS.ERROR:
42
+ case LEVELS.FATAL:
43
+ console.error(prefix, ...args); // 输出到 stderr,通常显示为红色错误
44
+ break;
45
+ }
46
+ }
47
+
48
+ trace(...args) { this._print('TRACE', LEVELS.TRACE, args); }
49
+ debug(...args) { this._print('DEBUG', LEVELS.DEBUG, args); }
50
+ info(...args) { this._print('INFO', LEVELS.INFO, args); }
51
+ warn(...args) { this._print('WARN', LEVELS.WARN, args); }
52
+ error(...args) { this._print('ERROR', LEVELS.ERROR, args); }
53
+ fatal(...args) { this._print('FATAL', LEVELS.FATAL, args); }
54
+ }
55
+
56
+ const logger = new ConsoleLogger('INFO'); // 默认导出一个实例,级别为 INFO
57
+ export default logger
@@ -0,0 +1,206 @@
1
+ import request from '../nodeutils/requestHttp.js'
2
+ import { readFileContent, readJsonFile, readXmlFile } from './fileUtils.js'
3
+ import logger from './logger.js';
4
+
5
+ /**
6
+ * 导入Maximo对象配置
7
+ * @param {Object} options - 配置选项
8
+ * @param {string} options.fileName - 配置文件名(JSON或XML)
9
+ * @param {string} options.logname - 日志打印名称
10
+ * @returns {Promise<boolean>} 是否导入成功
11
+ */
12
+ export async function importMaxObject({fileName, logname}) {
13
+ if(!logname||logname===""||logname===null){
14
+ logname = fileName
15
+ }
16
+ try {
17
+ // 根据文件扩展名选择读取方式
18
+ let fileContent = null;
19
+ fileContent = readFileContent(fileName);
20
+
21
+ // 检查文件是否成功读取
22
+ if (!fileContent) {
23
+ logger.error(`[${logname}]文件读取失败: ${fileName}`);
24
+ return false;
25
+ }
26
+
27
+ logger.info(`[${logname}]成功读取文件: ${fileName}`);
28
+ logger.info(`[${logname}]文件大小: ${fileContent.length} 字节`);
29
+ logger.info(`[${logname}]准备导入对象: ${logname || '未指定'}`);
30
+
31
+ // 发送请求到Maximo API
32
+ logger.info(`[${logname}]开始发送请求...`);
33
+ const response = await request({
34
+ url: '/api/script/SHARPTREE.AUTOSCRIPT.LIBRARY',
35
+ method: 'post',
36
+ params: {
37
+ develop: true,
38
+ _langcode: 'zh',
39
+ },
40
+ data: fileContent // 将文件内容作为请求体发送
41
+ })
42
+
43
+ logger.info(`[${logname}]收到响应`);
44
+ logger.info(`[${logname}]响应类型:`, typeof response);
45
+ logger.info(`[${logname}]响应内容:`, response);
46
+
47
+ // 检查响应 - requestHttp的响应拦截器已返回res.data
48
+ if (response !== null && response !== undefined) {
49
+ logger.warn(`[${logname}]导入成功`)
50
+ return true
51
+ } else {
52
+ logger.error(`[${logname}]导入失败,响应为空或null`)
53
+ logger.error(`[${logname}]响应值:`, response)
54
+ return false
55
+ }
56
+ } catch (error) {
57
+ logger.error(`[${logname}]请求出错:`, error.message)
58
+ if (error.response) {
59
+ // 服务器返回了错误状态码
60
+ // logger.error(`[${logname}]错误状态码:`, error.response.status)
61
+ // logger.error(`[${logname}]错误响应数据:`, error.response.data)
62
+ } else if (error.request) {
63
+ // 请求已发出但没有收到响应
64
+ logger.error(`[${logname}]请求已发出但无响应`)
65
+ // logger.error(`[${logname}]请求URL:`, error.request.url)
66
+ // logger.error(`[${logname}]请求方法:`, error.request.method)
67
+ } else {
68
+ // 其他错误
69
+ logger.error(`[${logname}]错误详情:`, error)
70
+ }
71
+ return false
72
+ }
73
+ }
74
+
75
+
76
+ /**
77
+ * 导入应用XML配置到Maximo
78
+ * @param {Object} options - 配置选项
79
+ * @param {string} options.fileName - XML配置文件名
80
+ * @param {string} options.logname - 日志打印名称
81
+ * @returns {Promise<boolean>} 是否导入成功
82
+ */
83
+ export async function importMaxPresentation({fileName, logname}) {
84
+ if(!logname||logname===""||logname===null){
85
+ logname = fileName
86
+ }
87
+ try {
88
+ let fileContent = null;
89
+ fileContent = readFileContent(fileName);
90
+
91
+ if (!fileContent) {
92
+ logger.error(`[${logname}]文件读取失败: ${fileName}`);
93
+ return false;
94
+ }
95
+
96
+ logger.info(`[${logname}]成功读取文件: ${fileName}`);
97
+ logger.info(`[${logname}]文件大小: ${fileContent.length} 字节`);
98
+
99
+ logger.info(`[${logname}]开始发送请求...`);
100
+ const response = await request({
101
+ url: '/api/script/SHARPTREE.AUTOSCRIPT.SCREENS',
102
+ method: 'post',
103
+ headers: {
104
+ 'Content-Type': 'application/xml'
105
+ },
106
+ params: {
107
+ _langcode: 'zh',
108
+ },
109
+ data: fileContent
110
+ })
111
+
112
+ logger.info(`[${logname}]收到响应`);
113
+ logger.info(`[${logname}]响应类型:`, typeof response);
114
+ logger.info(`[${logname}]响应内容:`, response);
115
+
116
+ if (response !== null && response !== undefined) {
117
+ logger.warn(`[${logname}]导入成功`)
118
+ return true
119
+ } else {
120
+ logger.error(`[${logname}]导入失败,响应为空或null`)
121
+ logger.error(`[${logname}]响应值:`, response)
122
+ return false
123
+ }
124
+ } catch (error) {
125
+ logger.error(`[${logname}]请求出错:`, error.message)
126
+ if (error.response) {
127
+ // 服务器返回了错误状态码
128
+ } else if (error.request) {
129
+ logger.error(`[${logname}]请求已发出但无响应`)
130
+ } else {
131
+ logger.error(`[${logname}]错误详情:`, error)
132
+ }
133
+ return false
134
+ }
135
+ }
136
+
137
+
138
+ /**
139
+ * 导入Maximo域
140
+ * @param {Object} options - 配置选项
141
+ * @param {string} options.fileName - 配置文件名(JSON或XML)
142
+ * @param {string} options.logname - 日志打印名称
143
+ * @returns {Promise<boolean>} 是否导入成功
144
+ */
145
+ export async function importMaxDomain({fileName, logname}) {
146
+ if(!logname||logname===""||logname===null){
147
+ logname = fileName
148
+ }
149
+ try {
150
+ // 根据文件扩展名选择读取方式
151
+ let fileContent = null;
152
+ fileContent = readFileContent(fileName);
153
+
154
+ // 检查文件是否成功读取
155
+ if (!fileContent) {
156
+ logger.error(`[${logname}]文件读取失败: ${fileName}`);
157
+ return false;
158
+ }
159
+
160
+ logger.info(`[${logname}]成功读取文件: ${fileName}`);
161
+ logger.info(`[${logname}]文件大小: ${fileContent.length} 字节`);
162
+ logger.info(`[${logname}]准备导入domain: ${logname || '未指定'}`);
163
+
164
+ // 发送请求到Maximo API
165
+ logger.info(`[${logname}]开始发送请求...`);
166
+ const response = await request({
167
+ url: '/api/script/SKS_DEPLOY_DOMAIN',
168
+ method: 'post',
169
+ params: {
170
+ develop: true,
171
+ _langcode: 'zh',
172
+ },
173
+ data: fileContent // 将文件内容作为请求体发送
174
+ })
175
+
176
+ logger.info(`[${logname}]收到响应`);
177
+ logger.info(`[${logname}]响应类型:`, typeof response);
178
+ logger.info(`[${logname}]响应内容:`, response);
179
+
180
+ // 检查响应 - requestHttp的响应拦截器已返回res.data
181
+ if (response !== null && response !== undefined) {
182
+ logger.warn(`[${logname}]导入成功`)
183
+ return true
184
+ } else {
185
+ logger.error(`[${logname}]导入失败,响应为空或null`)
186
+ logger.error(`[${logname}]响应值:`, response)
187
+ return false
188
+ }
189
+ } catch (error) {
190
+ logger.error(`[${logname}]请求出错:`, error.message)
191
+ if (error.response) {
192
+ // 服务器返回了错误状态码
193
+ // logger.error(`[${logname}]错误状态码:`, error.response.status)
194
+ // logger.error(`[${logname}]错误响应数据:`, error.response.data)
195
+ } else if (error.request) {
196
+ // 请求已发出但没有收到响应
197
+ logger.error(`[${logname}]请求已发出但无响应`)
198
+ // logger.error(`[${logname}]请求URL:`, error.request.url)
199
+ // logger.error(`[${logname}]请求方法:`, error.request.method)
200
+ } else {
201
+ // 其他错误
202
+ logger.error(`[${logname}]错误详情:`, error)
203
+ }
204
+ return false
205
+ }
206
+ }
@@ -0,0 +1,264 @@
1
+ import request from '../nodeutils/requestHttp.js'
2
+ import { readJsonFile } from './fileUtils.js'
3
+
4
+ /**
5
+ * 发送mock数据到TEST_MOCKDATA_ITEMA接口
6
+ * @param {Object} options - 配置选项
7
+ * @param {string} options.fileName - mock数据JSON文件名(相对于项目根目录或绝对路径)
8
+ * @param {string} options.logname - 日志打印名称
9
+ * @returns {Promise<boolean>} 是否发送成功
10
+ */
11
+ export async function sendMockData({fileName, logname="mockdata"}) {
12
+ try {
13
+ // 读取JSON文件
14
+ const jsonData = readJsonFile(fileName);
15
+
16
+ if (!jsonData) {
17
+ console.error(`[${logname}]JSON文件读取/解析失败: ${fileName}`);
18
+ return false;
19
+ }
20
+
21
+ console.log(`[${logname}]成功读取文件: ${fileName}`);
22
+ console.log(`[${logname}]申请单号: ${jsonData.APPLYNUM || 'N/A'}`);
23
+ console.log(`[${logname}]明细行数: ${(jsonData.IBM_ITEM_APPLYLINE && jsonData.IBM_ITEM_APPLYLINE.length) || 0}`);
24
+
25
+ // 发送请求到TEST_MOCKDATA_ITEMA接口
26
+ console.log(`[${logname}]开始发送请求...`);
27
+ const response = await request({
28
+ url: '/api/script/TEST_MOCKDATA_ITEMA',
29
+ method: 'post',
30
+ data: jsonData // 将JSON对象作为requestBody传入脚本
31
+ })
32
+
33
+ console.log(`[${logname}]收到响应`);
34
+ console.log(`[${logname}]响应类型:`, typeof response);
35
+ console.log(`[${logname}]响应内容:`, response);
36
+
37
+ // 检查响应
38
+ if (response !== null && response !== undefined) {
39
+ console.log(`[${logname}]发送成功`)
40
+ return true
41
+ } else {
42
+ console.error(`[${logname}]发送失败,响应为空或null`)
43
+ console.error(`[${logname}]响应值:`, response)
44
+ return false
45
+ }
46
+ } catch (error) {
47
+ console.error(`[${logname}]请求出错:`, error.message)
48
+ if (error.response) {
49
+ console.error(`[${logname}]错误状态码:`, error.response.status)
50
+ console.error(`[${logname}]错误响应数据:`, error.response.data)
51
+ } else if (error.request) {
52
+ console.error(`[${logname}]请求已发出但无响应`)
53
+ } else {
54
+ console.error(`[${logname}]错误详情:`, error)
55
+ }
56
+ return false
57
+ }
58
+ }
59
+
60
+ /**
61
+ * 批量发送mock数据(遍历指定目录下的所有JSON文件,或指定文件列表)
62
+ * @param {Object} options - 配置选项
63
+ * @param {string[]} options.files - mock数据JSON文件名列表
64
+ * @param {string} options.logname - 日志打印名称
65
+ * @returns {Promise<{total: number, success: number, fail: number}>} 统计结果
66
+ */
67
+ export async function sendMockDataBatch({files, logname="mockdata"}) {
68
+ const result = { total: files.length, success: 0, fail: 0 };
69
+
70
+ for (let i = 0; i < files.length; i++) {
71
+ const fileName = files[i];
72
+ console.log(`\n========== [${logname}] 第 ${i + 1}/${files.length} 个 ==========`);
73
+ console.log(`[${logname}]文件: ${fileName}`);
74
+
75
+ const ok = await sendMockData({fileName, logname});
76
+ if (ok) {
77
+ result.success++;
78
+ } else {
79
+ result.fail++;
80
+ }
81
+ }
82
+
83
+ console.log(`\n========== [${logname}] 批量导入完成 ==========`);
84
+ console.log(`[${logname}]总计: ${result.total}, 成功: ${result.success}, 失败: ${result.fail}`);
85
+
86
+ return result;
87
+ }
88
+
89
+ /**
90
+ * 发送PO mock数据到MXPO OSLC API
91
+ * @param {Object} options - 配置选项
92
+ * @param {string} options.fileName - mock数据JSON文件名(包含PO数组)
93
+ * @param {string} options.logname - 日志打印名称
94
+ * @returns {Promise<{total: number, success: number, fail: number}>} 统计结果
95
+ */
96
+ export async function sendPOMockData({fileName, logname="PO"}) {
97
+ try {
98
+ const jsonData = readJsonFile(fileName);
99
+ if (!jsonData || !Array.isArray(jsonData)) {
100
+ console.error(`[${logname}]JSON文件读取/解析失败或格式非数组: ${fileName}`);
101
+ return { total: 0, success: 0, fail: 0 };
102
+ }
103
+
104
+ console.log(`[${logname}]成功读取文件: ${fileName}, 共 ${jsonData.length} 个PO`);
105
+
106
+ const result = { total: jsonData.length, success: 0, fail: 0 };
107
+
108
+ for (let i = 0; i < jsonData.length; i++) {
109
+ const po = jsonData[i];
110
+ console.log(`\n--- [${logname}] PO ${i + 1}/${jsonData.length} ---`);
111
+ console.log(`[${logname}]VENDOR: ${po['spi:vendor']}, DESCRIPTION: ${po['spi:description']}`);
112
+ console.log(`[${logname}]POLINE数: ${(po['spi:poline'] && po['spi:poline'].length) || 0}`);
113
+
114
+ try {
115
+ console.log(`[${logname}]开始发送请求...`);
116
+ const response = await request({
117
+ url: '/api/os/MXPO',
118
+ method: 'post',
119
+ data: po
120
+ });
121
+
122
+ console.log(`[${logname}]响应:`, response);
123
+ result.success++;
124
+ } catch (error) {
125
+ console.error(`[${logname}]PO创建失败:`, error.message);
126
+ if (error.response) {
127
+ console.error(`[${logname}]错误状态码:`, error.response.status);
128
+ console.error(`[${logname}]错误响应:`, error.response.data);
129
+ }
130
+ result.fail++;
131
+ }
132
+ }
133
+
134
+ console.log(`\n[${logname}]文件 ${fileName} 处理完成: 成功 ${result.success}, 失败 ${result.fail}`);
135
+ return result;
136
+ } catch (error) {
137
+ console.error(`[${logname}]处理文件出错:`, error.message);
138
+ return { total: 0, success: 0, fail: 0 };
139
+ }
140
+ }
141
+
142
+ /**
143
+ * 批量发送PO mock数据
144
+ * @param {Object} options - 配置选项
145
+ * @param {string[]} options.files - mock数据JSON文件名列表
146
+ * @param {string} options.logname - 日志打印名称
147
+ * @returns {Promise<{total: number, success: number, fail: number}>} 统计结果
148
+ */
149
+ export async function sendPOMockDataBatch({files, logname="PO"}) {
150
+ const result = { total: 0, success: 0, fail: 0 };
151
+
152
+ for (let i = 0; i < files.length; i++) {
153
+ const fileName = files[i];
154
+ console.log(`\n========== [${logname}] 文件 ${i + 1}/${files.length} ==========`);
155
+
156
+ const r = await sendPOMockData({fileName, logname});
157
+ result.total += r.total;
158
+ result.success += r.success;
159
+ result.fail += r.fail;
160
+ }
161
+
162
+ console.log(`\n========== [${logname}] 全部完成 ==========`);
163
+ console.log(`[${logname}]总计: ${result.total}, 成功: ${result.success}, 失败: ${result.fail}`);
164
+
165
+ return result;
166
+ }
167
+
168
+ /**
169
+ * 批量发送RL_IMPORT mock数据到TEST_MOCKDATA_RLIMPORT脚本接口
170
+ * @param {Object} options - 配置选项
171
+ * @param {number} options.loopCount - 循环调用次数,默认1
172
+ * @param {number} options.rlCount - 每次调用创建的IBM_RL主表记录数,默认5
173
+ * @param {number} options.minImportsPerRL - 每条主表最少子表记录数,默认100
174
+ * @param {number} options.maxImportsPerRL - 每条主表最多子表记录数,默认500
175
+ * @param {number} options.apprPercent - STATUS=APPR的比例(%),默认90
176
+ * @param {string} options.logname - 日志打印名称,默认RL_IMPORT
177
+ * @returns {Promise<{loopCount: number, totalRL: number, totalImports: number, success: number, fail: number}>} 统计结果
178
+ */
179
+ export async function sendRlImportMockDataBatch({
180
+ loopCount = 1,
181
+ rlCount = 5,
182
+ minImportsPerRL = 100,
183
+ maxImportsPerRL = 500,
184
+ apprPercent = 90,
185
+ logname = "RL_IMPORT"
186
+ } = {}) {
187
+ const result = {
188
+ loopCount,
189
+ totalRL: 0,
190
+ totalImports: 0,
191
+ success: 0,
192
+ fail: 0
193
+ };
194
+
195
+ const body = {
196
+ rlCount,
197
+ minImportsPerRL,
198
+ maxImportsPerRL,
199
+ apprPercent
200
+ };
201
+
202
+ console.log(`\n========== [${logname}] 开始批量生成 ==========`);
203
+ console.log(`[${logname}]循环次数: ${loopCount}, 每次创建${rlCount}条IBM_RL`);
204
+ console.log(`[${logname}]每条主表子表数: ${minImportsPerRL}~${maxImportsPerRL}`);
205
+ console.log(`[${logname}]APPR比例: ${apprPercent}% (REQSOURCE/RLTYPE/PARTTYPE由脚本随机取域值)`);
206
+ console.log(`[${logname}]预计总IBM_RL: ${loopCount * rlCount} 条`);
207
+ console.log(`[${logname}]预计总IBM_RLIMPORT: ${loopCount * rlCount * Math.floor((minImportsPerRL + maxImportsPerRL) / 2)} 条(估算)`);
208
+
209
+ const startTime = Date.now();
210
+
211
+ for (let i = 0; i < loopCount; i++) {
212
+ console.log(`\n--- [${logname}] 第 ${i + 1}/${loopCount} 次调用 ---`);
213
+ try {
214
+ const response = await request({
215
+ url: '/api/script/TEST_MOCKDATA_RLIMPORT',
216
+ method: 'post',
217
+ data: body
218
+ });
219
+
220
+ if (response !== null && response !== undefined) {
221
+ // 解析脚本返回的统计信息
222
+ let parsed = null;
223
+ try {
224
+ parsed = (typeof response === 'string') ? JSON.parse(response) : response;
225
+ } catch (parseErr) {
226
+ parsed = null;
227
+ }
228
+
229
+ if (parsed && parsed.status === 'success') {
230
+ const rlCreated = parsed.rlCount || rlCount;
231
+ const importsCreated = parsed.totalImports || 0;
232
+ result.totalRL += rlCreated;
233
+ result.totalImports += importsCreated;
234
+ result.success++;
235
+ console.log(`[${logname}]成功: RL=${rlCreated}, IMPORT=${importsCreated}, APPR=${parsed.apprCount || '?'}, DRAFT=${parsed.draftCount || '?'}`);
236
+ } else {
237
+ result.success++;
238
+ result.totalRL += rlCount;
239
+ console.log(`[${logname}]成功(响应无统计): 按默认${rlCount}条RL计算`);
240
+ console.log(`[${logname}]响应:`, response);
241
+ }
242
+ } else {
243
+ result.fail++;
244
+ console.error(`[${logname}]第${i + 1}次调用失败,响应为空`);
245
+ }
246
+ } catch (error) {
247
+ result.fail++;
248
+ console.error(`[${logname}]第${i + 1}次调用出错:`, error.message);
249
+ if (error.response) {
250
+ console.error(`[${logname}]错误状态码:`, error.response.status);
251
+ console.error(`[${logname}]错误响应:`, error.response.data);
252
+ }
253
+ }
254
+ }
255
+
256
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
257
+ console.log(`\n========== [${logname}] 批量生成完成 ==========`);
258
+ console.log(`[${logname}]循环次数: ${loopCount}, 成功: ${result.success}, 失败: ${result.fail}`);
259
+ console.log(`[${logname}]总IBM_RL: ${result.totalRL} 条`);
260
+ console.log(`[${logname}]总IBM_RLIMPORT: ${result.totalImports} 条`);
261
+ console.log(`[${logname}]耗时: ${elapsed} 秒`);
262
+
263
+ return result;
264
+ }
@@ -0,0 +1,159 @@
1
+ import http from 'http';
2
+ import https from 'https';
3
+ import axios from 'axios';
4
+ import { getConfig } from './config.js';
5
+ import logger from './logger.js';
6
+
7
+ // 完全禁用 TLS 证书验证(仅用于开发环境)
8
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
9
+
10
+ /**
11
+ * 限制日志输出长度
12
+ * @param {any} data 要输出的数据
13
+ * @param {number} [maxLength=200] 最大长度,默认 200
14
+ * @returns {{truncated: boolean, data: string}} true 表示未超出限制,false 表示已截断
15
+ */
16
+ function limitLogOutput(data, maxLength = 200) {
17
+ const str = typeof data === 'string' ? data : JSON.stringify(data);
18
+
19
+ if (str.length <= maxLength) {
20
+ return { truncated: false, data: str };
21
+ } else {
22
+ const truncated = str.substring(0, maxLength) + `...(${str.length}个字)`;
23
+ return { truncated: true, data: truncated };
24
+ }
25
+ }
26
+
27
+ // 创建axios实例
28
+ function createService() {
29
+ const config = getConfig();
30
+ const service = axios.create({
31
+ baseURL: config.baseUrl,
32
+ timeout: 50000,
33
+ headers: {
34
+ 'User-Agent': 'RequestHttp'
35
+ },
36
+ noAuth: false,
37
+ authType: 'apiKey',
38
+ });
39
+
40
+ // request拦截器
41
+ service.interceptors.request.use(config => {
42
+ const globalConfig = getConfig();
43
+ logger.info('[Request] 开始处理请求');
44
+ logger.info('[Request] URL:', config.url);
45
+ logger.info('[Request] Method:', config.method);
46
+
47
+ // 是否需要设置 token
48
+ if (!config.noAuth) {
49
+ if (config.authType == 'apiKey') {
50
+ config.headers['apiKey'] = globalConfig.apiKey;
51
+ logger.info('[Request] 设置 apiKey');
52
+ } else {
53
+ if (config.maxauth) {
54
+ config.headers['maxauth'] = globalConfig.maxauth;
55
+ logger.info('[Request] 设置 maxauth');
56
+ }
57
+ }
58
+ }
59
+
60
+ if (!config.headers['Content-Type']) config.headers['Content-Type'] = 'application/json;charset=utf-8';
61
+ if (!config.headers['Accept']) config.headers['Accept'] = '*/*';
62
+ if (!config.params) {
63
+ config.params = {};
64
+ }
65
+ if (!(config.url.concat('_langcode') || config.params['_langcode'])) {
66
+ config.params['_langcode'] = 'zh';
67
+ }
68
+ var url = `${config.method} ${globalConfig.baseUrl}${config.url}`;
69
+ if (config.params && Object.keys(config.params).length > 0) {
70
+ if (url.indexOf('?') > -1) {
71
+ url += `&`;
72
+ } else {
73
+ url += `?`;
74
+ }
75
+ url += `${Object.entries(config.params).map(([key, value]) => `${key}=${value}`).join('&')}`;
76
+ }
77
+ let httpContent = `### ${url}\n`;
78
+ httpContent += `${url}\n`;
79
+ // 添加 headers(过滤掉一些自动添加的头)
80
+ const skipHeaders = ['user-agent', 'content-length', 'host', 'accept-encoding', 'connection'];
81
+ Object.entries(config.headers || {}).forEach(([key, value]) => {
82
+ const lowerKey = key.toLowerCase();
83
+ if (!skipHeaders.includes(lowerKey)) {
84
+ httpContent += `${key}: ${value}\n`;
85
+ }
86
+ });
87
+ if (config.data) {
88
+ httpContent += `### Request Data\n${limitLogOutput(config.data).data}\n`;
89
+ }
90
+ logger.info(httpContent);
91
+ logger.info('[Request] 请求准备完成,发送请求...');
92
+ return config;
93
+ }, error => {
94
+ logger.debug(error);
95
+ Promise.reject(error);
96
+ });
97
+
98
+ // 响应拦截器
99
+ service.interceptors.response.use(res => {
100
+ logger.info('[Response] 收到响应,状态码:', res.status);
101
+ logger.info('[Response] 响应数据:', res.data);
102
+
103
+ // 未设置状态码则默认成功状态
104
+ const code = res.data?.code || res.status || 200;
105
+ if (code >= 200 && code < 300) {
106
+ return res.data;
107
+ }
108
+
109
+ // 处理各种错误状态码
110
+ logger.error('[Response] 请求失败,状态码:', code);
111
+ if (code === 401) {
112
+ logger.error("无效的token");
113
+ return Promise.reject(new Error('无效的token'));
114
+ } else if (code === 500) {
115
+ logger.error("服务器内部错误");
116
+ return Promise.reject(new Error(res.data?.message || '服务器内部错误'));
117
+ } else if (code > 200) {
118
+ return Promise.reject(new Error(res.data?.message || `请求失败,状态码: ${code}`));
119
+ } else {
120
+ return Promise.reject(new Error('未知错误'));
121
+ }
122
+ }, error => {
123
+ logger.error('[Response Error] 请求错误:', error.message);
124
+ logger.debug('[Response Error] 错误对象:', error);
125
+
126
+ let { message } = error;
127
+ if (message == "Network Error") {
128
+ message = "后端接口连接异常";
129
+ }
130
+ else if (message.includes("timeout")) {
131
+ message = "系统接口请求超时";
132
+ }
133
+ else if (message.includes("Request failed with status code")) {
134
+ let code = message.substr(message.length - 3);
135
+ if (code == '502') {
136
+ message = "系统正在维护,请稍后再试";
137
+ } else {
138
+ message = "系统接口" + code + "异常";
139
+ }
140
+ }
141
+ logger.error('[Response Error] 错误信息:', message);
142
+ return Promise.reject(error);
143
+ });
144
+
145
+ return service;
146
+ }
147
+
148
+ // 延迟创建 service,确保配置已加载
149
+ let service = null;
150
+ function getService() {
151
+ if (!service) {
152
+ service = createService();
153
+ }
154
+ return service;
155
+ }
156
+
157
+ // 默认导出函数
158
+ const requestFn = (config) => getService()(config);
159
+ export default requestFn;
@@ -0,0 +1,23 @@
1
+ {
2
+ "logLevel": "INFO",
3
+ "envs": {
4
+ "local": {
5
+ "baseUrl": "http://127.0.0.1:9080/maximo",
6
+ "apiKey": "your-local-api-key",
7
+ "maxauth": "",
8
+ "authType": "apiKey"
9
+ },
10
+ "dev": {
11
+ "baseUrl": "https://mdev/maximo",
12
+ "apiKey": "your-dev-api-key",
13
+ "maxauth": "",
14
+ "authType": "apiKey"
15
+ },
16
+ "hd": {
17
+ "baseUrl": "http://xxxxx:9080/maximo",
18
+ "apiKey": "your-hd-api-key",
19
+ "maxauth": "",
20
+ "authType": "apiKey"
21
+ }
22
+ }
23
+ }
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "sks-maximo-utils",
3
+ "version": "1.0.0",
4
+ "description": "Maximo helper tools for updating table structures and data",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "sks-maximo": "cli.js"
9
+ },
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "keywords": ["maximo", "sks"],
14
+ "author": "shoukaiseki",
15
+ "license": "ISC",
16
+ "dependencies": {
17
+ "axios": "^1.6.0"
18
+ }
19
+ }