del reports before import

This commit is contained in:
Sergey Yarkov 2026-03-18 14:52:57 +03:00
parent 1f73377e33
commit 44fd57ef2c
6 changed files with 88 additions and 46 deletions

View File

@ -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 } });
};

View File

@ -1 +1,2 @@
export * from "./uow.js";
export * from "./report.repository.js";

View File

@ -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<string, Array<string | number>>[]} 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;

27
repositories/uow.js Normal file
View File

@ -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<sql.Transaction>} */
static #als = new AsyncLocalStorage();
/** @param {() => Promise<unknown>} 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);
}
}

View File

@ -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();

View File

@ -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,15 +171,20 @@ export const reportsService = {
},
/**
* @param {InferType<typeof reportsSchema>['reports']} inReports
* @param {InferType<typeof reportsSchema>} 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: inReports, deleteAllBeforeImport } = payload;
/** @type {typeof payload['reports']} */
let unImportedReports = [];
if (!deleteAllBeforeImport) {
const reports = await reportRepository.getAll();
/** Отфильтровывание дубликатов по полям. */
const filteredInReports = inReports.filter((inputR) => {
unImportedReports = inReports.filter((inputR) => {
const isDub = reports.some((dbR) => {
return (
inputR.reason_call === dbR.reason_call &&
@ -193,13 +197,16 @@ export const reportsService = {
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;
}
};