From 44fd57ef2cdccbeeaef3d3584652ff7f1ef8de4c Mon Sep 17 00:00:00 2001 From: sergeyyarkov Date: Wed, 18 Mar 2026 14:52:57 +0300 Subject: [PATCH] del reports before import --- handlers/reports/syncParsed.handler.js | 4 +- repositories/index.js | 1 + repositories/report.repository.js | 30 +++++++----- repositories/uow.js | 27 ++++++++++ schemas/report.schema.js | 4 +- services/reports.service.js | 68 ++++++++++++++------------ 6 files changed, 88 insertions(+), 46 deletions(-) create mode 100644 repositories/uow.js diff --git a/handlers/reports/syncParsed.handler.js b/handlers/reports/syncParsed.handler.js index e72d589..2562734 100644 --- a/handlers/reports/syncParsed.handler.js +++ b/handlers/reports/syncParsed.handler.js @@ -10,10 +10,10 @@ import { reportsSchema } from "#schemas/report.schema.js"; * @type {import("#root/http-server/types/http-server.js").HttpRouteHandler} */ export default async ({ res, data }) => { - const { reports } = await reportsSchema.validate(data).catch((err) => { + const payload = await reportsSchema.validate(data).catch((err) => { throw new BadRequestError(err.message); }); - const rows = await reportsService.syncReportsWithDatabase(reports); + const rows = await reportsService.syncReportsWithDatabase(payload); res.sendJson({ data: { rows } }); }; diff --git a/repositories/index.js b/repositories/index.js index aa4c505..62c25bd 100644 --- a/repositories/index.js +++ b/repositories/index.js @@ -1 +1,2 @@ +export * from "./uow.js"; export * from "./report.repository.js"; diff --git a/repositories/report.repository.js b/repositories/report.repository.js index d683601..9da2ae2 100644 --- a/repositories/report.repository.js +++ b/repositories/report.repository.js @@ -1,13 +1,12 @@ -import sql from "mssql"; -import db from "#root/db.js"; import { ReportModel } from "#models/index.js"; import { ReportExecutorTable, ReportTable } from "#tables/index.js"; +import { UOW } from "./index.js"; /** @import { ReportColumns } from "#tables/report.table.js" */ export const reportRepository = { async getAll() { - const request = new sql.Request(db); + const request = UOW.request; const result = await request.query(`select * from dbo.${ReportTable.tableName};`); const reports = ReportModel.fromRecordset(result.recordset); @@ -15,18 +14,29 @@ export const reportRepository = { }, async getLast() { - const request = new sql.Request(db); + const request = UOW.request; const result = await request.query(`select top 1 * from ${ReportTable.tableName} order by id desc`); const report = ReportModel.fromRecordset(result.recordset); return report.at(0); }, + async delAll() { + await UOW.run(async () => { + const request = UOW.request; + + await request.query(`delete from ${ReportExecutorTable.tableName};`); + await request.query(`delete from ${ReportTable.tableName};`); + await request.query(`DBCC CHECKIDENT ('${ReportExecutorTable.tableName}', RESEED, 0);`); + await request.query(`DBCC CHECKIDENT ('${ReportTable.tableName}', RESEED, 0);`); + }); + }, + /** * @param {ReportColumns} entry */ async create(entry) { - const request = new sql.Request(db); + const request = UOW.request; const report = new ReportModel(entry); request.input("date", ReportTable.getColumn("date"), report.date); @@ -51,10 +61,9 @@ export const reportRepository = { /** * @param {Record>[]} links - * @param {sql.Transaction} [trx] */ - async attachExecutors(links, trx) { - const request = trx ? new sql.Request(trx) : new sql.Request(db); + async attachExecutors(links) { + const request = UOW.request; const table = ReportExecutorTable.createInstance(); for (const report of Object.values(links)) { @@ -69,10 +78,9 @@ export const reportRepository = { /** * @param {ReportColumns[]} entries - * @param {sql.Transaction} [trx] */ - async bulk(entries, trx) { - const request = trx ? new sql.Request(trx) : new sql.Request(db); + async bulk(entries) { + const request = UOW.request; const table = ReportTable.createInstance(); const lastReportId = (await this.getLast())?.id || 0; let nextAddId = typeof lastReportId === "string" ? Number.parseInt(lastReportId, 10) : lastReportId; diff --git a/repositories/uow.js b/repositories/uow.js new file mode 100644 index 0000000..8b199f1 --- /dev/null +++ b/repositories/uow.js @@ -0,0 +1,27 @@ +import { AsyncLocalStorage } from "node:async_hooks"; +import sql from "mssql"; +import db from "#root/db.js"; + +export class UOW { + /** @type {AsyncLocalStorage} */ + static #als = new AsyncLocalStorage(); + + /** @param {() => Promise} callback */ + static async run(callback) { + const trx = new sql.Transaction(db); + try { + await trx.begin(); + const ret = await this.#als.run(trx, callback); + await trx.commit(); + return ret; + } catch (error) { + await trx.rollback(); + throw error; + } + } + + static get request() { + const trx = this.#als.getStore(); + return trx ? new sql.Request(trx) : new sql.Request(db); + } +} diff --git a/schemas/report.schema.js b/schemas/report.schema.js index 2bf658f..884bc26 100644 --- a/schemas/report.schema.js +++ b/schemas/report.schema.js @@ -23,4 +23,6 @@ export const reportItemSchema = y.object({ executors: y.array(y.object({ id: y.number().required(), fullname: y.string().required() })).required() }); -export const reportsSchema = y.object({ reports: y.array(reportItemSchema).min(1).required() }).required(); +export const reportsSchema = y + .object({ reports: y.array(reportItemSchema).min(1).required(), deleteAllBeforeImport: y.boolean().required() }) + .required(); diff --git a/services/reports.service.js b/services/reports.service.js index 0259993..56ca593 100644 --- a/services/reports.service.js +++ b/services/reports.service.js @@ -1,8 +1,7 @@ import xlsx from "xlsx"; import pLimit from "p-limit"; import db from "#root/db.js"; -import mssql from "mssql"; -import { reportRepository } from "#repositories/index.js"; +import { reportRepository, UOW } from "#repositories/index.js"; import * as utilsDate from "#utils/date.js"; /** @import { reportsSchema } from "#schemas/report.schema.js" */ @@ -172,34 +171,42 @@ export const reportsService = { }, /** - * @param {InferType['reports']} inReports + * @param {InferType} payload */ - async syncReportsWithDatabase(inReports) { + async syncReportsWithDatabase(payload) { const INVALID_DATE_FALLBACK = new Date("2016-10-24"); // дата для постановки вместо неверной. (колонка `date` в таблице имеет ограничение not null) - const trx = new mssql.Transaction(db); - const reports = await reportRepository.getAll(); + const { reports: inReports, deleteAllBeforeImport } = payload; - /** Отфильтровывание дубликатов по полям. */ - const filteredInReports = inReports.filter((inputR) => { - const isDub = reports.some((dbR) => { - return ( - inputR.reason_call === dbR.reason_call && - inputR.job_description === dbR.job_description && - inputR.root_cause === dbR.root_cause && - inputR.equipment?.id == dbR.equipment_id && - inputR.applicant?.id == dbR.applicant_id - ); + /** @type {typeof payload['reports']} */ + let unImportedReports = []; + + if (!deleteAllBeforeImport) { + const reports = await reportRepository.getAll(); + + /** Отфильтровывание дубликатов по полям. */ + unImportedReports = inReports.filter((inputR) => { + const isDub = reports.some((dbR) => { + return ( + inputR.reason_call === dbR.reason_call && + inputR.job_description === dbR.job_description && + inputR.root_cause === dbR.root_cause && + inputR.equipment?.id == dbR.equipment_id && + inputR.applicant?.id == dbR.applicant_id + ); + }); + return !isDub; }); - return !isDub; - }); - if (filteredInReports.length === 0) return 0; + if (unImportedReports.length === 0) return 0; + } else { + await reportRepository.delAll(); + unImportedReports = inReports; + } /** Добавление отчетов и связей с исполнителями к ним */ - try { - await trx.begin(); + const ret = await UOW.run(async () => { const bulkResult = await reportRepository.bulk( - filteredInReports.map((r) => { + unImportedReports.map((r) => { const reportDate = new Date(r.date || ""); return { applicant_id: r.applicant?.id || null, @@ -209,22 +216,19 @@ export const reportsService = { reason_call: r.reason_call || "", root_cause: r.root_cause || "" }; - }), - trx + }) ); - const executorsLinks = filteredInReports.map((r, i) => { + const executorsLinks = unImportedReports.map((r, i) => { return Object.fromEntries([[`${bulkResult.addedRowIds[i]}`, r.executors.map((e) => e.id)]]); }); - await reportRepository.attachExecutors(executorsLinks, trx); - await trx.commit(); + await reportRepository.attachExecutors(executorsLinks); + return bulkResult.rowsAffected; - } catch (error) { - console.error(error); - await trx.rollback(); - throw error; - } + }); + + return ret; } };