fix(planting-service): 签名后查看合同返回已签名的PDF
- 修改 /tasks/:orderNo/pdf 接口,检查任务状态 - 如果已签名且有 signedPdfUrl,从 MinIO 下载已签名的 PDF - 添加 downloadSignedPdf 方法到 MinioStorageService - 在 ContractSigningTaskDto 中添加 signedPdfUrl 字段 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
86103e4c4d
commit
666be6ea60
|
|
@ -428,18 +428,23 @@ export class ContractSigningController {
|
||||||
throw new Error('签署任务不存在');
|
throw new Error('签署任务不存在');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化签署日期
|
let pdfBuffer: Buffer;
|
||||||
const signingDate = new Date().toISOString().split('T')[0];
|
|
||||||
|
|
||||||
// 生成 PDF(使用 pdf-lib 直接操作 PDF 模板)
|
// 如果已签名且有签名PDF URL,从MinIO下载已签名的PDF
|
||||||
const pdfBuffer = await this.pdfGeneratorService.generateContractPdf({
|
if (task.status === 'SIGNED' && task.signedPdfUrl) {
|
||||||
contractNo: task.contractNo,
|
this.logger.log(`Downloading signed PDF from MinIO for order ${orderNo}`);
|
||||||
userRealName: task.userRealName || '未认证',
|
try {
|
||||||
userIdCard: task.userIdCardNumber || '',
|
pdfBuffer = await this.minioStorageService.downloadSignedPdf(task.signedPdfUrl);
|
||||||
userPhone: task.userPhoneNumber || '',
|
this.logger.log(`Signed PDF downloaded: ${pdfBuffer.length} bytes`);
|
||||||
treeCount: task.treeCount,
|
} catch (downloadError) {
|
||||||
signingDate,
|
this.logger.warn(`Failed to download signed PDF, regenerating: ${downloadError.message}`);
|
||||||
});
|
// 下载失败时回退到重新生成
|
||||||
|
pdfBuffer = await this.regeneratePreviewPdf(task);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 未签名,生成预览PDF
|
||||||
|
pdfBuffer = await this.regeneratePreviewPdf(task);
|
||||||
|
}
|
||||||
|
|
||||||
// 设置响应头
|
// 设置响应头
|
||||||
res.set({
|
res.set({
|
||||||
|
|
@ -450,8 +455,29 @@ export class ContractSigningController {
|
||||||
|
|
||||||
return new StreamableFile(pdfBuffer);
|
return new StreamableFile(pdfBuffer);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Failed to generate PDF for order ${orderNo}:`, error);
|
this.logger.error(`Failed to get PDF for order ${orderNo}:`, error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成预览PDF(不含签名)
|
||||||
|
*/
|
||||||
|
private async regeneratePreviewPdf(task: {
|
||||||
|
contractNo: string;
|
||||||
|
userRealName?: string;
|
||||||
|
userIdCardNumber?: string;
|
||||||
|
userPhoneNumber?: string;
|
||||||
|
treeCount: number;
|
||||||
|
}): Promise<Buffer> {
|
||||||
|
const signingDate = new Date().toISOString().split('T')[0];
|
||||||
|
return this.pdfGeneratorService.generateContractPdf({
|
||||||
|
contractNo: task.contractNo,
|
||||||
|
userRealName: task.userRealName || '未认证',
|
||||||
|
userIdCard: task.userIdCardNumber || '',
|
||||||
|
userPhone: task.userPhoneNumber || '',
|
||||||
|
treeCount: task.treeCount,
|
||||||
|
signingDate,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ export interface ContractSigningTaskDto {
|
||||||
scrolledToBottomAt?: Date;
|
scrolledToBottomAt?: Date;
|
||||||
acknowledgedAt?: Date;
|
acknowledgedAt?: Date;
|
||||||
signedAt?: Date;
|
signedAt?: Date;
|
||||||
|
signedPdfUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -466,6 +467,7 @@ export class ContractSigningService {
|
||||||
scrolledToBottomAt: task.scrolledToBottomAt,
|
scrolledToBottomAt: task.scrolledToBottomAt,
|
||||||
acknowledgedAt: task.acknowledgedAt,
|
acknowledgedAt: task.acknowledgedAt,
|
||||||
signedAt: task.signedAt,
|
signedAt: task.signedAt,
|
||||||
|
signedPdfUrl: task.signedPdfUrl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,45 @@ export class MinioStorageService implements OnModuleInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载已签署的合同 PDF
|
||||||
|
* @param signedPdfUrl PDF 文件的公开 URL
|
||||||
|
* @returns PDF 文件 Buffer
|
||||||
|
*/
|
||||||
|
async downloadSignedPdf(signedPdfUrl: string): Promise<Buffer> {
|
||||||
|
try {
|
||||||
|
// 从 URL 中提取对象名称
|
||||||
|
const objectName = this.extractObjectName(signedPdfUrl);
|
||||||
|
|
||||||
|
const dataStream = await this.minioClient.getObject(this.bucketName, objectName);
|
||||||
|
|
||||||
|
// 将流转换为 Buffer
|
||||||
|
const chunks: Buffer[] = [];
|
||||||
|
for await (const chunk of dataStream) {
|
||||||
|
chunks.push(Buffer.from(chunk));
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = Buffer.concat(chunks);
|
||||||
|
this.logger.log(`Downloaded signed PDF: ${objectName}, size: ${buffer.length} bytes`);
|
||||||
|
return buffer;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to download signed PDF from ${signedPdfUrl}: ${error.message}`);
|
||||||
|
throw new Error(`已签署合同下载失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 URL 中提取对象名称
|
||||||
|
*/
|
||||||
|
private extractObjectName(objectUrl: string): string {
|
||||||
|
// URL 格式: https://minio.xxx.com/contracts/contracts/orderNo/signed-contract-xxx.pdf
|
||||||
|
// 需要提取: contracts/orderNo/signed-contract-xxx.pdf
|
||||||
|
const url = new URL(objectUrl);
|
||||||
|
const pathParts = url.pathname.split('/').filter(Boolean);
|
||||||
|
// 第一个是桶名,后面的是对象路径
|
||||||
|
return pathParts.slice(1).join('/');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除文件
|
* 删除文件
|
||||||
* @param objectUrl 文件的公开 URL
|
* @param objectUrl 文件的公开 URL
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue