Архивация списков #9

Merged
gorbenko.pavel merged 17 commits from archive into main 2026-08-19 09:07:53 +00:00
4 changed files with 32 additions and 15 deletions
Showing only changes of commit fe03b9e640 - Show all commits
@@ -16,6 +16,7 @@ import { IMyRequest } from './models/IMyRequest';
import { DataTable, IDataTableColumn } from '@tnsu/ui-kit';
import { REQUEST_STATUS, REQUEST_STATUS_CHOICES, IRequestType, StatusBadge } from '../../shared/approval';
import { onRequestsChanged } from '../../shared/requestEvents';
import { traced } from './perfTrace';
import { useRoutedQuery } from '../../shared/usePersistedQuery';
import { IPage, pageKey, pageSignature, readPage, writePage } from './pageCache';
import { Pager } from './Pager';
@@ -394,7 +395,7 @@ export const RequestsTableView: React.FC<IRequestsTableViewProps> = ({
const load = async (forceRender = false): Promise<void> => {
try {
const fresh = await fetchPage();
const fresh = await traced(`ИТОГО таблица:${view}#${page}`, fetchPage);
if (cancelled) return;
// Перерисовываем, только если данные реально изменились — иначе при
@@ -4,6 +4,7 @@ import { getLinkedTypes, hydratePendingAuthors, searchMyRequests } from './myReq
import { IMyRequest } from './models/IMyRequest';
import { IRequestType, StatusBadge } from '../../shared/approval';
import { onRequestsChanged } from '../../shared/requestEvents';
import { traced } from './perfTrace';
import { useRoutedQuery } from '../../shared/usePersistedQuery';
import { pageKey, pageSignature, readPage, writePage } from './pageCache';
import { Pager } from './Pager';
@@ -184,7 +185,7 @@ export const SearchView: React.FC<ISearchViewProps> = ({
const load = async (forceRender = false): Promise<void> => {
try {
const fresh = await searchMyRequests(context, types, debouncedQuery);
const fresh = await traced('ИТОГО поиск', () => searchMyRequests(context, types, debouncedQuery));
if (cancelled) return;
applyKnownAuthors(fresh.rows, authors.current);
const prev = readPage(cacheKey);
@@ -232,6 +232,10 @@ export interface INavCounts {
// своим запросом — но параллельно, и ровно тем же вызовом, что наполняет саму
// вкладку: иначе счётчик и список могли бы разойтись.
export async function getNavCounts(context: WebPartContext): Promise<INavCounts> {
return traced('ИТОГО getNavCounts', () => getNavCountsInner(context));
}
async function getNavCountsInner(context: WebPartContext): Promise<INavCounts> {
const types = await getLinkedTypes(context);
const [turn, rework] = await Promise.all([
@@ -352,8 +356,15 @@ async function indexApprovalsForPartition(
const sp = spForSite(target.siteUrl, context);
const list = sp.web.getList(target.listId);
const dataListInfo = await traced(`dataListTitle:${type.name}#${target.listId}`, () => list.select('Title')());
const dataTitle = (dataListInfo.Title as string) || '';
// target.title — из bulk-ответа /api/request-types/lists, уже загруженного
// при старте страницы (см. IListTarget.title): 11 партиций — это 11 лишних
// REST-запросов только за тем, что уже пришло бесплатно одним запросом.
// REST — только если реестр партиций был недоступен (see listTargetsForType),
// тогда Title взять больше неоткуда.
const dataTitle =
target.title ??
((await traced(`dataListTitle:${type.name}#${target.listId}`, () => list.select('Title')())).Title as string) ??
'';
if (!DATA_LIST_SUFFIX.test(dataTitle)) {
debug.approvalSkippedReason = `«${dataTitle}» не оканчивается на «— данные» — список согласования не ищем`;
return debug;
@@ -707,6 +718,14 @@ export interface IListTarget {
siteUrl: string;
/** Абсолютный префикс пути элементов этого списка, со слешем на конце. */
prefix: string;
/**
* Title списка данных на момент регистрации партиции (см. IListPartition.title) —
* уже пришёл бесплатно вместе с bulk-ответом /api/request-types/lists при
* старте страницы. Заполнен только когда партиция известна реестру; для
* фолбэка по RequestType.listId (реестр недоступен) — undefined, тогда
* indexApprovalsForPartition сам дочитает Title через REST.
*/
title?: string;
}
/**
@@ -731,7 +750,8 @@ export function listTargetsForType(type: IRequestType): IListTarget[] {
return partitions.map((p) => ({
listId: p.listId,
siteUrl: p.siteUrl,
prefix: `${siteOrigin(p.siteUrl).replace(/\/+$/, '')}${p.listId.replace(/\/+$/, '')}/`
prefix: `${siteOrigin(p.siteUrl).replace(/\/+$/, '')}${p.listId.replace(/\/+$/, '')}/`,
title: p.title
}));
}
@@ -3,20 +3,15 @@
* (индекс согласований, свои заявки по типу, конкретная партиция, Search),
* а не просто «страница долго грузится».
*
* Включение/выключение БЕЗ пересборки — из консоли браузера:
* window.rpPerfTrace = true // писать тайминги
* window.rpPerfTrace = false // выключить (по умолчанию)
* ВРЕМЕННО: пишет всегда, без флага-переключателя — этот модуль и все traced()
* вокруг снимаются целиком, как только разбор текущей проблемы с
* производительностью закончится (см. myRequests.service.ts, RequestsTableView.tsx,
* SearchView.tsx).
*/
function enabled(): boolean {
return !!(window as unknown as { rpPerfTrace?: boolean }).rpPerfTrace;
}
const PREFIX = '[Перф]';
/** Оборачивает async-шаг замером времени. Без включённой трассировки — просто вызывает fn(). */
/** Оборачивает async-шаг замером времени. */
export async function traced<T>(label: string, fn: () => Promise<T>): Promise<T> {
if (!enabled()) return fn();
const start = performance.now();
try {
return await fn();