feat: enhance operations dashboard and reporting controls
This commit is contained in:
@@ -63,6 +63,8 @@ export interface CreateDrainageFieldDto {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export type UpdateDrainageFieldDto = CreateDrainageFieldDto;
|
||||
|
||||
export interface UpsertDrainageDetectionRuleDto {
|
||||
code: string;
|
||||
name: string;
|
||||
@@ -87,6 +89,11 @@ export interface CreateCommonReportFieldDto {
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
export interface ReorderCommonReportFieldsDto {
|
||||
reportType: 'signature' | 'drainage';
|
||||
ids: string[];
|
||||
}
|
||||
|
||||
export interface DictionaryStatusDto {
|
||||
status?: string;
|
||||
operatorId?: string;
|
||||
@@ -399,6 +406,58 @@ export class DictionariesService {
|
||||
});
|
||||
}
|
||||
|
||||
async updateDrainageField(id: string, data: UpdateDrainageFieldDto, operatorId?: string) {
|
||||
const code = data.code?.trim();
|
||||
const name = data.name?.trim();
|
||||
if (!code || !/^[A-Za-z0-9]+$/.test(code)) {
|
||||
throw new BadRequestException('code must contain only Arabic numerals and English letters');
|
||||
}
|
||||
if (!name) throw new BadRequestException('name is required');
|
||||
if (!['string', 'image', 'file'].includes(data.fieldType)) {
|
||||
throw new BadRequestException('fieldType must be string, image or file');
|
||||
}
|
||||
const existing = await this.prisma.drainageField.findUnique({ where: { id } });
|
||||
if (!existing) throw new NotFoundException('报备字段不存在');
|
||||
const [usageCount, commonUsageCount] = await Promise.all([
|
||||
this.prisma.channelReportField.count({ where: { drainageFieldId: id, channel: { status: { not: 'deleted' } } } }),
|
||||
this.prisma.commonReportField.count({ where: { drainageFieldId: id } }),
|
||||
]);
|
||||
if ((usageCount > 0 || commonUsageCount > 0) && (code !== existing.code || data.fieldType !== existing.fieldType)) {
|
||||
throw new BadRequestException('字段已被引用,只能修改名称和说明,不能修改字段代码或类型');
|
||||
}
|
||||
try {
|
||||
return await this.prisma.$transaction(async (tx) => {
|
||||
const updated = await tx.drainageField.update({
|
||||
where: { id },
|
||||
data: {
|
||||
code,
|
||||
name,
|
||||
fieldType: data.fieldType,
|
||||
description: data.description?.trim() || null,
|
||||
},
|
||||
});
|
||||
await tx.operationLog.create({
|
||||
data: {
|
||||
userId: operatorId,
|
||||
action: 'drainage_field.update',
|
||||
resource: 'drainage_field',
|
||||
resourceId: id,
|
||||
detail: {
|
||||
before: { code: existing.code, name: existing.name, fieldType: existing.fieldType, description: existing.description },
|
||||
after: { code: updated.code, name: updated.name, fieldType: updated.fieldType, description: updated.description },
|
||||
} as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
return updated;
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002') {
|
||||
throw new ConflictException('字段代码已存在');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteDrainageField(id: string) {
|
||||
const [usageCount, commonUsageCount] = await Promise.all([
|
||||
this.prisma.channelReportField.count({
|
||||
@@ -541,6 +600,40 @@ export class DictionariesService {
|
||||
});
|
||||
}
|
||||
|
||||
async reorderCommonReportFields(data: ReorderCommonReportFieldsDto, operatorId?: string) {
|
||||
if (!['signature', 'drainage'].includes(data?.reportType) || !Array.isArray(data?.ids) || !data.ids.length) {
|
||||
throw new BadRequestException('通用字段排序参数无效');
|
||||
}
|
||||
if (new Set(data.ids).size !== data.ids.length) throw new BadRequestException('通用字段排序不能包含重复项');
|
||||
return this.prisma.$transaction(async (tx) => {
|
||||
const existing = await tx.commonReportField.findMany({
|
||||
where: { reportType: data.reportType, status: 'active' },
|
||||
select: { id: true },
|
||||
orderBy: [{ sortOrder: 'asc' }, { createdAt: 'asc' }],
|
||||
});
|
||||
const existingIds = existing.map((field) => field.id);
|
||||
if (existingIds.length !== data.ids.length || existingIds.some((id) => !data.ids.includes(id))) {
|
||||
throw new BadRequestException('通用字段排序范围已变化,请刷新页面后重试');
|
||||
}
|
||||
for (const [index, id] of data.ids.entries()) {
|
||||
await tx.commonReportField.update({ where: { id }, data: { sortOrder: (index + 1) * 10 } });
|
||||
}
|
||||
await tx.operationLog.create({
|
||||
data: {
|
||||
userId: operatorId,
|
||||
action: 'common_report_field.reorder',
|
||||
resource: 'common_report_field',
|
||||
detail: { reportType: data.reportType, before: existingIds, after: data.ids } as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
return tx.commonReportField.findMany({
|
||||
where: { reportType: data.reportType, status: 'active' },
|
||||
include: { drainageField: true },
|
||||
orderBy: [{ sortOrder: 'asc' }, { createdAt: 'asc' }],
|
||||
});
|
||||
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
|
||||
}
|
||||
|
||||
deleteCommonReportField(id: string) {
|
||||
return this.prisma.commonReportField.delete({ where: { id } });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user