@xbongbong/xbbcli 2.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 +436 -0
- package/ai-call-add-task.js +106 -0
- package/ai-word-list.js +81 -0
- package/bin/xbbcli.js +120 -0
- package/buyer-inquiry-detail.js +112 -0
- package/buyer-inquiry-list.js +142 -0
- package/clue-add-couser.js +86 -0
- package/clue-add.js +102 -0
- package/clue-back.js +82 -0
- package/clue-del.js +75 -0
- package/clue-delete-couser.js +84 -0
- package/clue-delete-mainuser.js +84 -0
- package/clue-detail.js +78 -0
- package/clue-distribution.js +91 -0
- package/clue-edit.js +101 -0
- package/clue-handover.js +103 -0
- package/clue-list.js +224 -0
- package/clue-thorough-delete.js +82 -0
- package/communicate-add.js +144 -0
- package/communicate-del.js +118 -0
- package/communicate-detail.js +123 -0
- package/communicate-edit.js +146 -0
- package/communicate-list.js +194 -0
- package/communicate-plan-add.js +102 -0
- package/communicate-plan-cancel.js +76 -0
- package/communicate-plan-del.js +76 -0
- package/communicate-plan-detail.js +79 -0
- package/communicate-plan-list.js +129 -0
- package/communicate-plan-single-operate.js +86 -0
- package/contact-add-couser.js +87 -0
- package/contact-add-mainuser.js +87 -0
- package/contact-add.js +125 -0
- package/contact-del.js +70 -0
- package/contact-delete-couser.js +87 -0
- package/contact-delete-mainuser.js +87 -0
- package/contact-detail.js +72 -0
- package/contact-edit.js +103 -0
- package/contact-handover.js +84 -0
- package/contact-list.js +195 -0
- package/contract-add-couser.js +129 -0
- package/contract-add-mainuser.js +129 -0
- package/contract-add.js +148 -0
- package/contract-del.js +117 -0
- package/contract-delete-couser.js +128 -0
- package/contract-delete-mainuser.js +129 -0
- package/contract-detail.js +124 -0
- package/contract-edit.js +147 -0
- package/contract-handover.js +129 -0
- package/contract-list.js +235 -0
- package/contract-outstock-add.js +104 -0
- package/contract-outstock-del.js +78 -0
- package/contract-outstock-detail.js +80 -0
- package/contract-outstock-edit.js +106 -0
- package/contract-outstock-list.js +121 -0
- package/customer-add-couser.js +186 -0
- package/customer-add.js +152 -0
- package/customer-back.js +125 -0
- package/customer-del.js +117 -0
- package/customer-delete-couser.js +128 -0
- package/customer-delete-mainuser.js +129 -0
- package/customer-detail.js +148 -0
- package/customer-distribution.js +132 -0
- package/customer-edit.js +174 -0
- package/customer-handover.js +131 -0
- package/customer-invoice-address-add.js +101 -0
- package/customer-invoice-address-edit.js +102 -0
- package/customer-invoice-address.js +86 -0
- package/customer-invoice-info-add.js +101 -0
- package/customer-invoice-info-edit.js +102 -0
- package/customer-invoice-info.js +88 -0
- package/customer-list.js +187 -0
- package/department-add.js +77 -0
- package/department-del.js +76 -0
- package/department-edit.js +77 -0
- package/department-list.js +190 -0
- package/form-data-add-couser.js +84 -0
- package/form-data-add-mainuser.js +84 -0
- package/form-data-add.js +171 -0
- package/form-data-del.js +135 -0
- package/form-data-delete-couser.js +85 -0
- package/form-data-delete-mainuser.js +84 -0
- package/form-data-detail.js +146 -0
- package/form-data-edit.js +171 -0
- package/form-data-handover.js +84 -0
- package/form-data-list.js +188 -0
- package/form-get.js +157 -0
- package/form-list.js +169 -0
- package/invoice-add.js +98 -0
- package/invoice-cancel.js +97 -0
- package/invoice-del.js +83 -0
- package/invoice-detail.js +85 -0
- package/invoice-edit.js +101 -0
- package/invoice-get-amount-detail.js +84 -0
- package/invoice-list.js +125 -0
- package/invoice-red.js +101 -0
- package/label-add.js +93 -0
- package/label-all-list.js +102 -0
- package/label-batch-add.js +111 -0
- package/label-batch-remove.js +111 -0
- package/label-del-completely.js +88 -0
- package/label-del.js +98 -0
- package/label-edit.js +96 -0
- package/label-group-add.js +89 -0
- package/label-group-del.js +83 -0
- package/label-group-list.js +87 -0
- package/label-group-update.js +92 -0
- package/label-list.js +97 -0
- package/label-reduction.js +92 -0
- package/label-repeat.js +89 -0
- package/market-activity-add.js +109 -0
- package/market-activity-del.js +83 -0
- package/market-activity-detail.js +84 -0
- package/market-activity-edit.js +106 -0
- package/market-activity-handover.js +88 -0
- package/market-activity-list.js +121 -0
- package/opportunity-add-couser.js +129 -0
- package/opportunity-add-mainuser.js +129 -0
- package/opportunity-add.js +147 -0
- package/opportunity-del.js +118 -0
- package/opportunity-delete-couser.js +129 -0
- package/opportunity-delete-mainuser.js +128 -0
- package/opportunity-detail.js +125 -0
- package/opportunity-edit.js +146 -0
- package/opportunity-handover.js +130 -0
- package/opportunity-list.js +169 -0
- package/package.json +34 -0
- package/pay-plan-add-couser.js +88 -0
- package/pay-plan-add-mainuser.js +88 -0
- package/pay-plan-add.js +104 -0
- package/pay-plan-delete-couser.js +87 -0
- package/pay-plan-delete-mainuser.js +87 -0
- package/pay-plan-handover.js +85 -0
- package/pay-sheet-add-bad.js +104 -0
- package/pay-sheet-add-couser.js +87 -0
- package/pay-sheet-add-mainuser.js +88 -0
- package/pay-sheet-add-red.js +104 -0
- package/pay-sheet-add-write-off.js +104 -0
- package/pay-sheet-del.js +86 -0
- package/pay-sheet-delete-couser.js +87 -0
- package/pay-sheet-delete-mainuser.js +88 -0
- package/pay-sheet-detail.js +85 -0
- package/pay-sheet-edit-bad.js +110 -0
- package/pay-sheet-edit-red.js +110 -0
- package/pay-sheet-edit-write-off.js +106 -0
- package/pay-sheet-get-amount-detail.js +83 -0
- package/pay-sheet-list.js +123 -0
- package/payment-add-couser.js +129 -0
- package/payment-add-mainuser.js +129 -0
- package/payment-add.js +142 -0
- package/payment-del.js +117 -0
- package/payment-delete-couser.js +128 -0
- package/payment-delete-mainuser.js +128 -0
- package/payment-detail.js +123 -0
- package/payment-edit.js +146 -0
- package/payment-handover.js +130 -0
- package/payment-list.js +195 -0
- package/payment-sheet-add-bad-debt.js +142 -0
- package/payment-sheet-add-pre.js +104 -0
- package/payment-sheet-add-red.js +142 -0
- package/payment-sheet-add-write-off.js +130 -0
- package/payment-sheet-del.js +106 -0
- package/payment-sheet-detail.js +113 -0
- package/payment-sheet-edit-bad-debt.js +131 -0
- package/payment-sheet-edit-red.js +146 -0
- package/payment-sheet-edit-write-off.js +146 -0
- package/payment-sheet-get-amount-detail.js +84 -0
- package/payment-sheet-handover.js +85 -0
- package/payment-sheet-list.js +206 -0
- package/product-add.js +129 -0
- package/product-category-add.js +124 -0
- package/product-category-del.js +132 -0
- package/product-category-list.js +164 -0
- package/product-category-update.js +139 -0
- package/product-del.js +106 -0
- package/product-detail.js +183 -0
- package/product-edit.js +131 -0
- package/product-list.js +193 -0
- package/product-online.js +91 -0
- package/quotation-add-couser.js +106 -0
- package/quotation-add-mainuser.js +101 -0
- package/quotation-add.js +100 -0
- package/quotation-del.js +83 -0
- package/quotation-delete-couser.js +101 -0
- package/quotation-delete-mainuser.js +101 -0
- package/quotation-detail.js +85 -0
- package/quotation-edit.js +101 -0
- package/quotation-handover.js +84 -0
- package/quotation-list.js +123 -0
- package/refund-add.js +83 -0
- package/refund-del.js +68 -0
- package/refund-detail.js +71 -0
- package/refund-edit.js +84 -0
- package/refund-list.js +193 -0
- package/role-list.js +85 -0
- package/sign-in-list.js +136 -0
- package/stage-get.js +100 -0
- package/stage-jump.js +109 -0
- package/supplier-add.js +142 -0
- package/token-generate.js +109 -0
- package/token-set.js +222 -0
- package/user-add.js +103 -0
- package/user-del.js +90 -0
- package/user-edit.js +102 -0
- package/user-handover.js +78 -0
- package/user-list.js +147 -0
- package/version.js +18 -0
- package/work-order-add.js +171 -0
- package/work-order-asset-add-couser.js +101 -0
- package/work-order-asset-add-mainuser.js +104 -0
- package/work-order-asset-add.js +98 -0
- package/work-order-asset-del-couser.js +101 -0
- package/work-order-asset-del-mainuser.js +101 -0
- package/work-order-asset-del.js +83 -0
- package/work-order-asset-detail.js +85 -0
- package/work-order-asset-edit.js +101 -0
- package/work-order-asset-handover.js +101 -0
- package/work-order-asset-list.js +123 -0
- package/work-order-del.js +136 -0
- package/work-order-detail.js +186 -0
- package/work-order-dynamic-list.js +145 -0
- package/work-order-edit.js +170 -0
- package/work-order-list.js +243 -0
- package/work-order-operate.js +167 -0
- package/work-order-template-detail.js +157 -0
- package/work-order-template-list.js +145 -0
- package/work-report-daily-add.js +97 -0
- package/work-report-daily-del.js +75 -0
- package/work-report-daily-detail.js +78 -0
- package/work-report-daily-edit.js +129 -0
- package/work-report-daily-get-work-plan.js +80 -0
- package/work-report-daily-list.js +116 -0
- package/work-report-monthly-add.js +98 -0
- package/work-report-monthly-del.js +79 -0
- package/work-report-monthly-detail.js +78 -0
- package/work-report-monthly-edit.js +98 -0
- package/work-report-monthly-list.js +116 -0
- package/work-report-weekly-add.js +98 -0
- package/work-report-weekly-del.js +76 -0
- package/work-report-weekly-detail.js +79 -0
- package/work-report-weekly-edit.js +99 -0
- package/work-report-weekly-list.js +116 -0
- package/work-task-add-couser.js +88 -0
- package/work-task-add.js +242 -0
- package/work-task-delete-couser.js +85 -0
- package/work-task-detail.js +101 -0
- package/work-task-edit.js +116 -0
- package/work-task-handover.js +85 -0
- package/workorder-add-couser.js +128 -0
- package/workorder-delete-couser.js +129 -0
- package/workorder-handover.js +137 -0
- package/workorderlist.js +194 -0
- package/workorderproductlist.js +159 -0
- package/worktimerecorddetail.js +157 -0
- package/worktimerecordlist.js +199 -0
- package/xbb-registry.js +21 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { cli, Strategy } from './xbb-registry.js';
|
|
6
|
+
|
|
7
|
+
const CONFIG_FILE = path.join(os.homedir(), '.xbbcli', 'config.env');
|
|
8
|
+
const API_URL = 'https://proapi.xbongbong.com/pro/v2/api/user/generateToken';
|
|
9
|
+
const DEFAULT_BASE_URL = 'https://proapi.xbongbong.com';
|
|
10
|
+
const MISSING_TOKEN_MESSAGE = '缺少 token;请先执行 xbbcli token-set --corpid <CORPID> --token <TOKEN> --userId <USERID>';
|
|
11
|
+
|
|
12
|
+
function readConfig() {
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
15
|
+
} catch {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function resolveBaseUrl(corpid) {
|
|
21
|
+
if (corpid.startsWith('ding') || corpid.includes('$$ding')) {
|
|
22
|
+
return 'https://proapi.xbongbong.com';
|
|
23
|
+
}
|
|
24
|
+
return 'https://appapi.xbongbong.com';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getRuntimeConfig(kwargs) {
|
|
28
|
+
const suppliedCorpid = String(kwargs.corpid ?? '').trim();
|
|
29
|
+
const suppliedToken = String(kwargs.token ?? '').trim();
|
|
30
|
+
if (suppliedCorpid && suppliedToken) {
|
|
31
|
+
return {
|
|
32
|
+
corpid: suppliedCorpid,
|
|
33
|
+
token: suppliedToken,
|
|
34
|
+
baseUrl: resolveBaseUrl(suppliedCorpid),
|
|
35
|
+
userId: '',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const config = readConfig();
|
|
40
|
+
return {
|
|
41
|
+
corpid: String(config.corpid || '').trim(),
|
|
42
|
+
token: String(config.token || '').trim(),
|
|
43
|
+
baseUrl: String(config.baseurl || DEFAULT_BASE_URL).trim(),
|
|
44
|
+
userId: String(config.userId || '').trim(),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function buildApiUrl(baseUrl, apiUrl) {
|
|
49
|
+
const apiPath = new URL(apiUrl).pathname;
|
|
50
|
+
return `${baseUrl.replace(/\/+$/, '')}${apiPath}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function makeErrorRow(code, msg, baseUrl = '', corpid = '') {
|
|
54
|
+
return [{ corpid, token: '', checkUserId: '', resetToken: '', baseUrl, code, msg }];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
cli({
|
|
58
|
+
site: 'xbb',
|
|
59
|
+
name: 'token-generate',
|
|
60
|
+
description: '生成/获取个人token接口',
|
|
61
|
+
strategy: Strategy.PUBLIC,
|
|
62
|
+
access: 'write',
|
|
63
|
+
browser: false,
|
|
64
|
+
domain: 'proapi.xbongbong.com',
|
|
65
|
+
args: [
|
|
66
|
+
{ name: 'corpid', type: 'str', default: '', help: '企业 ID;需与 --token 同时提供,提供后不读取本地配置' },
|
|
67
|
+
{ name: 'token', type: 'str', default: '', help: 'API token;需与 --corpid 同时提供,提供后不读取本地配置' },
|
|
68
|
+
{ name: 'checkUserId', type: 'str', default: '', help: '生成/获取个人token对应的人员id(必填)' },
|
|
69
|
+
{ name: 'resetToken', type: 'str', default: '0', help: '是否重置token:0不刷新(默认),1刷新token' },
|
|
70
|
+
{ name: 'debug', type: 'bool', default: false, help: '输出请求体和返回体调试信息' },
|
|
71
|
+
{ name: 'raw', type: 'bool', default: false, help: '输出接口返回的原文' },
|
|
72
|
+
],
|
|
73
|
+
columns: ['corpid', 'token', 'checkUserId', 'resetToken', 'baseUrl', 'code', 'msg'],
|
|
74
|
+
func: async function (kwargs) {
|
|
75
|
+
const debug = Boolean(kwargs.debug);
|
|
76
|
+
const { corpid, token, baseUrl, userId } = getRuntimeConfig(kwargs);
|
|
77
|
+
const checkUserId = (kwargs.checkUserId ? String(kwargs.checkUserId) : userId).trim();
|
|
78
|
+
const resetTokenValue = String(kwargs.resetToken ?? '').trim();
|
|
79
|
+
const resetToken = resetTokenValue !== '' ? Number(resetTokenValue) : 0;
|
|
80
|
+
if (resetToken !== 0 && resetToken !== 1) {
|
|
81
|
+
return makeErrorRow('INVALID_RESETTOKEN', '--resetToken 仅支持 0(获取)或 1(重置)', baseUrl, corpid);
|
|
82
|
+
}
|
|
83
|
+
const payload = { corpid, resetToken, checkUserId };
|
|
84
|
+
const requestBody = JSON.stringify(payload);
|
|
85
|
+
if (!payload.corpid) return makeErrorRow('NO_CORPID', '缺少本地 corpid;请先执行 xbbcli token-set --corpid <CORPID> --token <TOKEN> --userId <USERID>', baseUrl, payload.corpid);
|
|
86
|
+
if (!payload.checkUserId) return makeErrorRow('NO_CHECKUSERID', '缺少 --checkUserId;未填则使用配置中的userId', baseUrl, payload.corpid);
|
|
87
|
+
if (!token) return makeErrorRow('NO_TOKEN', MISSING_TOKEN_MESSAGE, baseUrl, payload.corpid);
|
|
88
|
+
const sign = crypto.createHash('sha256').update(requestBody + token).digest('hex');
|
|
89
|
+
const headers = Object.assign({ 'Content-Type': 'application/json;charset=UTF-8', sign }, userId ? { userId } : {});
|
|
90
|
+
if (debug) {
|
|
91
|
+
process.stderr.write(`[debug] URL: ${buildApiUrl(baseUrl, API_URL)}\n[debug] Headers: ${JSON.stringify(headers)}\n[debug] RequestBody: ${requestBody}\n`);
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const resp = await fetch(buildApiUrl(baseUrl, API_URL), { method: 'POST', headers, body: requestBody });
|
|
95
|
+
if (!resp.ok) return makeErrorRow(resp.status, `HTTP ${resp.status} ${resp.statusText}`, baseUrl, payload.corpid);
|
|
96
|
+
const data = await resp.json();
|
|
97
|
+
const responseBody = JSON.stringify(data);
|
|
98
|
+
if (debug) process.stderr.write(`[debug] ResponseBody: ${responseBody}\n`);
|
|
99
|
+
if (kwargs.raw) return [{ raw: responseBody }];
|
|
100
|
+
if (data.code !== 1) return makeErrorRow(data.code ?? '', data.msg ?? '未知错误', baseUrl, payload.corpid);
|
|
101
|
+
const result = data.result;
|
|
102
|
+
const tokenValue = typeof result === 'string' ? result : (result && typeof result === 'object' ? (result.personalToken ?? result.token ?? JSON.stringify(result)) : '');
|
|
103
|
+
return [{ corpid: payload.corpid, token: tokenValue || '', checkUserId: payload.checkUserId, resetToken: String(resetToken), baseUrl: debug ? baseUrl : '', code: data.code ?? '', msg: data.msg || '' }];
|
|
104
|
+
} catch (error) {
|
|
105
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
106
|
+
return makeErrorRow('REQUEST_FAILED', message || '请求失败', baseUrl, payload.corpid);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
});
|
package/token-set.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { cli, Strategy, getRegistry } from './xbb-registry.js';
|
|
5
|
+
|
|
6
|
+
const CONFIG_DIR = path.join(os.homedir(), '.xbbcli');
|
|
7
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.env');
|
|
8
|
+
const FORMLIST_FILE_SUFFIX = '.formlist.json';
|
|
9
|
+
|
|
10
|
+
function resolveBaseUrl(corpid) {
|
|
11
|
+
if (corpid.startsWith('ding') || corpid.includes('$$ding')) {
|
|
12
|
+
return 'https://proapi.xbongbong.com';
|
|
13
|
+
}
|
|
14
|
+
return 'https://appapi.xbongbong.com';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalizeArg(value) {
|
|
18
|
+
return String(value || '').trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getFormlistFile(corpid) {
|
|
22
|
+
return path.join(CONFIG_DIR, `${corpid}${FORMLIST_FILE_SUFFIX}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function runCommandJson(name, kwargs) {
|
|
26
|
+
const command = getRegistry().get(`xbb/${name}`);
|
|
27
|
+
if (!command) throw new Error(`找不到命令:${name}`);
|
|
28
|
+
const rows = await command.func(kwargs);
|
|
29
|
+
if (!Array.isArray(rows)) throw new Error(`${name} 返回结果不是数组`);
|
|
30
|
+
return rows;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function getFormlistRows(corpid, saasMark) {
|
|
34
|
+
const rows = await runCommandJson('form-list', { saasMark: String(saasMark) });
|
|
35
|
+
|
|
36
|
+
if (!Array.isArray(rows)) {
|
|
37
|
+
throw new Error(`form-list 返回结果不是数组:saasMark=${saasMark}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const errorRow = rows.find((item) => item && item.code);
|
|
41
|
+
if (errorRow) {
|
|
42
|
+
throw new Error(`form-list 获取失败(saasMark=${saasMark}):${errorRow.code} ${errorRow.msg || ''}`.trim());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return rows;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function getPersonalToken(corpid, token, userId) {
|
|
49
|
+
const rows = await runCommandJson('token-generate', {
|
|
50
|
+
checkUserId: userId,
|
|
51
|
+
resetToken: '1',
|
|
52
|
+
token,
|
|
53
|
+
corpid,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (!Array.isArray(rows)) {
|
|
57
|
+
throw new Error('token-generate 返回结果不是数组');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const errorRow = rows.find((item) => item && String(item.code ?? '') !== '' && Number(item.code) !== 1);
|
|
61
|
+
if (errorRow) {
|
|
62
|
+
throw new Error(`token-generate 获取个人 token 失败:${errorRow.code} ${errorRow.msg || ''}`.trim());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const personalToken = String(rows[0]?.token || '').trim();
|
|
66
|
+
if (!personalToken.startsWith('user_')) {
|
|
67
|
+
throw new Error('token-generate 未返回有效的个人 token');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return personalToken;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function writeFormlistFile(corpid) {
|
|
74
|
+
const customForms = await getFormlistRows(corpid, 2);
|
|
75
|
+
const systemForms = await getFormlistRows(corpid, 1);
|
|
76
|
+
const formlistFile = getFormlistFile(corpid);
|
|
77
|
+
const mergedRows = [...customForms, ...systemForms];
|
|
78
|
+
|
|
79
|
+
fs.writeFileSync(formlistFile, JSON.stringify(mergedRows, null, 2) + '\n', 'utf8');
|
|
80
|
+
return formlistFile;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function normalizeBusinessType(value) {
|
|
84
|
+
return String(value).trim().replace(/[))\]】,,;;::。!?!?]+$/g, '');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getCommandBusinessType(command) {
|
|
88
|
+
if (command.businessType !== undefined && command.businessType !== null) {
|
|
89
|
+
return normalizeBusinessType(command.businessType);
|
|
90
|
+
}
|
|
91
|
+
const description = String(command.description || '');
|
|
92
|
+
const match = description.match(/businessType\s*:\s*(\d+)/i);
|
|
93
|
+
return match ? normalizeBusinessType(match[1]) : '';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function escapeCell(value) {
|
|
97
|
+
return String(value ?? '').replace(/\|/g, '\\|').replace(/\r?\n/g, ' ');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function optionDescription(option) {
|
|
101
|
+
return `--${option.name}: ${option.help || ''}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function writeCommandMap(corpid) {
|
|
105
|
+
const formFile = getFormlistFile(corpid);
|
|
106
|
+
if (!fs.existsSync(formFile)) {
|
|
107
|
+
throw new Error(`Form cache not found: ${formFile}`);
|
|
108
|
+
}
|
|
109
|
+
const raw = fs.readFileSync(formFile, 'utf8').replace(/^\uFEFF/, '');
|
|
110
|
+
const data = JSON.parse(raw);
|
|
111
|
+
const forms = Array.isArray(data) ? data : data.data || [];
|
|
112
|
+
|
|
113
|
+
const commands = [...getRegistry().values()]
|
|
114
|
+
.filter((command) => command.site === 'xbb')
|
|
115
|
+
.map((command) => ({
|
|
116
|
+
...command,
|
|
117
|
+
command_options: command.args || [],
|
|
118
|
+
}));
|
|
119
|
+
|
|
120
|
+
const lines = [
|
|
121
|
+
'# XBB 命令映射',
|
|
122
|
+
'',
|
|
123
|
+
`来源:xbb --help;表单缓存:${path.basename(formFile)}`,
|
|
124
|
+
'',
|
|
125
|
+
'| 命令 | 命令名称 | formId | businessType | 字段说明 |',
|
|
126
|
+
'| --- | --- | --- | --- | --- |',
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
for (const command of commands) {
|
|
130
|
+
const commandBusinessType = getCommandBusinessType(command);
|
|
131
|
+
const matchedForms = forms.filter((form) => String(form.businessType ?? '').trim() === commandBusinessType);
|
|
132
|
+
const formIds = [...new Set(matchedForms.map((form) => form.formId).filter(Boolean))];
|
|
133
|
+
const businessTypes = [...new Set(matchedForms.map((form) => form.businessType).filter(Boolean))];
|
|
134
|
+
const options = (command.command_options || []).map(optionDescription).join('<br>');
|
|
135
|
+
lines.push(
|
|
136
|
+
`| ${escapeCell(command.name)} | ${escapeCell(command.description)} | ${escapeCell(formIds.join(', '))} | ${escapeCell(businessTypes.join(', ') || commandBusinessType)} | ${escapeCell(options)} |`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const commandMapFile = path.join(CONFIG_DIR, 'command-map.md');
|
|
141
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
142
|
+
fs.writeFileSync(commandMapFile, `${lines.join('\n')}\n`, 'utf8');
|
|
143
|
+
return commandMapFile;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function createResult(status, message, corpid, baseurl, userId, formlistFile = '', commandMapFile = '') {
|
|
147
|
+
return [{
|
|
148
|
+
status,
|
|
149
|
+
message,
|
|
150
|
+
configFile: CONFIG_FILE,
|
|
151
|
+
corpid,
|
|
152
|
+
baseurl,
|
|
153
|
+
userId,
|
|
154
|
+
formlistFile,
|
|
155
|
+
commandMapFile,
|
|
156
|
+
}];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function setToken(kwargs) {
|
|
160
|
+
const corpid = normalizeArg(kwargs.corpid);
|
|
161
|
+
const token = normalizeArg(kwargs.token);
|
|
162
|
+
const userId = normalizeArg(kwargs.userId);
|
|
163
|
+
|
|
164
|
+
if (!corpid) {
|
|
165
|
+
return createResult('error', '缺少 --corpid', '', '', '');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!token) {
|
|
169
|
+
return createResult('error', '缺少 --token', corpid, '', '');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!userId) {
|
|
173
|
+
return createResult('error', '缺少 --userId', corpid, '', '');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let personalToken = token;
|
|
177
|
+
if (!token.startsWith('user_')) {
|
|
178
|
+
try {
|
|
179
|
+
personalToken = await getPersonalToken(corpid, token, userId);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
return createResult('error', `生成个人 token 失败:${error.message}`, corpid, '', userId);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const baseurl = resolveBaseUrl(corpid);
|
|
186
|
+
const config = { corpid, token: personalToken, baseurl, userId };
|
|
187
|
+
|
|
188
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
189
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const formlistFile = await writeFormlistFile(corpid);
|
|
193
|
+
const commandMapFile = writeCommandMap(corpid);
|
|
194
|
+
return createResult('ok', '已保存 corpid、token、baseurl、userId,并同步表单模板缓存与命令映射文件', corpid, baseurl, userId, formlistFile, commandMapFile);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
return createResult(
|
|
197
|
+
'partial',
|
|
198
|
+
`已保存 corpid、token、baseurl、userId,但同步缓存失败:${error.message}`,
|
|
199
|
+
corpid,
|
|
200
|
+
baseurl,
|
|
201
|
+
userId,
|
|
202
|
+
getFormlistFile(corpid),
|
|
203
|
+
'',
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
cli({
|
|
209
|
+
site: 'xbb',
|
|
210
|
+
name: 'token-set',
|
|
211
|
+
description: '保存 xbb API token,corpid,formId清单 到本地配置文件,其他命令需要意图识别或找formId时,优先查命令映射文件(command-map.md)',
|
|
212
|
+
strategy: Strategy.PUBLIC,
|
|
213
|
+
access: 'write',
|
|
214
|
+
browser: false,
|
|
215
|
+
args: [
|
|
216
|
+
{ name: 'corpid', type: 'str', help: '公司id(必填)' },
|
|
217
|
+
{ name: 'token', type: 'str', help: '要保存的 API token' },
|
|
218
|
+
{ name: 'userId', type: 'str', help: '操作人id(必填)' },
|
|
219
|
+
],
|
|
220
|
+
columns: ['status', 'message', 'configFile', 'corpid', 'baseurl', 'userId', 'formlistFile', 'commandMapFile'],
|
|
221
|
+
func: setToken,
|
|
222
|
+
});
|
package/user-add.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { cli, Strategy } from './xbb-registry.js';
|
|
6
|
+
|
|
7
|
+
const CONFIG_FILE = path.join(os.homedir(), '.xbbcli', 'config.env');
|
|
8
|
+
const API_URL = 'https://proapi.xbongbong.com/pro/v2/api/user/add';
|
|
9
|
+
const DEFAULT_BASE_URL = 'https://proapi.xbongbong.com';
|
|
10
|
+
const MISSING_TOKEN_MESSAGE = '缺少 token;请先执行 xbbcli token-set --corpid <CORPID> --token <TOKEN> --userId <USERID>';
|
|
11
|
+
|
|
12
|
+
function readConfig() {
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
15
|
+
} catch {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getRuntimeConfig() {
|
|
21
|
+
const config = readConfig();
|
|
22
|
+
return {
|
|
23
|
+
corpid: String(config.corpid || '').trim(),
|
|
24
|
+
token: String(config.token || '').trim(),
|
|
25
|
+
baseUrl: String(config.baseurl || DEFAULT_BASE_URL).trim(),
|
|
26
|
+
userId: String(config.userId || '').trim(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildApiUrl(baseUrl, apiUrl) {
|
|
31
|
+
const apiPath = new URL(apiUrl).pathname;
|
|
32
|
+
return `${baseUrl.replace(/\/+$/, '')}${apiPath}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseJsonArray(raw) {
|
|
36
|
+
if (Array.isArray(raw)) return raw;
|
|
37
|
+
const text = String(raw || '').trim();
|
|
38
|
+
if (!text) return null;
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(text);
|
|
41
|
+
return Array.isArray(parsed) ? parsed : undefined;
|
|
42
|
+
} catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function makeErrorRow(code, msg) {
|
|
48
|
+
return [{ code, msg }];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
cli({
|
|
52
|
+
site: 'xbb',
|
|
53
|
+
name: 'user-add',
|
|
54
|
+
description: '新建用户接口(businessType: -1)',
|
|
55
|
+
strategy: Strategy.PUBLIC,
|
|
56
|
+
access: 'write',
|
|
57
|
+
browser: false,
|
|
58
|
+
domain: 'proapi.xbongbong.com',
|
|
59
|
+
args: [
|
|
60
|
+
{ name: 'name', type: 'str', help: '用户名称(必填)' },
|
|
61
|
+
{ name: 'mobile', type: 'str', help: '手机号(必填)' },
|
|
62
|
+
{ name: 'roleIds', type: 'str', help: '角色id列表,JSON数组字符串(必填)' },
|
|
63
|
+
{ name: 'userDepInfoList', type: 'str', help: '部门信息列表,JSON数组字符串(必填)' },
|
|
64
|
+
{ name: 'avatar', type: 'str', default: '', help: '头像(可选)' },
|
|
65
|
+
{ name: 'position', type: 'str', default: '', help: '职位(可选)' },
|
|
66
|
+
{ name: 'email', type: 'str', default: '', help: '邮箱(可选)' },
|
|
67
|
+
{ name: 'userId', type: 'str', default: '', help: '操作人id(可选)' },
|
|
68
|
+
{ name: 'debug', type: 'bool', default: false, help: '输出请求体和返回体调试信息' },
|
|
69
|
+
],
|
|
70
|
+
columns: ['code', 'msg'],
|
|
71
|
+
func: async function (kwargs) {
|
|
72
|
+
const debug = Boolean(kwargs.debug);
|
|
73
|
+
const { corpid, token, baseUrl, userId } = getRuntimeConfig();
|
|
74
|
+
const roleIds = parseJsonArray(kwargs.roleIds);
|
|
75
|
+
const userDepInfoList = parseJsonArray(kwargs.userDepInfoList);
|
|
76
|
+
const payload = { name: String(kwargs.name || ''), mobile: String(kwargs.mobile || ''), corpid };
|
|
77
|
+
if (Array.isArray(roleIds)) payload.roleIds = roleIds;
|
|
78
|
+
if (Array.isArray(userDepInfoList)) payload.userDepInfoList = userDepInfoList;
|
|
79
|
+
if (String(kwargs.avatar || '').trim()) payload.avatar = String(kwargs.avatar);
|
|
80
|
+
if (String(kwargs.position || '').trim()) payload.position = String(kwargs.position);
|
|
81
|
+
if (String(kwargs.email || '').trim()) payload.email = String(kwargs.email);
|
|
82
|
+
if (kwargs.userId) payload.userId = String(kwargs.userId);
|
|
83
|
+
const requestBody = JSON.stringify(payload);
|
|
84
|
+
if (!payload.name) return makeErrorRow('NO_NAME', '缺少 --name');
|
|
85
|
+
if (!payload.mobile) return makeErrorRow('NO_MOBILE', '缺少 --mobile');
|
|
86
|
+
if (!payload.corpid) return makeErrorRow('NO_CORPID', '缺少本地 corpid;请先执行 xbbcli token-set --corpid <CORPID> --token <TOKEN> --userId <USERID>');
|
|
87
|
+
if (!Array.isArray(roleIds) || !roleIds.length) return makeErrorRow(roleIds === undefined ? 'INVALID_ROLEIDS' : 'NO_ROLEIDS', roleIds === undefined ? '--roleIds 必须是 JSON 数组字符串' : '缺少 --roleIds');
|
|
88
|
+
if (!Array.isArray(userDepInfoList) || !userDepInfoList.length) return makeErrorRow(userDepInfoList === undefined ? 'INVALID_USERDEPINFOLIST' : 'NO_USERDEPINFOLIST', userDepInfoList === undefined ? '--userDepInfoList 必须是 JSON 数组字符串' : '缺少 --userDepInfoList');
|
|
89
|
+
if (!token) return makeErrorRow('NO_TOKEN', MISSING_TOKEN_MESSAGE);
|
|
90
|
+
const sign = crypto.createHash('sha256').update(requestBody + token).digest('hex');
|
|
91
|
+
const headers = Object.assign({ 'Content-Type': 'application/json;charset=UTF-8', sign }, userId ? { userId } : {});
|
|
92
|
+
if (debug) {
|
|
93
|
+
process.stderr.write(`[debug] URL: ${buildApiUrl(baseUrl, API_URL)}\n[debug] Headers: ${JSON.stringify(headers)}\n[debug] RequestBody: ${requestBody}\n`);
|
|
94
|
+
}
|
|
95
|
+
const resp = await fetch(buildApiUrl(baseUrl, API_URL), { method: 'POST', headers, body: requestBody });
|
|
96
|
+
if (!resp.ok) return makeErrorRow(resp.status, `HTTP ${resp.status} ${resp.statusText}`);
|
|
97
|
+
const data = await resp.json();
|
|
98
|
+
const responseBody = JSON.stringify(data);
|
|
99
|
+
if (debug) process.stderr.write(`[debug] ResponseBody: ${responseBody}\n`);
|
|
100
|
+
if (data.code !== 1) return makeErrorRow(data.code ?? '', data.msg ?? '未知错误');
|
|
101
|
+
return [{ code: data.code ?? '', msg: data.msg || '' }];
|
|
102
|
+
},
|
|
103
|
+
});
|
package/user-del.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { cli, Strategy } from './xbb-registry.js';
|
|
6
|
+
|
|
7
|
+
const CONFIG_FILE = path.join(os.homedir(), '.xbbcli', 'config.env');
|
|
8
|
+
const API_URL = 'https://proapi.xbongbong.com/pro/v2/api/user/del';
|
|
9
|
+
const DEFAULT_BASE_URL = 'https://proapi.xbongbong.com';
|
|
10
|
+
const MISSING_TOKEN_MESSAGE = '缺少 token;请先执行 xbbcli token-set --corpid <CORPID> --token <TOKEN> --userId <USERID>';
|
|
11
|
+
|
|
12
|
+
function readConfig() {
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
15
|
+
} catch {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getRuntimeConfig() {
|
|
21
|
+
const config = readConfig();
|
|
22
|
+
return {
|
|
23
|
+
corpid: String(config.corpid || '').trim(),
|
|
24
|
+
token: String(config.token || '').trim(),
|
|
25
|
+
baseUrl: String(config.baseurl || DEFAULT_BASE_URL).trim(),
|
|
26
|
+
userId: String(config.userId || '').trim(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildApiUrl(baseUrl, apiUrl) {
|
|
31
|
+
const apiPath = new URL(apiUrl).pathname;
|
|
32
|
+
return `${baseUrl.replace(/\/+$/, '')}${apiPath}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseJsonArray(raw) {
|
|
36
|
+
if (Array.isArray(raw)) return raw;
|
|
37
|
+
const text = String(raw || '').trim();
|
|
38
|
+
if (!text) return null;
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(text);
|
|
41
|
+
return Array.isArray(parsed) ? parsed : undefined;
|
|
42
|
+
} catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function makeErrorRow(code, msg) {
|
|
48
|
+
return [{ code, msg }];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
cli({
|
|
52
|
+
site: 'xbb',
|
|
53
|
+
name: 'user-del',
|
|
54
|
+
description: '删除用户接口(businessType: -1)',
|
|
55
|
+
strategy: Strategy.PUBLIC,
|
|
56
|
+
access: 'write',
|
|
57
|
+
browser: false,
|
|
58
|
+
domain: 'proapi.xbongbong.com',
|
|
59
|
+
args: [
|
|
60
|
+
{ name: 'userIds', type: 'str', help: '用户id列表,JSON数组字符串(必填)' },
|
|
61
|
+
{ name: 'userId', type: 'str', default: '', help: '操作人id(可选)' },
|
|
62
|
+
{ name: 'debug', type: 'bool', default: false, help: '输出请求体和返回体调试信息' },
|
|
63
|
+
],
|
|
64
|
+
columns: ['code', 'msg'],
|
|
65
|
+
func: async function (kwargs) {
|
|
66
|
+
const debug = Boolean(kwargs.debug);
|
|
67
|
+
const { corpid, token, baseUrl, userId } = getRuntimeConfig();
|
|
68
|
+
const userIds = parseJsonArray(kwargs.userIds);
|
|
69
|
+
const payload = { corpid };
|
|
70
|
+
if (Array.isArray(userIds)) payload.userIds = userIds;
|
|
71
|
+
if (kwargs.userId) payload.userId = String(kwargs.userId);
|
|
72
|
+
const requestBody = JSON.stringify(payload);
|
|
73
|
+
if (!payload.corpid) return makeErrorRow('NO_CORPID', '缺少本地 corpid;请先执行 xbbcli token-set --corpid <CORPID> --token <TOKEN> --userId <USERID>');
|
|
74
|
+
if (!Array.isArray(userIds) || !userIds.length) return makeErrorRow(userIds === undefined ? 'INVALID_USERIDS' : 'NO_USERIDS', userIds === undefined ? '--userIds 必须是 JSON 数组字符串' : '缺少 --userIds');
|
|
75
|
+
if (!token) return makeErrorRow('NO_TOKEN', MISSING_TOKEN_MESSAGE);
|
|
76
|
+
const sign = crypto.createHash('sha256').update(requestBody + token).digest('hex');
|
|
77
|
+
const headers = Object.assign({ 'Content-Type': 'application/json;charset=UTF-8', sign }, userId ? { userId } : {});
|
|
78
|
+
const apiUrl = buildApiUrl(baseUrl, API_URL);
|
|
79
|
+
if (debug) {
|
|
80
|
+
process.stderr.write(`[debug] URL: ${apiUrl}\n[debug] Headers: ${JSON.stringify(headers)}\n[debug] RequestBody: ${requestBody}\n`);
|
|
81
|
+
}
|
|
82
|
+
const resp = await fetch(apiUrl, { method: 'POST', headers, body: requestBody });
|
|
83
|
+
if (!resp.ok) return makeErrorRow(resp.status, `HTTP ${resp.status} ${resp.statusText}`);
|
|
84
|
+
const data = await resp.json();
|
|
85
|
+
const responseBody = JSON.stringify(data);
|
|
86
|
+
if (debug) process.stderr.write(`[debug] ResponseBody: ${responseBody}\n`);
|
|
87
|
+
if (data.code !== 1) return makeErrorRow(data.code ?? '', data.msg ?? '未知错误');
|
|
88
|
+
return [{ code: data.code ?? '', msg: data.msg || '' }];
|
|
89
|
+
},
|
|
90
|
+
});
|
package/user-edit.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { cli, Strategy } from './xbb-registry.js';
|
|
6
|
+
|
|
7
|
+
const CONFIG_FILE = path.join(os.homedir(), '.xbbcli', 'config.env');
|
|
8
|
+
const API_URL = 'https://proapi.xbongbong.com/pro/v2/api/user/edit';
|
|
9
|
+
const DEFAULT_BASE_URL = 'https://proapi.xbongbong.com';
|
|
10
|
+
const MISSING_TOKEN_MESSAGE = '缺少 token;请先执行 xbbcli token-set --corpid <CORPID> --token <TOKEN> --userId <USERID>';
|
|
11
|
+
|
|
12
|
+
function readConfig() {
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
15
|
+
} catch {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getRuntimeConfig() {
|
|
21
|
+
const config = readConfig();
|
|
22
|
+
return {
|
|
23
|
+
corpid: String(config.corpid || '').trim(),
|
|
24
|
+
token: String(config.token || '').trim(),
|
|
25
|
+
baseUrl: String(config.baseurl || DEFAULT_BASE_URL).trim(),
|
|
26
|
+
userId: String(config.userId || '').trim(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildApiUrl(baseUrl, apiUrl) {
|
|
31
|
+
const apiPath = new URL(apiUrl).pathname;
|
|
32
|
+
return `${baseUrl.replace(/\/+$/, '')}${apiPath}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseJsonArray(raw) {
|
|
36
|
+
if (Array.isArray(raw)) return raw;
|
|
37
|
+
const text = String(raw || '').trim();
|
|
38
|
+
if (!text) return null;
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(text);
|
|
41
|
+
return Array.isArray(parsed) ? parsed : undefined;
|
|
42
|
+
} catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function makeErrorRow(code, msg) {
|
|
48
|
+
return [{ code, msg }];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
cli({
|
|
52
|
+
site: 'xbb',
|
|
53
|
+
name: 'user-edit',
|
|
54
|
+
description: '编辑用户接口(businessType: -1)',
|
|
55
|
+
strategy: Strategy.PUBLIC,
|
|
56
|
+
access: 'write',
|
|
57
|
+
browser: false,
|
|
58
|
+
domain: 'proapi.xbongbong.com',
|
|
59
|
+
args: [
|
|
60
|
+
{ name: 'id', type: 'int', help: '用户id(必填)' },
|
|
61
|
+
{ name: 'name', type: 'str', help: '用户名称(必填)' },
|
|
62
|
+
{ name: 'roleIds', type: 'str', help: '角色id列表,JSON数组字符串(必填)' },
|
|
63
|
+
{ name: 'userDepInfoList', type: 'str', help: '部门信息列表,JSON数组字符串(必填)' },
|
|
64
|
+
{ name: 'avatar', type: 'str', default: '', help: '头像(可选)' },
|
|
65
|
+
{ name: 'position', type: 'str', default: '', help: '职位(可选)' },
|
|
66
|
+
{ name: 'userId', type: 'str', default: '', help: '操作人id(可选)' },
|
|
67
|
+
{ name: 'debug', type: 'bool', default: false, help: '输出请求体和返回体调试信息' },
|
|
68
|
+
],
|
|
69
|
+
columns: ['code', 'msg'],
|
|
70
|
+
func: async function (kwargs) {
|
|
71
|
+
const debug = Boolean(kwargs.debug);
|
|
72
|
+
const { corpid, token, baseUrl, userId } = getRuntimeConfig();
|
|
73
|
+
const roleIds = parseJsonArray(kwargs.roleIds);
|
|
74
|
+
const userDepInfoList = parseJsonArray(kwargs.userDepInfoList);
|
|
75
|
+
const payload = { id: Number(kwargs.id || 0), name: String(kwargs.name || ''), corpid };
|
|
76
|
+
if (Array.isArray(roleIds)) payload.roleIds = roleIds;
|
|
77
|
+
if (Array.isArray(userDepInfoList)) payload.userDepInfoList = userDepInfoList;
|
|
78
|
+
if (String(kwargs.avatar || '').trim()) payload.avatar = String(kwargs.avatar);
|
|
79
|
+
if (String(kwargs.position || '').trim()) payload.position = String(kwargs.position);
|
|
80
|
+
if (kwargs.userId) payload.userId = String(kwargs.userId);
|
|
81
|
+
const requestBody = JSON.stringify(payload);
|
|
82
|
+
if (!payload.id) return makeErrorRow('NO_ID', '缺少 --id');
|
|
83
|
+
if (!payload.name) return makeErrorRow('NO_NAME', '缺少 --name');
|
|
84
|
+
if (!payload.corpid) return makeErrorRow('NO_CORPID', '缺少本地 corpid;请先执行 xbbcli token-set --corpid <CORPID> --token <TOKEN> --userId <USERID>');
|
|
85
|
+
if (!Array.isArray(roleIds) || !roleIds.length) return makeErrorRow(roleIds === undefined ? 'INVALID_ROLEIDS' : 'NO_ROLEIDS', roleIds === undefined ? '--roleIds 必须是 JSON 数组字符串' : '缺少 --roleIds');
|
|
86
|
+
if (!Array.isArray(userDepInfoList) || !userDepInfoList.length) return makeErrorRow(userDepInfoList === undefined ? 'INVALID_USERDEPINFOLIST' : 'NO_USERDEPINFOLIST', userDepInfoList === undefined ? '--userDepInfoList 必须是 JSON 数组字符串' : '缺少 --userDepInfoList');
|
|
87
|
+
if (!token) return makeErrorRow('NO_TOKEN', MISSING_TOKEN_MESSAGE);
|
|
88
|
+
const sign = crypto.createHash('sha256').update(requestBody + token).digest('hex');
|
|
89
|
+
const headers = Object.assign({ 'Content-Type': 'application/json;charset=UTF-8', sign }, userId ? { userId } : {});
|
|
90
|
+
const apiUrl = buildApiUrl(baseUrl, API_URL);
|
|
91
|
+
if (debug) {
|
|
92
|
+
process.stderr.write(`[debug] URL: ${apiUrl}\n[debug] Headers: ${JSON.stringify(headers)}\n[debug] RequestBody: ${requestBody}\n`);
|
|
93
|
+
}
|
|
94
|
+
const resp = await fetch(apiUrl, { method: 'POST', headers, body: requestBody });
|
|
95
|
+
if (!resp.ok) return makeErrorRow(resp.status, `HTTP ${resp.status} ${resp.statusText}`);
|
|
96
|
+
const data = await resp.json();
|
|
97
|
+
const responseBody = JSON.stringify(data);
|
|
98
|
+
if (debug) process.stderr.write(`[debug] ResponseBody: ${responseBody}\n`);
|
|
99
|
+
if (data.code !== 1) return makeErrorRow(data.code ?? '', data.msg ?? '未知错误');
|
|
100
|
+
return [{ code: data.code ?? '', msg: data.msg || '' }];
|
|
101
|
+
},
|
|
102
|
+
});
|