hikvision-cli 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.
Files changed (73) hide show
  1. package/dist/commands/binding.d.ts +6 -0
  2. package/dist/commands/binding.d.ts.map +1 -0
  3. package/dist/commands/binding.js +274 -0
  4. package/dist/commands/binding.js.map +1 -0
  5. package/dist/commands/card.d.ts +6 -0
  6. package/dist/commands/card.d.ts.map +1 -0
  7. package/dist/commands/card.js +259 -0
  8. package/dist/commands/card.js.map +1 -0
  9. package/dist/commands/group.d.ts +6 -0
  10. package/dist/commands/group.d.ts.map +1 -0
  11. package/dist/commands/group.js +87 -0
  12. package/dist/commands/group.js.map +1 -0
  13. package/dist/commands/org.d.ts +6 -0
  14. package/dist/commands/org.d.ts.map +1 -0
  15. package/dist/commands/org.js +248 -0
  16. package/dist/commands/org.js.map +1 -0
  17. package/dist/commands/person.d.ts +6 -0
  18. package/dist/commands/person.d.ts.map +1 -0
  19. package/dist/commands/person.js +563 -0
  20. package/dist/commands/person.js.map +1 -0
  21. package/dist/commands/service.d.ts +6 -0
  22. package/dist/commands/service.d.ts.map +1 -0
  23. package/dist/commands/service.js +153 -0
  24. package/dist/commands/service.js.map +1 -0
  25. package/dist/commands/sync.d.ts +6 -0
  26. package/dist/commands/sync.d.ts.map +1 -0
  27. package/dist/commands/sync.js +138 -0
  28. package/dist/commands/sync.js.map +1 -0
  29. package/dist/commands/system.d.ts +6 -0
  30. package/dist/commands/system.d.ts.map +1 -0
  31. package/dist/commands/system.js +167 -0
  32. package/dist/commands/system.js.map +1 -0
  33. package/dist/commands/vehicle.d.ts +12 -0
  34. package/dist/commands/vehicle.d.ts.map +1 -0
  35. package/dist/commands/vehicle.js +550 -0
  36. package/dist/commands/vehicle.js.map +1 -0
  37. package/dist/index.d.ts +6 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +94 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/services/hikvisionClient.d.ts +25 -0
  42. package/dist/services/hikvisionClient.d.ts.map +1 -0
  43. package/dist/services/hikvisionClient.js +59 -0
  44. package/dist/services/hikvisionClient.js.map +1 -0
  45. package/dist/utils/config.d.ts +66 -0
  46. package/dist/utils/config.d.ts.map +1 -0
  47. package/dist/utils/config.js +213 -0
  48. package/dist/utils/config.js.map +1 -0
  49. package/dist/utils/output.d.ts +42 -0
  50. package/dist/utils/output.d.ts.map +1 -0
  51. package/dist/utils/output.js +157 -0
  52. package/dist/utils/output.js.map +1 -0
  53. package/dist/utils/validation.d.ts +52 -0
  54. package/dist/utils/validation.d.ts.map +1 -0
  55. package/dist/utils/validation.js +171 -0
  56. package/dist/utils/validation.js.map +1 -0
  57. package/package.json +29 -0
  58. package/src/commands/binding.ts +305 -0
  59. package/src/commands/card.ts +238 -0
  60. package/src/commands/group.ts +91 -0
  61. package/src/commands/org.ts +228 -0
  62. package/src/commands/person.ts +596 -0
  63. package/src/commands/service.ts +174 -0
  64. package/src/commands/sync.ts +156 -0
  65. package/src/commands/system.ts +137 -0
  66. package/src/commands/vehicle.ts +572 -0
  67. package/src/index.ts +111 -0
  68. package/src/services/hikvisionClient.ts +74 -0
  69. package/src/types/cli-table3.d.ts +20 -0
  70. package/src/utils/config.ts +199 -0
  71. package/src/utils/output.ts +181 -0
  72. package/src/utils/validation.ts +160 -0
  73. package/tsconfig.json +19 -0
