feat: add reconciliation and quality reporting

This commit is contained in:
hectorzhao
2026-07-15 14:22:50 +08:00
parent 16311546af
commit 8c3336600e
32 changed files with 1730 additions and 200 deletions
+56 -5
View File
@@ -53,6 +53,13 @@ export interface CreateDrainageFieldDto {
description?: string;
}
export interface CreateCommonReportFieldDto {
drainageFieldId: string;
reportType: 'signature' | 'drainage';
required?: boolean;
sortOrder?: number;
}
export interface DictionaryStatusDto {
status?: string;
operatorId?: string;
@@ -264,10 +271,14 @@ export class DictionariesService {
async listDrainageFields() {
const fields = await this.prisma.drainageField.findMany({
include: { _count: { select: { channelReportFields: true } } },
include: { _count: { select: { channelReportFields: true, commonReportFields: true } } },
orderBy: { createdAt: 'desc' },
});
return fields.map(({ _count, ...field }) => ({ ...field, usageCount: _count.channelReportFields }));
return fields.map(({ _count, ...field }) => ({
...field,
usageCount: _count.channelReportFields,
commonUsageCount: _count.commonReportFields,
}));
}
createDrainageField(data: CreateDrainageFieldDto) {
@@ -291,13 +302,53 @@ export class DictionariesService {
}
async deleteDrainageField(id: string) {
const usageCount = await this.prisma.channelReportField.count({ where: { drainageFieldId: id } });
if (usageCount > 0) {
throw new BadRequestException(`该字段已被 ${usageCount} 个通道使用,不能删除`);
const [usageCount, commonUsageCount] = await Promise.all([
this.prisma.channelReportField.count({ where: { drainageFieldId: id } }),
this.prisma.commonReportField.count({ where: { drainageFieldId: id } }),
]);
if (usageCount > 0 || commonUsageCount > 0) {
throw new BadRequestException(`该字段已被 ${usageCount} 个通道和 ${commonUsageCount} 个通用配置使用,不能删除`);
}
return this.prisma.drainageField.delete({ where: { id } });
}
listCommonReportFields() {
return this.prisma.commonReportField.findMany({
include: { drainageField: true },
orderBy: [{ reportType: 'asc' }, { sortOrder: 'asc' }, { createdAt: 'asc' }],
});
}
async createCommonReportField(data: CreateCommonReportFieldDto) {
if (data.reportType !== 'signature' && data.reportType !== 'drainage') {
throw new BadRequestException('reportType must be signature or drainage');
}
const field = await this.prisma.drainageField.findUnique({ where: { id: data.drainageFieldId } });
if (!field || field.status !== 'active') {
throw new BadRequestException('报备字段库字段不存在或已停用');
}
const existing = await this.prisma.commonReportField.findUnique({
where: { drainageFieldId_reportType: { drainageFieldId: field.id, reportType: data.reportType } },
});
if (existing) {
throw new BadRequestException('该字段已配置为对应类型的通用字段');
}
return this.prisma.commonReportField.create({
data: {
drainageFieldId: field.id,
reportType: data.reportType,
required: data.required ?? field.required,
sortOrder: data.sortOrder ?? 100,
status: 'active',
},
include: { drainageField: true },
});
}
deleteCommonReportField(id: string) {
return this.prisma.commonReportField.delete({ where: { id } });
}
private writeOperationLog(userId: string | undefined, action: string, resource: string, resourceId: string, detail: Record<string, unknown>) {
return this.prisma.operationLog.create({
data: {