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.
- package/dist/commands/binding.d.ts +6 -0
- package/dist/commands/binding.d.ts.map +1 -0
- package/dist/commands/binding.js +274 -0
- package/dist/commands/binding.js.map +1 -0
- package/dist/commands/card.d.ts +6 -0
- package/dist/commands/card.d.ts.map +1 -0
- package/dist/commands/card.js +259 -0
- package/dist/commands/card.js.map +1 -0
- package/dist/commands/group.d.ts +6 -0
- package/dist/commands/group.d.ts.map +1 -0
- package/dist/commands/group.js +87 -0
- package/dist/commands/group.js.map +1 -0
- package/dist/commands/org.d.ts +6 -0
- package/dist/commands/org.d.ts.map +1 -0
- package/dist/commands/org.js +248 -0
- package/dist/commands/org.js.map +1 -0
- package/dist/commands/person.d.ts +6 -0
- package/dist/commands/person.d.ts.map +1 -0
- package/dist/commands/person.js +563 -0
- package/dist/commands/person.js.map +1 -0
- package/dist/commands/service.d.ts +6 -0
- package/dist/commands/service.d.ts.map +1 -0
- package/dist/commands/service.js +153 -0
- package/dist/commands/service.js.map +1 -0
- package/dist/commands/sync.d.ts +6 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +138 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/system.d.ts +6 -0
- package/dist/commands/system.d.ts.map +1 -0
- package/dist/commands/system.js +167 -0
- package/dist/commands/system.js.map +1 -0
- package/dist/commands/vehicle.d.ts +12 -0
- package/dist/commands/vehicle.d.ts.map +1 -0
- package/dist/commands/vehicle.js +550 -0
- package/dist/commands/vehicle.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +94 -0
- package/dist/index.js.map +1 -0
- package/dist/services/hikvisionClient.d.ts +25 -0
- package/dist/services/hikvisionClient.d.ts.map +1 -0
- package/dist/services/hikvisionClient.js +59 -0
- package/dist/services/hikvisionClient.js.map +1 -0
- package/dist/utils/config.d.ts +66 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +213 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/output.d.ts +42 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +157 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/validation.d.ts +52 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +171 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +29 -0
- package/src/commands/binding.ts +305 -0
- package/src/commands/card.ts +238 -0
- package/src/commands/group.ts +91 -0
- package/src/commands/org.ts +228 -0
- package/src/commands/person.ts +596 -0
- package/src/commands/service.ts +174 -0
- package/src/commands/sync.ts +156 -0
- package/src/commands/system.ts +137 -0
- package/src/commands/vehicle.ts +572 -0
- package/src/index.ts +111 -0
- package/src/services/hikvisionClient.ts +74 -0
- package/src/types/cli-table3.d.ts +20 -0
- package/src/utils/config.ts +199 -0
- package/src/utils/output.ts +181 -0
- package/src/utils/validation.ts +160 -0
- 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
|
+
});
|