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,305 @@
1
+ /**
2
+ * 绑定管理命令
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import { config } from '../utils/config';
7
+ import { success, error, formatOutput } from '../utils/output';
8
+ import { createHikvisionClient } from '../services/hikvisionClient';
9
+
10
+ export const bindingCommands = new Command()
11
+ .command('binding')
12
+ .description('绑定关系管理(人员-卡片/人员-车辆)');
13
+
14
+ /**
15
+ * 人员-卡片绑定
16
+ */
17
+ const personCard = bindingCommands
18
+ .command('person-card')
19
+ .description('人员-卡片绑定');
20
+
21
+ personCard
22
+ .command('list')
23
+ .description('查询人员-卡片绑定列表')
24
+ .option('-p, --page <page>', '页码', '1')
25
+ .option('-s, --size <size>', '每页数量', '50')
26
+ .action(async (options) => {
27
+ try {
28
+ const client = createHikvisionClient(config.get());
29
+
30
+ // 查询所有卡片,过滤有 personId 的
31
+ const result: any = await client.cardService.list({
32
+ pageNo: parseInt(options.page),
33
+ pageSize: parseInt(options.size),
34
+ });
35
+
36
+ const cards = (result?.data?.list || result?.list || [])
37
+ .filter((c: any) => c.personId);
38
+
39
+ if (cards.length === 0) {
40
+ console.log('暂无绑定记录');
41
+ return;
42
+ }
43
+
44
+ console.log(`\n🪪 人员-卡片绑定列表 (共 ${cards.length} 条)\n`);
45
+ console.log(' 人员ID 人员姓名 卡号 绑定状态');
46
+ console.log(' ─────────────────────────────────────────────────────────────────────────────');
47
+
48
+ for (const c of cards) {
49
+ console.log(` ${c.personId} ${(c.personName || '-').substring(0, 10).padEnd(12)} ${c.cardNo.padEnd(20)} ${statusText(c.status)}`);
50
+ }
51
+ } catch (err: any) {
52
+ error(`查询失败: ${err.message || err}`);
53
+ }
54
+ });
55
+
56
+ personCard
57
+ .command('bind')
58
+ .description('绑定人员到卡片')
59
+ .option('-pid, --person-id <personId>', '人员 ID')
60
+ .option('-n, --person-name <personName>', '人员姓名')
61
+ .option('-c, --card-no <cardNo>', '卡号')
62
+ .action(async (options) => {
63
+ try {
64
+ const client = createHikvisionClient(config.get());
65
+ let personId = options.personId;
66
+
67
+ if (!personId && options.personName) {
68
+ personId = await findPersonIdByName(client, options.personName);
69
+ if (!personId) {
70
+ error(`未找到名为 "${options.personName}" 的人员`);
71
+ return;
72
+ }
73
+ }
74
+
75
+ if (!personId || !options.cardNo) {
76
+ error('请指定 --person-id (或 --person-name) 和 --card-no');
77
+ return;
78
+ }
79
+
80
+ await client.cardService.bind({
81
+ startDate: new Date().toISOString().split('T')[0],
82
+ endDate: '2037-12-31',
83
+ cards: [{ cardNo: options.cardNo, personId, cardType: 0 }],
84
+ });
85
+
86
+ success(`绑定成功:人员 ${personId} → 卡片 ${options.cardNo}`);
87
+ } catch (err: any) {
88
+ error(`绑定失败: ${err.message || err}`);
89
+ }
90
+ });
91
+
92
+ personCard
93
+ .command('unbind')
94
+ .description('解绑人员与卡片')
95
+ .option('-pid, --person-id <personId>', '人员 ID')
96
+ .option('-n, --person-name <personName>', '人员姓名')
97
+ .option('-c, --card-no <cardNo>', '卡号')
98
+ .action(async (options) => {
99
+ try {
100
+ const client = createHikvisionClient(config.get());
101
+ let personId = options.personId;
102
+
103
+ if (!personId && options.personName) {
104
+ personId = await findPersonIdByName(client, options.personName);
105
+ if (!personId) {
106
+ error(`未找到名为 "${options.personName}" 的人员`);
107
+ return;
108
+ }
109
+ }
110
+
111
+ if (!personId || !options.cardNo) {
112
+ error('请指定 --person-id (或 --person-name) 和 --card-no');
113
+ return;
114
+ }
115
+
116
+ await client.cardService.unbind(options.cardNo, personId);
117
+ success(`解绑成功:人员 ${personId} ←→ 卡片 ${options.cardNo}`);
118
+ } catch (err: any) {
119
+ error(`解绑失败: ${err.message || err}`);
120
+ }
121
+ });
122
+
123
+ /**
124
+ * 人员-车辆绑定
125
+ */
126
+ const personVehicle = bindingCommands
127
+ .command('person-vehicle')
128
+ .description('人员-车辆绑定');
129
+
130
+ personVehicle
131
+ .command('list')
132
+ .description('查询人员-车辆绑定列表')
133
+ .option('-p, --page <page>', '页码', '1')
134
+ .option('-s, --size <size>', '每页数量', '50')
135
+ .action(async (options) => {
136
+ try {
137
+ const client = createHikvisionClient(config.get());
138
+
139
+ // 使用 advance 接口,通过 personIds 过滤
140
+ const allBindings: any[] = [];
141
+ let pageNo = 1;
142
+ const pageSize = 100;
143
+
144
+ while (true) {
145
+ const result: any = await (client.vehicleService as any).list({
146
+ pageNo,
147
+ pageSize,
148
+ isBandPerson: 1,
149
+ });
150
+
151
+ const vehicles = result?.data?.list || result?.list || [];
152
+ if (vehicles.length === 0) break;
153
+
154
+ const bound = vehicles.filter((v: any) => v.personId);
155
+ allBindings.push(...bound);
156
+
157
+ if (vehicles.length < pageSize) break;
158
+ pageNo++;
159
+ }
160
+
161
+ if (allBindings.length === 0) {
162
+ console.log('暂无绑定记录');
163
+ return;
164
+ }
165
+
166
+ console.log(`\n🚗 人员-车辆绑定列表 (共 ${allBindings.length} 条)\n`);
167
+ console.log(' 车辆ID 车牌号 人员ID 人员姓名');
168
+ console.log(' ─────────────────────────────────────────────────────────────────────────────');
169
+
170
+ for (const v of allBindings.slice(0, parseInt(options.size))) {
171
+ console.log(` ${v.vehicleId} ${(v.plateNo || '-').padEnd(10)} ${v.personId} ${v.personName || '-'}`);
172
+ }
173
+ } catch (err: any) {
174
+ error(`查询失败: ${err.message || err}`);
175
+ }
176
+ });
177
+
178
+ personVehicle
179
+ .command('bind')
180
+ .description('绑定人员到车辆(设置车辆的车主)')
181
+ .option('-vid, --vehicle-id <vehicleId>', '车辆 ID')
182
+ .option('-l, --plate-no <plateNo>', '车牌号(与 --vehicle-id 二选一)')
183
+ .option('-pid, --person-id <personId>', '人员 ID')
184
+ .option('-n, --person-name <personName>', '人员姓名')
185
+ .action(async (options) => {
186
+ try {
187
+ const client = createHikvisionClient(config.get());
188
+
189
+ // 获取车辆 ID
190
+ let vehicleId = options.vehicleId;
191
+ if (!vehicleId && options.plateNo) {
192
+ const v: any = await client.vehicleService.getByPlateNo(options.plateNo);
193
+ vehicleId = v?.vehicleId;
194
+ if (!vehicleId) {
195
+ error(`未找到车牌为 "${options.plateNo}" 的车辆`);
196
+ return;
197
+ }
198
+ }
199
+
200
+ // 获取人员 ID
201
+ let personId = options.personId;
202
+ if (!personId && options.personName) {
203
+ personId = await findPersonIdByName(client, options.personName);
204
+ if (!personId) {
205
+ error(`未找到名为 "${options.personName}" 的人员`);
206
+ return;
207
+ }
208
+ }
209
+
210
+ if (!vehicleId || !personId) {
211
+ error('请指定车辆 ID (--vehicle-id 或 --plate-no) 和人员 ID (--person-id 或 --person-name)');
212
+ return;
213
+ }
214
+
215
+ // 获取车辆信息用于更新
216
+ const vehicle: any = await client.vehicleService.getById(vehicleId);
217
+
218
+ // 通过 saveOrUpdate 绑定人员(personId 字段即为绑定)
219
+ await client.vehicleService.saveOrUpdate({
220
+ plateNo: vehicle.plateNo,
221
+ vehicleType: vehicle.vehicleType,
222
+ vehicleColor: vehicle.vehicleColor,
223
+ vehicleModel: vehicle.vehicleModel,
224
+ personId,
225
+ });
226
+
227
+ success(`绑定成功:车辆 ${vehicleId} → 人员 ${personId}`);
228
+ } catch (err: any) {
229
+ error(`绑定失败: ${err.message || err}`);
230
+ }
231
+ });
232
+
233
+ personVehicle
234
+ .command('unbind')
235
+ .description('解绑人员与车辆(清除车辆的车主)')
236
+ .option('-vid, --vehicle-id <vehicleId>', '车辆 ID')
237
+ .option('-l, --plate-no <plateNo>', '车牌号(与 --vehicle-id 二选一)')
238
+ .action(async (options) => {
239
+ try {
240
+ const client = createHikvisionClient(config.get());
241
+
242
+ // 获取车辆 ID
243
+ let vehicleId = options.vehicleId;
244
+ if (!vehicleId && options.plateNo) {
245
+ const v: any = await client.vehicleService.getByPlateNo(options.plateNo);
246
+ vehicleId = v?.vehicleId;
247
+ if (!vehicleId) {
248
+ error(`未找到车牌为 "${options.plateNo}" 的车辆`);
249
+ return;
250
+ }
251
+ }
252
+
253
+ if (!vehicleId) {
254
+ error('请指定 --vehicle-id 或 --plate-no');
255
+ return;
256
+ }
257
+
258
+ // 获取车辆信息
259
+ const vehicle: any = await client.vehicleService.getById(vehicleId);
260
+
261
+ // 通过 saveOrUpdate 解绑(personId 置空)
262
+ await client.vehicleService.saveOrUpdate({
263
+ plateNo: vehicle.plateNo,
264
+ vehicleType: vehicle.vehicleType,
265
+ vehicleColor: vehicle.vehicleColor,
266
+ vehicleModel: vehicle.vehicleModel,
267
+ personId: '',
268
+ });
269
+
270
+ success(`解绑成功:车辆 ${vehicleId} 已清除车主`);
271
+ } catch (err: any) {
272
+ error(`解绑失败: ${err.message || err}`);
273
+ }
274
+ });
275
+
276
+ // ============ 内部辅助函数 ============
277
+
278
+ async function findPersonIdByName(
279
+ client: ReturnType<typeof createHikvisionClient>,
280
+ personName: string
281
+ ): Promise<string | null> {
282
+ const list: any = await client.personService.list({
283
+ pageNo: 1,
284
+ pageSize: 50,
285
+ personName,
286
+ });
287
+
288
+ const persons = list?.data?.list || list?.list || [];
289
+ if (persons.length === 0) return null;
290
+ if (persons.length === 1) return persons[0].personId;
291
+
292
+ console.log(`找到 ${persons.length} 条匹配记录,请使用 --person-id 指定:`);
293
+ for (const p of persons) {
294
+ console.log(` ${p.personId} ${p.personName} ${p.phoneNo || '-'}`);
295
+ }
296
+ return null;
297
+ }
298
+
299
+ function statusText(status?: number): string {
300
+ if (status === 0) return '正常';
301
+ if (status === 1) return '挂失';
302
+ if (status === 2) return '退卡';
303
+ if (status === 3) return '损坏';
304
+ return '未知';
305
+ }
@@ -0,0 +1,238 @@
1
+ /**
2
+ * 卡片管理命令
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import { config } from '../utils/config';
7
+ import { success, error, formatOutput, formatBatchResult } from '../utils/output';
8
+ import { createHikvisionClient } from '../services/hikvisionClient';
9
+ import { validateJsonFile } from '../utils/validation';
10
+
11
+ export const cardCommands = new Command()
12
+ .command('card')
13
+ .description('卡片管理');
14
+
15
+ // 开卡并绑定(开卡+绑定一体)
16
+ cardCommands
17
+ .command('bind')
18
+ .description('开卡并绑定(开卡+绑定一体)')
19
+ .option('-f, --file <file>', 'JSON 文件路径')
20
+ .option('-c, --card-no <cardNo>', '卡号')
21
+ .option('-p, --person-id <personId>', '人员 ID')
22
+ .option('-n, --person-name <personName>', '人员姓名')
23
+ .option('-o, --org <orgIndexCode>', '组织 ID')
24
+ .option('-on, --org-name <orgName>', '组织名称')
25
+ .option('-t, --type <type>', '卡类型 (0:普通, 1:临时, 2:特殊)')
26
+ .option('-e, --expire <expire>', '过期时间')
27
+ .action(async (options) => {
28
+ try {
29
+ const client = createHikvisionClient(config.get());
30
+
31
+ // 如果指定了人员姓名,需要先查找人员 ID
32
+ let personId = options.personId;
33
+ if (!personId && options.personName) {
34
+ personId = await findPersonIdByName(client, options.personName, options.org, options.orgName);
35
+ if (!personId) {
36
+ error(`未找到名为 "${options.personName}" 的人员`);
37
+ return;
38
+ }
39
+ }
40
+
41
+ if (!personId || !options.cardNo) {
42
+ error('请指定人员 ID (--person-id 或 --person-name) 和卡号 (--card-no)');
43
+ return;
44
+ }
45
+
46
+ const result = await client.cardService.bind({
47
+ startDate: new Date().toISOString().split('T')[0],
48
+ endDate: '2037-12-31',
49
+ cards: [{ cardNo: options.cardNo, personId, cardType: options.type ? parseInt(options.type) : undefined }]
50
+ });
51
+ console.log(formatBatchResult(result));
52
+ } catch (err: any) {
53
+ error(`绑定卡片失败: ${err.message || err}`);
54
+ }
55
+ });
56
+
57
+ // 查询卡片列表
58
+ cardCommands
59
+ .command('list')
60
+ .description('查询卡片列表')
61
+ .option('-p, --page <page>', '页码', '1')
62
+ .option('-s, --size <size>', '每页数量', '20')
63
+ .option('-c, --card-no <cardNo>', '卡号搜索')
64
+ .option('-pid, --person-id <personId>', '人员 ID')
65
+ .option('-n, --person-name <personName>', '人员姓名(模糊搜索)')
66
+ .option('-t, --type <type>', '卡类型')
67
+ .option('-st, --status <status>', '状态 (0:正常, 1:挂失, 2:退卡, 3:损坏)')
68
+ .action(async (options) => {
69
+ try {
70
+ const client = createHikvisionClient(config.get());
71
+
72
+ const result = await client.cardService.list({
73
+ pageNo: parseInt(options.page),
74
+ pageSize: parseInt(options.size),
75
+ cardNo: options.cardNo,
76
+ personId: options.personId,
77
+ personName: options.personName,
78
+ cardType: options.type as '0' | '1' | '2',
79
+ status: options.status as '0' | '1' | '2' | '3',
80
+ });
81
+
82
+ console.log(formatOutput(result));
83
+ } catch (err: any) {
84
+ error(`查询卡片失败: ${err.message || err}`);
85
+ }
86
+ });
87
+
88
+ // 退卡(解绑)
89
+ cardCommands
90
+ .command('unbind')
91
+ .description('退卡(解绑人员与卡片)')
92
+ .option('-f, --file <file>', 'JSON 文件路径')
93
+ .option('-c, --card-no <cardNo>', '卡号')
94
+ .option('-p, --person-id <personId>', '人员 ID')
95
+ .option('-n, --person-name <personName>', '人员姓名')
96
+ .option('-o, --org <orgIndexCode>', '组织 ID')
97
+ .option('-on, --org-name <orgName>', '组织名称')
98
+ .action(async (options) => {
99
+ try {
100
+ const client = createHikvisionClient(config.get());
101
+
102
+ if (options.file) {
103
+ const fs = await import('fs');
104
+ // 验证 JSON 文件格式和内容
105
+ const validated = validateJsonFile(fs.readFileSync(options.file, 'utf-8'), {
106
+ requiredFields: ['cards'],
107
+ maxSize: 5 * 1024 * 1024, // 5MB
108
+ });
109
+ const data = JSON.parse(validated.content);
110
+ const result = await client.cardService.batchUnbind(data.cards);
111
+ console.log(formatBatchResult(result));
112
+ } else if (options.cardNo) {
113
+ let personId = options.personId;
114
+ if (!personId && options.personName) {
115
+ personId = await findPersonIdByName(client, options.personName, options.org, options.orgName);
116
+ if (!personId) {
117
+ error(`未找到名为 "${options.personName}" 的人员`);
118
+ return;
119
+ }
120
+ }
121
+
122
+ if (!personId) {
123
+ error('请指定 --person-id 或 --person-name');
124
+ return;
125
+ }
126
+
127
+ await client.cardService.unbind(options.cardNo, personId);
128
+ success('退卡成功');
129
+ } else {
130
+ error('请指定 --file 或 --card-no');
131
+ }
132
+ } catch (err: any) {
133
+ error(`退卡失败: ${err.message || err}`);
134
+ }
135
+ });
136
+
137
+ // 挂失
138
+ cardCommands
139
+ .command('loss')
140
+ .description('批量挂失')
141
+ .option('-f, --file <file>', 'JSON 文件路径')
142
+ .option('-c, --card-no <cardNo>', '卡号')
143
+ .action(async (options) => {
144
+ try {
145
+ const client = createHikvisionClient(config.get());
146
+
147
+ if (options.file) {
148
+ const fs = await import('fs');
149
+ // 验证 JSON 文件格式和内容
150
+ const validated = validateJsonFile(fs.readFileSync(options.file, 'utf-8'), {
151
+ requiredFields: ['cards'],
152
+ maxSize: 5 * 1024 * 1024, // 5MB
153
+ });
154
+ const data = JSON.parse(validated.content);
155
+ const result = await client.cardService.batchLoss(data.cards);
156
+ console.log(formatBatchResult(result));
157
+ } else if (options.cardNo) {
158
+ await client.cardService.loss(options.cardNo);
159
+ success('挂失成功');
160
+ } else {
161
+ error('请指定 --file 或 --card-no');
162
+ }
163
+ } catch (err: any) {
164
+ error(`挂失失败: ${err.message || err}`);
165
+ }
166
+ });
167
+
168
+ // 解挂
169
+ cardCommands
170
+ .command('unloss')
171
+ .description('批量解挂')
172
+ .option('-f, --file <file>', 'JSON 文件路径')
173
+ .option('-c, --card-no <cardNo>', '卡号')
174
+ .action(async (options) => {
175
+ try {
176
+ const client = createHikvisionClient(config.get());
177
+
178
+ if (options.file) {
179
+ const fs = await import('fs');
180
+ // 验证 JSON 文件格式和内容
181
+ const validated = validateJsonFile(fs.readFileSync(options.file, 'utf-8'), {
182
+ requiredFields: ['cards'],
183
+ maxSize: 5 * 1024 * 1024, // 5MB
184
+ });
185
+ const data = JSON.parse(validated.content);
186
+ const result = await client.cardService.batchUnloss(data.cards);
187
+ console.log(formatBatchResult(result));
188
+ } else if (options.cardNo) {
189
+ await client.cardService.unloss(options.cardNo);
190
+ success('解挂成功');
191
+ } else {
192
+ error('请指定 --file 或 --card-no');
193
+ }
194
+ } catch (err: any) {
195
+ error(`解挂失败: ${err.message || err}`);
196
+ }
197
+ });
198
+
199
+ // ============ 内部辅助函数 ============
200
+
201
+ async function findPersonIdByName(
202
+ client: ReturnType<typeof createHikvisionClient>,
203
+ personName: string,
204
+ orgIndexCode?: string,
205
+ orgName?: string
206
+ ): Promise<string | null> {
207
+ // 如果有 orgName,先查 orgIndexCode
208
+ let targetOrgCode = orgIndexCode;
209
+ if (orgName && !targetOrgCode) {
210
+ const orgs = await client.personService.getOrganizations();
211
+ const found = orgs.find(o => o.orgName === orgName);
212
+ if (!found) {
213
+ return null;
214
+ }
215
+ targetOrgCode = found.orgIndexCode;
216
+ }
217
+
218
+ const list = await client.personService.list({
219
+ pageNo: 1,
220
+ pageSize: 50,
221
+ personName,
222
+ orgIndexCode: targetOrgCode,
223
+ });
224
+
225
+ const persons = (list as any).list ?? [];
226
+ if (persons.length === 0) {
227
+ return null;
228
+ }
229
+ if (persons.length === 1) {
230
+ return persons[0].personId;
231
+ }
232
+
233
+ console.log(`找到 ${persons.length} 条匹配记录,请使用 --person-id 指定:`);
234
+ for (const p of persons) {
235
+ console.log(` ${p.personId} ${p.personName} ${p.phoneNo || '-'} org: ${p.orgIndexCode}`);
236
+ }
237
+ return null;
238
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * 分组管理命令
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import { config } from '../utils/config';
7
+ import { success, error, formatOutput } from '../utils/output';
8
+ import { createHikvisionClient } from '../services/hikvisionClient';
9
+
10
+ export const groupCommands = new Command()
11
+ .command('group')
12
+ .description('车辆群组管理');
13
+
14
+ // 查询分组列表
15
+ groupCommands
16
+ .command('list')
17
+ .description('查询车辆群组列表')
18
+ .action(async () => {
19
+ try {
20
+ const client = createHikvisionClient(config.get());
21
+
22
+ const result = await client.vehicleService.listGroups();
23
+ console.log(formatOutput(result));
24
+ } catch (err: any) {
25
+ error(`查询分组失败: ${err.message || err}`);
26
+ }
27
+ });
28
+
29
+ // 添加分组
30
+ groupCommands
31
+ .command('add')
32
+ .description('添加车辆群组')
33
+ .option('-n, --name <name>', '分组名称')
34
+ .option('-t, --type <type>', '分组类型 (0:内部, 1:外部)', '0')
35
+ .option('-d, --desc <desc>', '描述')
36
+ .action(async (options) => {
37
+ try {
38
+ const client = createHikvisionClient(config.get());
39
+
40
+ const result = await client.vehicleService.addGroup({
41
+ groupName: options.name,
42
+ groupType: options.type as '0' | '1',
43
+ description: options.desc,
44
+ });
45
+
46
+ success('分组添加成功!');
47
+ console.log(formatOutput(result));
48
+ } catch (err: any) {
49
+ error(`添加分组失败: ${err.message || err}`);
50
+ }
51
+ });
52
+
53
+ // 修改分组
54
+ groupCommands
55
+ .command('update')
56
+ .description('修改车辆群组')
57
+ .argument('<groupId>', '分组 ID')
58
+ .option('-n, --name <name>', '分组名称')
59
+ .option('-t, --type <type>', '分组类型 (0:内部, 1:外部)')
60
+ .option('-d, --desc <desc>', '描述')
61
+ .action(async (groupId, options) => {
62
+ try {
63
+ const client = createHikvisionClient(config.get());
64
+
65
+ await client.vehicleService.updateGroup(groupId, {
66
+ groupName: options.name,
67
+ groupType: options.type as '0' | '1',
68
+ description: options.desc,
69
+ });
70
+
71
+ success('分组已更新');
72
+ } catch (err: any) {
73
+ error(`修改分组失败: ${err.message || err}`);
74
+ }
75
+ });
76
+
77
+ // 删除分组
78
+ groupCommands
79
+ .command('delete')
80
+ .description('删除车辆群组')
81
+ .argument('<groupId>', '分组 ID')
82
+ .action(async (groupId) => {
83
+ try {
84
+ const client = createHikvisionClient(config.get());
85
+
86
+ await client.vehicleService.deleteGroup(groupId);
87
+ success('分组已删除');
88
+ } catch (err: any) {
89
+ error(`删除分组失败: ${err.message || err}`);
90
+ }
91
+ });