@@ -0,0 +1,572 @@
1
+ /**
2
+ * 车辆管理命令
3
+ *
4
+ * 重要概念澄清(2026-05-23):
5
+ * - 车辆群组:停车场功能,用于车辆出入控制
6
+ * - 车辆分类:系统预置分类(固定车、临时车等)+ 自定义群组
7
+ *
8
+ * 注意:车辆群组仅用于停车场管理,与人员通行无关
9
+ */
10
+
11
+ import { Command } from 'commander';
12
+ import { config } from '../utils/config';
13
+ import { success, error, formatOutput, formatBatchResult } from '../utils/output';
14
+ import { createHikvisionClient } from '../services/hikvisionClient';
15
+ import { validateJsonFile } from '../utils/validation';
16
+
17
+ export const vehicleCommands = new Command()
18
+ .name('vehicle')
19
+ .description('车辆管理');
20
+
21
+ // ========== 车辆 CRUD ==========
22
+
23
+ // 添加/更新车辆
24
+ vehicleCommands
25
+ .command('add')
26
+ .description('添加或更新车辆')
27
+ .option('-pl, --plate-no <plateNo>', '车牌号')
28
+ .option('-pid, --person-id <personId>', '人员 ID')
29
+ .option('-pn, --person-name <personName>', '人员姓名')
30
+ .option('-o, --org <orgIndexCode>', '组织 ID')
31
+ .option('-on, --org-name <orgName>', '组织名称')
32
+ .option('-t, --type <type>', '车辆类型 (0:燃油, 1:新能源, 2:电动, 3:其他)', '0')
33
+ .option('-c, --color <color>', '车辆颜色')
34
+ .option('-m, --model <model>', '车辆型号')
35
+ .option('-g, --group-id <groupId>', '车辆群组编号')
36
+ .action(async (options) => {
37
+ try {
38
+ const client = createHikvisionClient(config.get());
39
+
40
+ if (!options.plateNo) {
41
+ error('请指定车牌号 --plate-no 或 -pl');
42
+ return;
43
+ }
44
+
45
+ // 如果指定了人员姓名,需要先查找人员 ID
46
+ let personId = options.personId;
47
+ if (!personId && options.personName) {
48
+ personId = await findPersonIdByName(client, options.personName, options.org, options.orgName);
49
+ if (!personId) {
50
+ error(`未找到名为 "${options.personName}" 的人员`);
51
+ return;
52
+ }
53
+ }
54
+
55
+ const result = await client.vehicleService.saveOrUpdate({
56
+ plateNo: options.plateNo,
57
+ personId,
58
+ vehicleType: options.type as '0' | '1' | '2' | '3',
59
+ vehicleColor: options.color,
60
+ vehicleModel: options.model,
61
+ groupId: options.groupId,
62
+ });
63
+
64
+ success('车辆添加/更新成功!');
65
+ console.log(formatOutput(result));
66
+ } catch (err: any) {
67
+ error(`添加/更新车辆失败: ${err.message || err}`);
68
+ }
69
+ });
70
+
71
+ // 批量添加车辆
72
+ vehicleCommands
73
+ .command('batch-add')
74
+ .description('批量添加车辆')
75
+ .option('-f, --file <file>', 'JSON 文件路径')
76
+ .action(async (options) => {
77
+ try {
78
+ if (!options.file) {
79
+ error('请指定 --file 参数');
80
+ return;
81
+ }
82
+
83
+ const fs = await import('fs');
84
+ // 验证 JSON 文件格式和内容
85
+ const validated = validateJsonFile(fs.readFileSync(options.file, 'utf-8'), {
86
+ requiredFields: ['vehicles'],
87
+ maxSize: 5 * 1024 * 1024, // 5MB
88
+ });
89
+ if (!validated.valid) {
90
+ error(validated.error || 'JSON 文件验证失败');
91
+ return;
92
+ }
93
+ const data = JSON.parse(validated.content);
94
+
95
+ const client = createHikvisionClient(config.get());
96
+ const result = await client.vehicleService.batchAdd(data.vehicles);
97
+ console.log(formatBatchResult(result));
98
+ } catch (err: any) {
99
+ error(`批量添加车辆失败: ${err.message || err}`);
100
+ }
101
+ });
102
+
103
+ // 查询车辆列表
104
+ vehicleCommands
105
+ .command('list')
106
+ .description('查询车辆列表')
107
+ .option('-p, --page <page>', '页码', '1')
108
+ .option('-s, --size <size>', '每页数量', '20')
109
+ .option('-pl, --plate-no <plateNo>', '车牌号搜索')
110
+ .option('-pid, --person-id <personId>', '人员 ID')
111
+ .option('-pn, --person-name <personName>', '人员姓名(模糊搜索)')
112
+ .option('-g, --group-id <groupId>', '车辆群组编号')
113
+ .option('-t, --type <type>', '车辆类型')
114
+ .option('-st, --status <status>', '状态 (0:正常, 1:禁用)')
115
+ .action(async (options) => {
116
+ try {
117
+ const client = createHikvisionClient(config.get());
118
+
119
+ const result = await client.vehicleService.list({
120
+ pageNo: parseInt(options.page),
121
+ pageSize: parseInt(options.size),
122
+ plateNo: options.plateNo,
123
+ personId: options.personId,
124
+ groupId: options.groupId,
125
+ vehicleType: options.type as '0' | '1' | '2' | '3',
126
+ status: options.status as '0' | '1',
127
+ });
128
+
129
+ console.log(formatOutput(result));
130
+ } catch (err: any) {
131
+ error(`查询车辆失败: ${err.message || err}`);
132
+ }
133
+ });
134
+
135
+ // 修改车辆
136
+ vehicleCommands
137
+ .command('update')
138
+ .description('修改车辆')
139
+ .option('-vid, --vehicle-id <vehicleId>', '车辆 ID')
140
+ .option('-pl, --plate-no <plateNo>', '车牌号(用于查找车辆)')
141
+ .option('-pid, --person-id <personId>', '人员 ID')
142
+ .option('-pn, --person-name <personName>', '人员姓名')
143
+ .option('-o, --org <orgIndexCode>', '组织 ID')
144
+ .option('-on, --org-name <orgName>', '组织名称')
145
+ .option('-t, --type <type>', '车辆类型 (0:燃油, 1:新能源, 2:电动, 3:其他)')
146
+ .option('-c, --color <color>', '车辆颜色')
147
+ .option('-m, --model <model>', '车辆型号')
148
+ .option('-g, --group-id <groupId>', '车辆群组编号')
149
+ .action(async (options) => {
150
+ try {
151
+ const client = createHikvisionClient(config.get());
152
+
153
+ // 获取车辆 ID
154
+ let vehicleId = options.vehicleId;
155
+ if (!vehicleId && options.plateNo) {
156
+ const vehicle = await client.vehicleService.getByPlateNo(options.plateNo);
157
+ if (!vehicle || !vehicle.vehicleId) {
158
+ error(`未找到车牌号为 ${options.plateNo} 的车辆`);
159
+ return;
160
+ }
161
+ vehicleId = vehicle.vehicleId;
162
+ }
163
+
164
+ if (!vehicleId) {
165
+ error('请提供 --vehicle-id 或 --plate-no');
166
+ return;
167
+ }
168
+
169
+ // 如果指定了人员姓名,需要先查找人员 ID
170
+ let personId = options.personId;
171
+ if (!personId && options.personName) {
172
+ personId = await findPersonIdByName(client, options.personName, options.org, options.orgName);
173
+ if (!personId) {
174
+ error(`未找到名为 "${options.personName}" 的人员`);
175
+ return;
176
+ }
177
+ }
178
+
179
+ const result = await client.vehicleService.update(vehicleId, {
180
+ plateNo: options.plateNo,
181
+ personId,
182
+ vehicleType: options.type as '0' | '1' | '2' | '3',
183
+ vehicleColor: options.color,
184
+ vehicleModel: options.model,
185
+ groupId: options.groupId,
186
+ } as any);
187
+
188
+ success('车辆修改成功!');
189
+ console.log(formatOutput(result));
190
+ } catch (err: any) {
191
+ error(`修改车辆失败: ${err.message || err}`);
192
+ }
193
+ });
194
+
195
+ // 删除车辆
196
+ vehicleCommands
197
+ .command('delete')
198
+ .description('删除车辆')
199
+ .option('-vid, --vehicle-id <vehicleId>', '车辆 ID')
200
+ .option('-pl, --plate-no <plateNo>', '车牌号(用于查找车辆)')
201
+ .action(async (options) => {
202
+ try {
203
+ const client = createHikvisionClient(config.get());
204
+
205
+ let vehicleId = options.vehicleId;
206
+ if (!vehicleId && options.plateNo) {
207
+ const vehicle = await client.vehicleService.getByPlateNo(options.plateNo);
208
+ if (!vehicle || !vehicle.vehicleId) {
209
+ error(`未找到车牌号为 ${options.plateNo} 的车辆`);
210
+ return;
211
+ }
212
+ vehicleId = vehicle.vehicleId;
213
+ }
214
+
215
+ if (!vehicleId) {
216
+ error('请提供 --vehicle-id 或 --plate-no');
217
+ return;
218
+ }
219
+
220
+ await client.vehicleService.delete(vehicleId);
221
+ success('车辆删除成功!');
222
+ } catch (err: any) {
223
+ error(`删除车辆失败: ${err.message || err}`);
224
+ }
225
+ });
226
+
227
+ // ========== 车辆群组管理 ==========
228
+ // API: /artemis/api/pms/v1/car/category/*
229
+ /**
230
+ * 车辆群组管理命令
231
+ */
232
+ const carGroupCommands = vehicleCommands
233
+ .command('car-group')
234
+ .description('车辆群组管理(停车场功能)');
235
+
236
+ // 查询车辆群组列表
237
+ carGroupCommands
238
+ .command('list')
239
+ .description('查询车辆群组列表(包括系统分类和自定义群组)')
240
+ .option('-t, --type <type>', '群组类型: all=全部, system=系统分类, custom=自定义群组', 'all')
241
+ .action(async (options) => {
242
+ try {
243
+ const client = createHikvisionClient(config.get());
244
+ const groups = await client.vehicleService.listGroups();
245
+
246
+ let filtered = groups;
247
+ if (options.type === 'system') {
248
+ filtered = groups.filter(g => g.type === 'system');
249
+ } else if (options.type === 'custom') {
250
+ filtered = groups.filter(g => g.type === 'custom');
251
+ }
252
+
253
+ // 格式化输出
254
+ const data = filtered.map(g => ({
255
+ groupId: g.groupId,
256
+ groupName: g.groupName,
257
+ type: g.type === 'system' ? '系统分类' : '自定义群组',
258
+ description: g.description || '-',
259
+ }));
260
+
261
+ console.log(formatOutput(data));
262
+ } catch (err: any) {
263
+ error(`查询车辆群组失败: ${err.message || err}`);
264
+ }
265
+ });
266
+
267
+ // 添加车辆群组
268
+ carGroupCommands
269
+ .command('add')
270
+ .description('添加车辆群组(自定义群组)')
271
+ .option('-n, --name <name>', '群组名称')
272
+ .option('-t, --type <type>', '群组类型 (0:内部, 1:外部)', '0')
273
+ .option('-d, --desc <desc>', '描述')
274
+ .action(async (options) => {
275
+ try {
276
+ const client = createHikvisionClient(config.get());
277
+
278
+ if (!options.name) {
279
+ error('请提供群组名称 --name 或 -n');
280
+ return;
281
+ }
282
+
283
+ const result = await client.vehicleService.addGroup({
284
+ groupName: options.name,
285
+ groupType: options.type as '0' | '1',
286
+ description: options.desc,
287
+ });
288
+
289
+ success('车辆群组添加成功!');
290
+ console.log(formatOutput(result));
291
+ } catch (err: any) {
292
+ error(`添加车辆群组失败: ${err.message || err}`);
293
+ }
294
+ });
295
+
296
+ // 更新车辆群组
297
+ carGroupCommands
298
+ .command('update')
299
+ .description('更新车辆群组')
300
+ .option('-gid, --group-id <groupId>', '群组编号')
301
+ .option('-gn, --group-name <groupName>', '新群组名称')
302
+ .option('-d, --desc <desc>', '描述')
303
+ .action(async (options) => {
304
+ try {
305
+ const client = createHikvisionClient(config.get());
306
+
307
+ if (!options.groupId) {
308
+ error('请提供群组编号 --group-id 或 -gid');
309
+ return;
310
+ }
311
+
312
+ await client.vehicleService.updateGroup(options.groupId, {
313
+ groupName: options.groupName,
314
+ description: options.desc,
315
+ });
316
+
317
+ success('车辆群组更新成功!');
318
+ } catch (err: any) {
319
+ error(`更新车辆群组失败: ${err.message || err}`);
320
+ }
321
+ });
322
+
323
+ // 删除车辆群组
324
+ carGroupCommands
325
+ .command('delete')
326
+ .description('删除车辆群组')
327
+ .option('-gid, --group-id <groupId>', '群组编号')
328
+ .option('-gn, --group-name <groupName>', '群组名称(自动查找)')
329
+ .action(async (options) => {
330
+ try {
331
+ const client = createHikvisionClient(config.get());
332
+
333
+ let groupId = options.groupId;
334
+ if (!groupId && options.groupName) {
335
+ const groups = await client.vehicleService.listGroups();
336
+ const found = groups.find(g => g.groupName === options.groupName);
337
+ if (!found) {
338
+ error(`未找到名为 "${options.groupName}" 的群组`);
339
+ return;
340
+ }
341
+ groupId = found.groupId;
342
+
343
+ // 检查是否为系统分类,不允许删除
344
+ if (found.type === 'system') {
345
+ error(`系统分类 "${options.groupName}" 不允许删除`);
346
+ return;
347
+ }
348
+ }
349
+
350
+ if (!groupId) {
351
+ error('请提供群组编号 --group-id 或群组名称 --group-name');
352
+ return;
353
+ }
354
+
355
+ await client.vehicleService.deleteGroup(groupId);
356
+ success('车辆群组删除成功!');
357
+ } catch (err: any) {
358
+ error(`删除车辆群组失败: ${err.message || err}`);
359
+ }
360
+ });
361
+
362
+ // 绑定车辆到群组
363
+ carGroupCommands
364
+ .command('bind')
365
+ .description('将车辆绑定到车辆群组(或从群组解绑)')
366
+ .option('-vid, --vehicle-ids <vehicleIds>', '车辆 ID 集合(逗号分隔)')
367
+ .option('-pl, --plate-no <plateNo>', '车牌号(单辆,用于自动查找车辆 ID)')
368
+ .option('-gid, --group-id <groupId>', '群组编号')
369
+ .option('-gn, --group-name <groupName>', '群组名称(自动查找群组编号)')
370
+ .option('-r, --region <regionIndexCode>', '区域编号', 'root00000000')
371
+ .option('-o, --operation <operation>', '操作: bind=绑定, unbind=解绑', 'bind')
372
+ .action(async (options) => {
373
+ try {
374
+ const client = createHikvisionClient(config.get());
375
+
376
+ // 如果只提供了车牌号,需要先查找车辆 ID
377
+ let vehicleIds = options.vehicleIds;
378
+ if (!vehicleIds && options.plateNo) {
379
+ const vehicle = await client.vehicleService.getByPlateNo(options.plateNo);
380
+ if (!vehicle || !vehicle.vehicleId) {
381
+ error(`未找到车牌号为 ${options.plateNo} 的车辆`);
382
+ return;
383
+ }
384
+ vehicleIds = vehicle.vehicleId;
385
+ }
386
+
387
+ if (!vehicleIds) {
388
+ error('请提供 --vehicle-ids 或 --plate-no');
389
+ return;
390
+ }
391
+
392
+ // 如果提供了群组名称,需要查找群组编号
393
+ let groupId = options.groupId;
394
+ if (!groupId && options.groupName) {
395
+ const groups = await client.vehicleService.listGroups();
396
+ const found = groups.find(g => g.groupName === options.groupName);
397
+ if (!found) {
398
+ error(`未找到名为 "${options.groupName}" 的群组`);
399
+ return;
400
+ }
401
+ groupId = found.groupId;
402
+ }
403
+
404
+ if (!groupId) {
405
+ error('请提供 --group-id 或 --group-name');
406
+ return;
407
+ }
408
+
409
+ const operation = options.operation === 'bind' ? 1 : 2;
410
+ const results = await client.vehicleService.bindGroup(
411
+ vehicleIds,
412
+ groupId,
413
+ options.region,
414
+ operation
415
+ );
416
+
417
+ if (operation === 1) {
418
+ success(`成功绑定 ${results.length} 辆车到群组`);
419
+ } else {
420
+ success(`成功从群组解绑 ${results.length} 辆车`);
421
+ }
422
+ console.log(formatOutput(results));
423
+ } catch (err: any) {
424
+ error(`车辆群组绑定/解绑失败: ${err.message || err}`);
425
+ }
426
+ });
427
+
428
+ // ========== 停车场查询 ==========
429
+
430
+ const parkingCommands = vehicleCommands
431
+ .command('parking')
432
+ .description('停车场基础信息查询');
433
+
434
+ // 查询停车库列表
435
+ parkingCommands
436
+ .command('lots')
437
+ .description('查询停车库列表')
438
+ .action(async () => {
439
+ try {
440
+ const client = createHikvisionClient(config.get());
441
+ const lots = await client.vehicleService.listParkingLots();
442
+ console.log(formatOutput(lots));
443
+ } catch (err: any) {
444
+ error(`查询停车库列表失败: ${err.message || err}`);
445
+ }
446
+ });
447
+
448
+ // 查询出入口列表
449
+ parkingCommands
450
+ .command('entrances')
451
+ .description('查询出入口列表')
452
+ .option('-pc, --park-codes <parkCodes>', '停车库编号(逗号分隔)')
453
+ .action(async (options) => {
454
+ try {
455
+ const client = createHikvisionClient(config.get());
456
+
457
+ // 需要先获取停车库编号
458
+ let parkIndexCodes = options.parkCodes;
459
+ if (!parkIndexCodes) {
460
+ const lots = await client.vehicleService.listParkingLots();
461
+ if (lots.length > 0) {
462
+ parkIndexCodes = (lots as any[]).map(l => l.parkIndexCode).join(',');
463
+ }
464
+ }
465
+
466
+ if (!parkIndexCodes) {
467
+ error('未找到停车库,请先配置停车库');
468
+ return;
469
+ }
470
+
471
+ const entrances = await client.vehicleService.listEntrances(parkIndexCodes);
472
+ console.log(formatOutput(entrances));
473
+ } catch (err: any) {
474
+ error(`查询出入口列表失败: ${err.message || err}`);
475
+ }
476
+ });
477
+
478
+ // 查询车道列表
479
+ parkingCommands
480
+ .command('roadways')
481
+ .description('查询车道列表')
482
+ .option('-ec, --entrance-codes <entranceCodes>', '出入口编号(逗号分隔)')
483
+ .action(async (options) => {
484
+ try {
485
+ const client = createHikvisionClient(config.get());
486
+
487
+ let entranceIndexCodes = options.entranceCodes;
488
+ if (!entranceIndexCodes) {
489
+ error('请提供出入口编号 --entrance-codes');
490
+ return;
491
+ }
492
+
493
+ const roadways = await client.vehicleService.listRoadways(entranceIndexCodes);
494
+ console.log(formatOutput(roadways));
495
+ } catch (err: any) {
496
+ error(`查询车道列表失败: ${err.message || err}`);
497
+ }
498
+ });
499
+
500
+ // ========== 数据同步 ==========
501
+
502
+ // 增量同步车辆数据
503
+ vehicleCommands
504
+ .command('sync')
505
+ .description('增量同步车辆数据')
506
+ .option('-s, --start <startTime>', '开始时间 (ISO8601格式)')
507
+ .option('-e, --end <endTime>', '结束时间 (ISO8601格式)')
508
+ .action(async (options) => {
509
+ try {
510
+ const client = createHikvisionClient(config.get());
511
+
512
+ // 如果没有指定时间,使用默认时间范围(最近24小时)
513
+ let startTime = options.start;
514
+ let endTime = options.end;
515
+
516
+ if (!startTime || !endTime) {
517
+ const now = new Date();
518
+ const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
519
+ startTime = yesterday.toISOString();
520
+ endTime = now.toISOString();
521
+ }
522
+
523
+ const result = await client.vehicleService.syncIncrement(startTime, endTime);
524
+
525
+ console.log('增量同步完成!');
526
+ console.log(`新增: ${result.added.length} 条`);
527
+ console.log(`修改: ${result.modified.length} 条`);
528
+ console.log(`删除: ${result.deleted.length} 条`);
529
+
530
+ if (result.added.length > 0) {
531
+ console.log('\n新增车辆:');
532
+ console.log(formatOutput(result.added));
533
+ }
534
+ } catch (err: any) {
535
+ error(`增量同步车辆数据失败: ${err.message || err}`);
536
+ }
537
+ });
538
+
539
+ // ========== 辅助函数 ==========
540
+
541
+ /**
542
+ * 根据人员姓名查找人员 ID
543
+ */
544
+ async function findPersonIdByName(client: any, personName: string, orgIndexCode?: string, orgName?: string): Promise<string | undefined> {
545
+ try {
546
+ // 如果提供了组织名称,先查找组织编号
547
+ let targetOrgIndexCode = orgIndexCode;
548
+ if (!targetOrgIndexCode && orgName) {
549
+ const orgs = await client.personService.listOrganizations();
550
+ const found = orgs.find((o: any) => o.name === orgName || o.indexCode === orgName);
551
+ targetOrgIndexCode = found?.indexCode;
552
+ }
553
+
554
+ // 搜索人员
555
+ const result = await client.personService.list({
556
+ personName,
557
+ regionIndexCode: targetOrgIndexCode,
558
+ pageNo: 1,
559
+ pageSize: 10,
560
+ });
561
+
562
+ if (result.list && result.list.length > 0) {
563
+ // 精确匹配姓名
564
+ const exact = result.list.find((p: any) => p.personName === personName);
565
+ return exact?.personId || result.list[0]?.personId;
566
+ }
567
+
568
+ return undefined;
569
+ } catch {
570
+ return undefined;
571
+ }
572
+ }
package/src/index.ts ADDED
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI 入口文件
4
+ */
5
+
6
+ import { Command } from 'commander';
7
+ import { config, ConfigManager } from './utils/config';
8
+ import { success, error, info } from './utils/output';
9
+
10
+ // 导入命令
11
+ import { personCommands } from './commands/person';
12
+ import { cardCommands } from './commands/card';
13
+ import { vehicleCommands } from './commands/vehicle';
14
+ import { groupCommands } from './commands/group';
15
+ import { orgCommands } from './commands/org';
16
+ import { syncCommands } from './commands/sync';
17
+ import { systemCommands } from './commands/system';
18
+ import { serviceCommands } from './commands/service';
19
+ import { bindingCommands } from './commands/binding';
20
+
21
+ const program = new Command();
22
+
23
+ // 设置版本和描述
24
+ program
25
+ .name('hikvision-cli')
26
+ .description('海康威视平台管理 CLI 工具')
27
+ .version('1.0.0');
28
+
29
+ // 全局选项(注意:已移除 -k/-s 选项以防止密钥泄露到 shell 历史)
30
+ program
31
+ .option('-h, --host <host>', '海康平台主机地址')
32
+ .option('-f, --format <format>', '输出格式 (table|json|yaml)', 'table')
33
+ .option('-v, --verbose', '详细输出');
34
+
35
+ // 配置命令
36
+ program
37
+ .command('config')
38
+ .description('管理 CLI 配置')
39
+ .command('show', { isDefault: true })
40
+ .description('显示当前配置(敏感信息已脱敏)')
41
+ .action(() => {
42
+ const cfg = config.get();
43
+ if (!cfg) {
44
+ console.log('⚠️ 未检测到配置');
45
+ return;
46
+ }
47
+ // 输出时脱敏敏感信息
48
+ console.log(JSON.stringify(config.maskSensitive(cfg), null, 2));
49
+ });
50
+
51
+ program
52
+ .command('config set')
53
+ .description('设置配置项')
54
+ .argument('<key>', '配置键 (host|appKey|appSecret|outputFormat|pageSize|logLevel)')
55
+ .argument('<value>', '配置值')
56
+ .action((key, value) => {
57
+ config.update({ [key]: value });
58
+ success(`配置已更新: ${key} = ${value}`);
59
+ });
60
+
61
+ program
62
+ .command('config reset')
63
+ .description('重置为默认配置')
64
+ .action(() => {
65
+ config.reset();
66
+ success('配置已重置为默认值');
67
+ });
68
+
69
+ // 人员命令
70
+ program.addCommand(personCommands);
71
+
72
+ // 卡片命令
73
+ program.addCommand(cardCommands);
74
+
75
+ // 车辆命令
76
+ program.addCommand(vehicleCommands);
77
+
78
+ // 分组命令
79
+ program.addCommand(groupCommands);
80
+
81
+ // 组织命令
82
+ program.addCommand(orgCommands);
83
+
84
+ // 同步命令
85
+ program.addCommand(syncCommands);
86
+
87
+ // 系统命令
88
+ program.addCommand(systemCommands);
89
+
90
+ // 服务控制命令
91
+ program.addCommand(serviceCommands);
92
+
93
+ // 绑定管理命令
94
+ program.addCommand(bindingCommands);
95
+
96
+ // 全局前置检查:确保已配置
97
+ function checkConfig(): void {
98
+ const command = process.argv[2];
99
+ if (command === 'config' || command === '--help' || command === '-h' || command === 'version' || command === '--version') {
100
+ return;
101
+ }
102
+ if (!config.isConfigured()) {
103
+ ConfigManager.promptConfig();
104
+ process.exit(1);
105
+ }
106
+ }
107
+
108
+ checkConfig();
109
+
110
+ // 解析命令行
111
+ program.parse();