mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 22:18:12 +00:00
add goverment booking
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, MinLength } from 'class-validator';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsOptional, IsString, MinLength } from 'class-validator';
|
||||
|
||||
export class SaveSignatureDto {
|
||||
@ApiProperty()
|
||||
@@ -13,6 +13,15 @@ export class SaveSignatureDto {
|
||||
@IsString()
|
||||
@MinLength(20)
|
||||
signatureImageBase64!: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
'Company stamp/seal image as base64 (with or without data URL prefix). Omit to keep the existing saved stamp.',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MinLength(20)
|
||||
stampImageBase64?: string;
|
||||
}
|
||||
|
||||
export class SavedSignatureDto {
|
||||
@@ -21,4 +30,7 @@ export class SavedSignatureDto {
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
signatureImageUrl!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
stampImageUrl!: string | null;
|
||||
}
|
||||
|
||||
@@ -22,4 +22,11 @@ export class SavedSignature extends BaseEntity {
|
||||
@ManyToOne(() => FileRecord, { nullable: true })
|
||||
@JoinColumn({ name: 'signature_file_id' })
|
||||
signatureFile?: FileRecord | null;
|
||||
|
||||
@Column({ name: 'stamp_file_id', type: 'uuid', nullable: true })
|
||||
stampFileId?: string | null;
|
||||
|
||||
@ManyToOne(() => FileRecord, { nullable: true })
|
||||
@JoinColumn({ name: 'stamp_file_id' })
|
||||
stampFile?: FileRecord | null;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ export class SignaturesController {
|
||||
userId,
|
||||
signerDisplayName: dto.signerDisplayName,
|
||||
signatureImageBase64: dto.signatureImageBase64,
|
||||
stampImageBase64: dto.stampImageBase64,
|
||||
});
|
||||
return this.signaturesService.getForUser(userId);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export class SignaturesRepository extends BaseRepository<SavedSignature> {
|
||||
findByUserId(userId: string): Promise<SavedSignature | null> {
|
||||
return this.repository.findOne({
|
||||
where: { userId } as never,
|
||||
relations: ['signatureFile'],
|
||||
relations: ['signatureFile', 'stampFile'],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ export interface UpsertSignatureInput {
|
||||
userId: string;
|
||||
signerDisplayName: string;
|
||||
signatureImageBase64: string;
|
||||
/** Optional company stamp/seal; omitted = keep the existing saved stamp. */
|
||||
stampImageBase64?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -31,15 +33,65 @@ export class SignaturesService {
|
||||
return {
|
||||
signerDisplayName: saved.signerDisplayName,
|
||||
signatureImageUrl: await this.inlineImageUrl(saved.signatureFile?.url),
|
||||
stampImageUrl: await this.inlineImageUrl(saved.stampFile?.url),
|
||||
};
|
||||
}
|
||||
|
||||
/** Insert or update the user's reusable signature, storing the image in MinIO. */
|
||||
/** Insert or update the user's reusable signature (and optional stamp), storing the images in MinIO. */
|
||||
async upsertForUser(input: UpsertSignatureInput): Promise<SavedSignature> {
|
||||
const buffer = this.decodeSignatureImage(input.signatureImageBase64);
|
||||
const file: Express.Multer.File = {
|
||||
fieldname: 'signature',
|
||||
originalname: `signature-${input.userId}.png`,
|
||||
// Capture the previously referenced files so we can remove them only AFTER
|
||||
// the saved_signatures row is repointed — deleting first would violate the
|
||||
// FK constraint (saved_signatures.*_file_id -> files.id).
|
||||
const existing = await this.signaturesRepository.findByUserId(input.userId);
|
||||
const previousFileId = existing?.signatureFileId ?? null;
|
||||
const previousStampFileId = existing?.stampFileId ?? null;
|
||||
|
||||
const fileRecord = await this.filesService.upload({
|
||||
resourceId: input.userId,
|
||||
resource: 'saved_signatures',
|
||||
code: 'signature',
|
||||
file: this.toUploadFile('signature', input.userId, input.signatureImageBase64),
|
||||
});
|
||||
|
||||
const stampRecord = input.stampImageBase64
|
||||
? await this.filesService.upload({
|
||||
resourceId: input.userId,
|
||||
resource: 'saved_signatures',
|
||||
code: 'stamp',
|
||||
file: this.toUploadFile('stamp', input.userId, input.stampImageBase64),
|
||||
})
|
||||
: null;
|
||||
|
||||
const saved = await this.signaturesRepository.upsert({
|
||||
userId: input.userId,
|
||||
signerDisplayName: input.signerDisplayName,
|
||||
signatureFileId: fileRecord.id,
|
||||
// Omitted stamp keeps whatever was saved before.
|
||||
...(stampRecord ? { stampFileId: stampRecord.id } : {}),
|
||||
});
|
||||
|
||||
const staleIds = [
|
||||
previousFileId !== fileRecord.id ? previousFileId : null,
|
||||
stampRecord && previousStampFileId !== stampRecord.id
|
||||
? previousStampFileId
|
||||
: null,
|
||||
].filter((id): id is string => Boolean(id));
|
||||
if (staleIds.length) {
|
||||
await this.dataSource.getRepository(FileRecord).delete(staleIds);
|
||||
}
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
private toUploadFile(
|
||||
kind: 'signature' | 'stamp',
|
||||
userId: string,
|
||||
base64: string,
|
||||
): Express.Multer.File {
|
||||
const buffer = this.decodeSignatureImage(base64);
|
||||
return {
|
||||
fieldname: kind,
|
||||
originalname: `${kind}-${userId}.png`,
|
||||
encoding: '7bit',
|
||||
mimetype: 'image/png',
|
||||
size: buffer.length,
|
||||
@@ -49,33 +101,6 @@ export class SignaturesService {
|
||||
filename: '',
|
||||
path: '',
|
||||
};
|
||||
|
||||
// Capture the previously referenced file so we can remove it only AFTER the
|
||||
// saved_signatures row is repointed — deleting it first would violate the
|
||||
// FK constraint (saved_signatures.signature_file_id -> files.id).
|
||||
const existing = await this.signaturesRepository.findByUserId(input.userId);
|
||||
const previousFileId = existing?.signatureFileId ?? null;
|
||||
|
||||
const fileRecord = await this.filesService.upload({
|
||||
resourceId: input.userId,
|
||||
resource: 'saved_signatures',
|
||||
code: 'signature',
|
||||
file,
|
||||
});
|
||||
|
||||
const saved = await this.signaturesRepository.upsert({
|
||||
userId: input.userId,
|
||||
signerDisplayName: input.signerDisplayName,
|
||||
signatureFileId: fileRecord.id,
|
||||
});
|
||||
|
||||
if (previousFileId && previousFileId !== fileRecord.id) {
|
||||
await this.dataSource
|
||||
.getRepository(FileRecord)
|
||||
.delete({ id: previousFileId });
|
||||
}
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
private async inlineImageUrl(
|
||||
|
||||
Reference in New Issue
Block a user