feat: show DingTalk punch device details
This commit is contained in:
@@ -104,8 +104,10 @@ export class AttendanceImportService {
|
||||
|
||||
// Stage 2: Parse & deduplicate
|
||||
this.emit('parsing', 0, total, `Parsing ${total} records...`);
|
||||
const existingDingIds = await this.getExistingDingIds(rawResults);
|
||||
const newRecords = rawResults.filter((r) => !existingDingIds.has(r.checkId));
|
||||
const existingByDingId = await this.getExistingRecordsByDingId(rawResults);
|
||||
const newRecords = rawResults.filter((r) => !existingByDingId.has(r.checkId));
|
||||
const duplicateRecords = rawResults.filter((r) => existingByDingId.has(r.checkId));
|
||||
await this.refreshDuplicatePunchMetadata(duplicateRecords, existingByDingId);
|
||||
skipped = rawResults.length - newRecords.length;
|
||||
this.emit('parsing', newRecords.length, total, `${newRecords.length} new records, ${skipped} duplicates skipped`);
|
||||
|
||||
@@ -246,17 +248,45 @@ export class AttendanceImportService {
|
||||
/**
|
||||
* Query which dingIds already exist to skip duplicates.
|
||||
*/
|
||||
private async getExistingDingIds(
|
||||
private async getExistingRecordsByDingId(
|
||||
results: DingTalkAttendanceResult[],
|
||||
): Promise<Set<string>> {
|
||||
): Promise<Map<string, DingAttendanceRaw>> {
|
||||
const dingIds = results.map((r) => r.checkId).filter(Boolean);
|
||||
if (dingIds.length === 0) return new Set();
|
||||
if (dingIds.length === 0) return new Map();
|
||||
|
||||
const existing = await this.dingRawRepo.find({
|
||||
where: { dingId: In(dingIds) },
|
||||
select: ['dingId'],
|
||||
});
|
||||
return new Set(existing.map((e) => e.dingId));
|
||||
return new Map(existing.map((entity) => [entity.dingId, entity]));
|
||||
}
|
||||
|
||||
private async refreshDuplicatePunchMetadata(
|
||||
results: DingTalkAttendanceResult[],
|
||||
existingByDingId: Map<string, DingAttendanceRaw>,
|
||||
): Promise<void> {
|
||||
const changed: DingAttendanceRaw[] = [];
|
||||
for (const result of results) {
|
||||
const entity = existingByDingId.get(result.checkId);
|
||||
if (!entity) continue;
|
||||
const punchSource = result.sourceType || entity.punchSource || null;
|
||||
const punchDeviceName = result.deviceName || entity.punchDeviceName || null;
|
||||
const punchDeviceId = result.deviceId || entity.punchDeviceId || null;
|
||||
if (
|
||||
entity.punchSource === punchSource &&
|
||||
entity.punchDeviceName === punchDeviceName &&
|
||||
entity.punchDeviceId === punchDeviceId
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
entity.punchSource = punchSource;
|
||||
entity.punchDeviceName = punchDeviceName;
|
||||
entity.punchDeviceId = punchDeviceId;
|
||||
entity.rawData = JSON.stringify(result);
|
||||
changed.push(entity);
|
||||
}
|
||||
if (changed.length > 0) {
|
||||
await this.dingRawRepo.save(changed, { chunk: 50 });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,6 +301,9 @@ export class AttendanceImportService {
|
||||
entity.attendanceType = r.checkType || 'OnDuty';
|
||||
entity.timeResult = r.timeResult;
|
||||
entity.locationResult = r.locationResult || '';
|
||||
entity.punchSource = r.sourceType || null;
|
||||
entity.punchDeviceName = r.deviceName || null;
|
||||
entity.punchDeviceId = r.deviceId || null;
|
||||
|
||||
// Parse check-in/out times
|
||||
if (r.actualCheckTime) {
|
||||
|
||||
Reference in New Issue
Block a user