tu-service

TUService

Спільний модуль project-application-api для роботи з ProjectTU.

Задача

  1. Тримати єдине frontend API для списків і partner-профілів TU.
  2. Фільтрувати TU за роллю та доступами користувача.
  3. Призначати й знімати KM, тімліда та operator для однієї або кількох TU.
  4. Тримати спільні правила призначень один раз для всіх проєктів.
  5. Викликати Project API для partner-операцій і окремі post-effects для ще не перенесених legacy-логік.

API

Поточний API project-application-api:

№ОпераціяEndpointСтан
1Запустити refresh TU з partner APIPOST /v2/tus/refresh✅
2Змінити паролі TUPOST /v2/tus/changePasswords✅
3Призначити або зняти тімліда у змініPOST /v2/tus/changeTeamLeads✅
4Призначити або зняти operator у змініPOST /v2/tus/changeOperators✅
5Список TU за проєктом і доступами користувачаPOST /v2/tus/find✅
6Структура команди для групування TUPOST /v2/team/find✅
7Змінити поля projectData однієї TUPOST /v2/tus/updateProjectData✅
8Одна TUPOST /v2/tus/findById⬜

Password request приймає tuIds[]. Assignment request приймає changes[] із tuId, current і candidate, щоб застарілий frontend не перезаписав нове призначення іншого користувача. Batch продовжує обробку після помилки окремої TU та повертає succeeded[]/failed[].

Публічного endpoint для KM немає. Сервісний метод синхронізує clientManagerProjectId зі Stack TU Card. Системний detach під час refresh знімає лише тімліда/operator і не змінює KM.

POST /v2/tus/refresh отримує типізований response проєктного сервісу, нормалізує його через adapter у project-application-api, порівнює snapshot із project_tus і записує лише фактичні зміни. Зміна пароля викликає Project Admin API.

POST /v2/tus/updateProjectData приймає лише поля, дозволені для проєкту цієї TU, і лише від ролі, якій дозволене саме це поле: сьогодні це findNew у Golden для top manager і директора. Решта projectData — системні поля sync, і через API вони не пишуться.

Право перевіряється на кожному полі окремо, бо гвард на весь endpoint для цього замалий: поля різних проєктів мають різних власників. Таблиця PROJECT_DATA_FIELDS живе поруч із матрицею доступу, і ролі самого endpoint рахуються з неї, щоб гвард не розходився з правилами. Новий проєкт або нове поле додає рядок, а не endpoint.

Після запису findNew платформа шле факт в Electron Gateway (tu-project-data), а той оновлює програму оператора — див. post-effects Electron.

Stack TU Card

TU Card залишається сутністю Stack і не входить у ProjectTU.profile. ProjectTU.profile містить partner-профіль конкретної TU, а Stack Card — нашу внутрішню картку людини, яка може бути пов’язана з TU різних проєктів.

Сторінка TU Card отримує пов’язані TU через API TUService. Дані й логіка самої картки залишаються у Stack.

Додаткові операції TU

Поки не виносимо їх в окремі модулі:

ОпераціяДеВласник логіки
Find New — дозвіл TU працювати із sender onlineGoldenTU-операція проєкту
MAX profiles per Auth Code і ліміт TU на operatorChathouseНалаштування проєкту і перевірка призначення
Доступ operator/teamlead до Client E-mail TUGoldenStack API

Доступ до TU

РольЯкі TU бачить
DirectorУсі доступні TU проєкту
KMПризначені цьому KM та вільні TU в його адмін панелях
Top managerTU тімлідів/operator зі своєї структури
ТімлідTU, де його supervisorProjectId стоїть у будь-якій зміні
OperatorTU, де його operatorProjectId стоїть у будь-якій зміні

Вільна TU у списку KM ще не належить цьому KM. Перед призначенням тімліда KM спочатку має призначити TU собі.

Читання списку

POST /v2/tus/find віддає одну форму документа всім ролям. Роль впливає на те, які TU потрапляють у вибірку і які поля заповнені, але не на структуру відповіді.

Інших параметрів немає свідомо. Пагінації теж: списки беруться цілим скоупом, а сортує й фільтрує їх frontend. Фільтри додаємо по одному, коли з’явиться сторінка, якій вони потрібні; із таблицею скоупів це два рядки на фільтр.

