Обходной лист #7

Merged
gorbenko.pavel merged 12 commits from сlearance-form into main 2026-08-17 05:43:39 +00:00
Showing only changes of commit 53e1d58ffe - Show all commits
@@ -5,6 +5,7 @@ import { IList } from '@pnp/sp/lists';
import '@pnp/sp/items';
import '@pnp/sp/site-users/web';
import '@pnp/sp/profiles';
import '@pnp/sp/batching';
import { getImpersonatedEmail } from '../impersonation';
import { IApprovalRow } from './models/IApprovalRow';
import { IMatrixRow } from './models/IMatrixRow';
@@ -106,11 +107,31 @@ export class ApprovalListService {
);
}
for (const { a, order } of withOrder.filter((x) => !!x.a.email)) {
const approverId = await this.resolveUserId(a.email);
await this.list().items.add({
const toWrite = withOrder.filter((x) => !!x.a.email);
if (toWrite.length === 0) return;
// Резолв Id согласующих — независимые запросы (ensureUser по email),
// ждать их по очереди незачем. На длинном маршруте (десяток+ шагов) это
// было основным источником задержки наравне с самой записью строк ниже.
const approverIds = await Promise.all(toWrite.map(({ a }) => this.resolveUserId(a.email)));
// Сами строки — ОДНИМ HTTP-запросом через batch API SharePoint, а не по
// POST на строку: add() на batched-инстансе не уходит в сеть сразу, а
// копится и отправляется одним вызовом по execute().
//
// ВАЖНО: batchedList (сам список) переиспользуем, а .items достаём ЗАНОВО
// на каждой итерации — PnPjs один и тот же Items-инстанс во второй add()
// внутри одного батча не даёт («This instance is already part of a batch»,
// см. docs/concepts/batching#reuse). .getById(id) ниже в deleteForRequest
// этой проблемы не имеет — он и так создаёт новый инстанс на каждый вызов.
const [batchedSP, execute] = getSP().batched();
const batchedList = batchedSP.web.lists.getByTitle(this.listTitle);
toWrite.forEach(({ a, order }, i) => {
// void: промис от add() на batched-инстансе нарочно не ждём поштучно —
// он разрешится вместе со всеми остальными после execute() ниже.
void batchedList.items.add({
Title: a.position || a.email,
[`${FIELDS.approver}Id`]: approverId,
[`${FIELDS.approver}Id`]: approverIds[i],
[FIELDS.status]: status,
[`${FIELDS.ticket}Id`]: requestId,
[FIELDS.order]: order,
@@ -120,7 +141,8 @@ export class ApprovalListService {
[FIELDS.slaHours]: a.slaHours ?? null,
[FIELDS.askOverdueReason]: a.askOverdueReason ?? null,
});
}
});
await execute();
}
// Продвигает очередь: среди ещё не активных строк со статусом «Ждёт очереди»
@@ -344,9 +366,18 @@ export class ApprovalListService {
// Удаляет все строки согласования по заявке (при возврате/повторной отправке).
public async deleteForRequest(requestId: number): Promise<void> {
const rows = await this.getForRequest(requestId);
for (const row of rows) {
await this.list().items.getById(row.id).delete();
}
if (rows.length === 0) return;
// writeRoute вызывает это перед перезаписью (пересохранение черновика,
// повторная отправка) — тот же per-row POST/DELETE в цикле, что и
// раньше в createSteps, и то же решение: один batch вместо N запросов.
const [batchedSP, execute] = getSP().batched();
const batchedItems = batchedSP.web.lists.getByTitle(this.listTitle).items;
rows.forEach((row) => {
// void — по той же причине, что и в createSteps выше.
void batchedItems.getById(row.id).delete();
});
await execute();
}
// Id текущего пользователя на сайте (для сравнения с полем «Согласующий»).