TUService
Спільний модуль project-application-api для роботи з ProjectTU.
Задача
- Тримати єдине frontend API для списків і partner-профілів TU.
- Фільтрувати TU за роллю та доступами користувача.
- Призначати й знімати KM, тімліда та operator для однієї або кількох TU.
- Тримати спільні правила призначень один раз для всіх проєктів.
- Викликати Project API для partner-операцій і окремі post-effects для ще не перенесених legacy-логік.
API
Поточний API project-application-api:
| № | Операція | Endpoint | Стан |
|---|---|---|---|
| 1 | Запустити refresh TU з partner API | POST /v2/tus/refresh | ✅ |
| 2 | Змінити паролі TU | POST /v2/tus/changePasswords | ✅ |
| 3 | Призначити або зняти тімліда у зміні | POST /v2/tus/changeTeamLeads | ✅ |
| 4 | Призначити або зняти operator у зміні | POST /v2/tus/changeOperators | ✅ |
| 5 | Список TU за проєктом і доступами користувача | POST /v2/tus/find | ✅ |
| 6 | Структура команди для групування TU | POST /v2/team/find | ✅ |
| 7 | Змінити поля projectData однієї TU | POST /v2/tus/updateProjectData | ✅ |
| 8 | Одна TU | POST /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 online | Golden | TU-операція проєкту |
MAX profiles per Auth Code і ліміт TU на operator | Chathouse | Налаштування проєкту і перевірка призначення |
| Доступ operator/teamlead до Client E-mail TU | Golden | Stack API |
Доступ до TU
| Роль | Які TU бачить |
|---|---|
| Director | Усі доступні TU проєкту |
| KM | Призначені цьому KM та вільні TU в його адмін панелях |
| Top manager | TU тімлідів/operator зі своєї структури |
| Тімлід | TU, де його supervisorProjectId стоїть у будь-якій зміні |
| Operator | TU, де його 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 оператора.
Видимість полів
Два правила:
- Форма не залежить від ролі.
assignments.shiftsзавжди містить усі зміни в однаковому порядку. Недоступна зміна приходить ізvisible: falseі порожніми призначеннями, тому «не твоя зміна» і «зміна вільна» розрізняються. projectIdіstackIdкористувача повертаються лише тому, хто може цим призначенням діяти. Інакше залишається тільки підпис (name,nickname).
| Поле | Director | Top manager | KM | Тімлід | Operator |
|---|---|---|---|---|---|
profile, status, partnerIds, stackId | ✅ | ✅ | ✅ | ✅ | ✅ |
capabilities, projectData | ✅ | ✅ | ✅ | ✅ | ✅ |
adminId | ✅ | ✅ | ✅ | ❌ | ❌ |
assignments.clientManager | ID | ID | ID | підпис | ❌ |
| Своя зміна | — | — | — | ID | підпис |
| Чужі зміни | ID¹ | ID¹ | ID¹ | visible: false | visible: false |
credentials | окремий запит | окремий запит | окремий запит | окремий запит | ❌ |
¹ ID видаються лише там, де роль може змінити призначення: top manager — для тімлідів своєї структури та вільних змін, KM — лише коли TU закріплена за ним. Інакше зміна видима, але приходить підписом.
Матриця живе в одному місці — TUAccessPolicy у project-application-api. Зміна бізнес-правила править цю таблицю й політику, а не endpoint і не frontend.
Порівняно з legacy getAll це змінює два місця: оператор більше не отримує familyId/stackId тімлідів своєї TU, і паролі TU не потрапляють у список для жодної ролі.
Правила призначень
- Тімліда можна призначити лише у вільну або явно передану поточну зміну.
- TU має
3зміни у всіх проєктах. - Тімлід і TU повинні належати до однієї доступної адмін панелі.
- KM може змінювати тімліда лише коли
assignments.clientManagerProjectIdдорівнює Project ID цього KM. - Top manager може призначати лише тімлідів зі своєї структури.
- Operator призначається в конкретну зміну і повинен належати тімліду цієї зміни.
- При знятті тімліда
operatorProjectIdу цій самій зміні також очищається. - Зміна перевіряє передані поточні значення і записується атомарно. Застарілий frontend state повертає conflict, а не перезаписує нове призначення.
- Після зміни пишеться assignment log. Canonical log collections і поля ще узгоджуються.
- Tasks, Electron
SET_LADY/DROP_LADYта інші post-effects повинні мати одного owner. До їх перенесення V2 делегує їх legacy Golden через versioned command; після перенесення command вимикається.
Порядок реалізації TU
- Проєктний сервіс
project-*отримує типізований partner response для активних адмін панелей і повертає його без canonical-конвертації. - Адаптер проєкту у
project-application-apiнормалізує response у snapshot. TUSyncServiceпорівнює snapshot ізproject_tus; repository записує лише створення та фактичні зміни status/admin/profile.- TUService віддає canonical список нашому frontend після реалізації read endpoint.
- Stack синхронізує KM під час зміни зв’язку TU Card.
- Director або KM призначає тімліда у конкретну зміну.
- Тімлід призначає operator у ту саму зміну.
- Frontend та Electron отримують лише TU, доступні поточному користувачу.