level і client email не входять у canonical ProjectTU — вони приєднуються зі Stack TU Card за stackId і приходять у card.

Групи команди

Сторінки сендерів, айсбрікерів, інвайтів і email media показують TU згрупованими по операторах. POST /v2/team/find віддає лише структуру команди; TU в неї не входять.

РольЩо отримує
Top managerГрупу на кожного тімліда своєї структури, у порядку структури
ТімлідОдну свою групу з його операторами
OperatorОдну групу зі своїм записом і без тімліда
{ supervisor: ProjectUserRef | null, operators: ProjectUserRef[] }[]

Frontend бере TU з POST /v2/tus/find і розкладає по групах за assignments.shifts[].operator.projectId. «Free Profiles» — це TU, у яких видима зміна має operator: null; окремого запиту чи псевдо-оператора для них немає.

Так TU серіалізується в одному місці й з одними правилами доступу. Якби групи носили TU всередині, ми мали б два формати одного документа і два місця з правилами видимості.

isOnline і sleepMode поки не повертаються — див. борг по online оператора.

Видимість полів

Два правила:

  1. Форма не залежить від ролі. assignments.shifts завжди містить усі зміни в однаковому порядку. Недоступна зміна приходить із visible: false і порожніми призначеннями, тому «не твоя зміна» і «зміна вільна» розрізняються.
  2. projectId і stackId користувача повертаються лише тому, хто може цим призначенням діяти. Інакше залишається тільки підпис (name, nickname).
ПолеDirectorTop managerKMТімлідOperator
profile, status, partnerIds, stackId✅✅✅✅✅
capabilities, projectData✅✅✅✅✅
adminId✅✅✅❌❌
assignments.clientManagerIDIDIDпідпис❌
Своя зміна———IDпідпис
Чужі зміниID¹ID¹ID¹visible: falsevisible: false
credentialsокремий запитокремий запитокремий запитокремий запит❌

¹ ID видаються лише там, де роль може змінити призначення: top manager — для тімлідів своєї структури та вільних змін, KM — лише коли TU закріплена за ним. Інакше зміна видима, але приходить підписом.

Матриця живе в одному місці — TUAccessPolicy у project-application-api. Зміна бізнес-правила править цю таблицю й політику, а не endpoint і не frontend.

Порівняно з legacy getAll це змінює два місця: оператор більше не отримує familyId/stackId тімлідів своєї TU, і паролі TU не потрапляють у список для жодної ролі.

Правила призначень

  1. Тімліда можна призначити лише у вільну або явно передану поточну зміну.
  2. TU має 3 зміни у всіх проєктах.
  3. Тімлід і TU повинні належати до однієї доступної адмін панелі.
  4. KM може змінювати тімліда лише коли assignments.clientManagerProjectId дорівнює Project ID цього KM.
  5. Top manager може призначати лише тімлідів зі своєї структури.
  6. Operator призначається в конкретну зміну і повинен належати тімліду цієї зміни.
  7. При знятті тімліда operatorProjectId у цій самій зміні також очищається.
  8. Зміна перевіряє передані поточні значення і записується атомарно. Застарілий frontend state повертає conflict, а не перезаписує нове призначення.
  9. Після зміни пишеться assignment log. Canonical log collections і поля ще узгоджуються.
  10. Tasks, Electron SET_LADY/DROP_LADY та інші post-effects повинні мати одного owner. До їх перенесення V2 делегує їх legacy Golden через versioned command; після перенесення command вимикається.

Порядок реалізації TU

  1. Проєктний сервіс project-* отримує типізований partner response для активних адмін панелей і повертає його без canonical-конвертації.
  2. Адаптер проєкту у project-application-api нормалізує response у snapshot.
  3. TUSyncService порівнює snapshot із project_tus; repository записує лише створення та фактичні зміни status/admin/profile.
  4. TUService віддає canonical список нашому frontend після реалізації read endpoint.
  5. Stack синхронізує KM під час зміни зв’язку TU Card.
  6. Director або KM призначає тімліда у конкретну зміну.
  7. Тімлід призначає operator у ту саму зміну.
  8. Frontend та Electron отримують лише TU, доступні поточному користувачу.