From ea80b8454a26649185acca410ebf46f670889334 Mon Sep 17 00:00:00 2001 From: Yurin Andrey Date: Fri, 5 Dec 2025 13:56:07 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/misc/json/json_search.dart | 64 +++++++++++++++++++ .../profile/employee/employee_data.dart | 19 +++--- .../models/profile/student/base_edu_info.dart | 9 +++ .../models/profile/student/student_data.dart | 11 ++++ lib/core/models/profile/user_data.dart | 23 +++++++ lib/core/models/profile/user_short_info.dart | 6 ++ .../profile/profile_service_impl.dart | 38 +++++++---- .../interfaces/profile/profile_service.dart | 2 +- 8 files changed, 152 insertions(+), 20 deletions(-) create mode 100644 lib/core/misc/json/json_search.dart diff --git a/lib/core/misc/json/json_search.dart b/lib/core/misc/json/json_search.dart new file mode 100644 index 00000000..d52ec4e9 --- /dev/null +++ b/lib/core/misc/json/json_search.dart @@ -0,0 +1,64 @@ +import 'package:unn_mobile/core/misc/json/json_utils.dart'; + +T? findFirstNonNullNotesDeep(dynamic data, String key) { + if (data is! JsonMap && data is! List) { + return null; + } + + if (data is JsonMap && data.containsKey(key) && data[key] is T) { + return data[key]! as T; + } + + Iterable items; + if (data is JsonMap) { + items = data.values; + } else { + items = data; + } + + for (final item in items) { + final result = findFirstNonNullNotesDeep(item, key); + if (result != null) { + return result; + } + } + + return null; +} + +void collectFirstValues( + dynamic data, + Set keys, + JsonMap results, +) { + if (data is! JsonMap && data is! List) { + return; + } + + if (results.length == keys.length) { + return; + } + + Iterable items; + if (data is JsonMap) { + for (final key in keys) { + if (results.containsKey(key)) { + continue; + } + final value = data[key]; + if (value != null) { + results[key] = value; + } + } + items = data.values; + } else { + items = data; + } + + for (final item in items) { + collectFirstValues(item, keys, results); + if (results.length == keys.length) { + return; + } + } +} diff --git a/lib/core/models/profile/employee/employee_data.dart b/lib/core/models/profile/employee/employee_data.dart index b2478e83..b35e7e5e 100644 --- a/lib/core/models/profile/employee/employee_data.dart +++ b/lib/core/models/profile/employee/employee_data.dart @@ -7,11 +7,16 @@ import 'package:unn_mobile/core/models/profile/user_data.dart'; class _EmployeeDataJsonKeys { static const String profiles = 'profiles'; - static const String user = 'user'; static const String syncId = 'sync_id'; } class EmployeeData extends UserData { + static Set get jsonKeys => { + _EmployeeDataJsonKeys.syncId, + _EmployeeDataJsonKeys.profiles, + ...UserData.jsonKeys, + }; + final String syncId; final List profiles; @@ -25,6 +30,8 @@ class EmployeeData extends UserData { required super.phone, required super.sex, required super.notes, + required super.web, + required super.birthdate, required this.syncId, required this.profiles, }); @@ -43,6 +50,8 @@ class EmployeeData extends UserData { sex: userData.sex, photoSrc: userData.photoSrc, notes: userData.notes, + web: userData.web, + birthdate: userData.birthdate, ); @override @@ -55,13 +64,7 @@ class EmployeeData extends UserData { }; factory EmployeeData.fromJson(JsonMap json) => EmployeeData.withUserData( - userData: UserData.fromJson( - // Если сделать каст, ужасно ломается форматирование - // ignore: avoid_dynamic_calls - (json[_EmployeeDataJsonKeys.profiles]! as List)[0] - [_EmployeeDataJsonKeys.user] ?? - json, - ), + userData: UserData.fromJson(json), syncId: json[_EmployeeDataJsonKeys.syncId]! as String, profiles: [ for (final item in json[_EmployeeDataJsonKeys.profiles]! as List) diff --git a/lib/core/models/profile/student/base_edu_info.dart b/lib/core/models/profile/student/base_edu_info.dart index 488f1426..a454eca4 100644 --- a/lib/core/models/profile/student/base_edu_info.dart +++ b/lib/core/models/profile/student/base_edu_info.dart @@ -21,6 +21,15 @@ class _BaseEduInfoJsonKeys { /// форму обучения, курс, уровень образования, факультет, направление подготовки, /// учебную группу и, при наличии, специализацию. class BaseEduInfo { + static Set get jsonKeys => { + _BaseEduInfoJsonKeys.eduForm, + _BaseEduInfoJsonKeys.eduCourse, + _BaseEduInfoJsonKeys.eduLevel, + _BaseEduInfoJsonKeys.eduDirection, + _BaseEduInfoJsonKeys.eduGroup, + _BaseEduInfoJsonKeys.eduSpecialization, + _BaseEduInfoJsonKeys.faculty, + }; final String eduForm; final int eduCourse; final String eduLevel; diff --git a/lib/core/models/profile/student/student_data.dart b/lib/core/models/profile/student/student_data.dart index cf54f16c..f9f7cfa8 100644 --- a/lib/core/models/profile/student/student_data.dart +++ b/lib/core/models/profile/student/student_data.dart @@ -11,6 +11,13 @@ class _StudentDataJsonKeys { } class StudentData extends UserData { + static Set get jsonKeys => { + _StudentDataJsonKeys.eduStatus, + _StudentDataJsonKeys.eduYear, + ...BaseEduInfo.jsonKeys, + ...UserData.jsonKeys, + }; + final BaseEduInfo baseEduInfo; final String eduStatus; final int eduYear; @@ -25,6 +32,8 @@ class StudentData extends UserData { required super.phone, required super.sex, required super.notes, + required super.web, + required super.birthdate, required this.baseEduInfo, required this.eduStatus, required this.eduYear, @@ -45,6 +54,8 @@ class StudentData extends UserData { sex: userData.sex, photoSrc: userData.photoSrc, notes: userData.notes, + web: userData.web, + birthdate: userData.birthdate, ); factory StudentData.fromJson(JsonMap json) => StudentData.withUserData( diff --git a/lib/core/models/profile/user_data.dart b/lib/core/models/profile/user_data.dart index 1dc53fe1..87455528 100644 --- a/lib/core/models/profile/user_data.dart +++ b/lib/core/models/profile/user_data.dart @@ -12,15 +12,30 @@ class _UserDataJsonKeys { static const String phone = 'phone'; static const String sex = 'sex'; static const String notes = 'notes'; + static const String web = 'web'; + static const String birthdate = 'birthdate'; } class UserData extends UserShortInfo { + static Set get jsonKeys => { + _UserDataJsonKeys.id, + _UserDataJsonKeys.login, + _UserDataJsonKeys.email, + _UserDataJsonKeys.phone, + _UserDataJsonKeys.sex, + _UserDataJsonKeys.notes, + _UserDataJsonKeys.web, + _UserDataJsonKeys.birthdate, + ...UserShortInfo.profileJsonKeys, + }; final int userId; final String? login; final String? email; final String? phone; final String sex; final String? notes; + final String? web; + final DateTime? birthdate; UserData({ required super.bitrixId, @@ -32,16 +47,20 @@ class UserData extends UserShortInfo { required this.phone, required this.sex, required this.notes, + required this.web, + required this.birthdate, }); UserData.withUserShortInfo({ required UserShortInfo userShortInfo, required this.userId, required this.sex, + this.birthdate, this.login, this.email, this.phone, this.notes, + this.web, }) : super( bitrixId: userShortInfo.bitrixId, fullname: userShortInfo.fullname, @@ -58,6 +77,10 @@ class UserData extends UserShortInfo { phone: userJsonMap[_UserDataJsonKeys.phone] as String?, sex: userJsonMap[_UserDataJsonKeys.sex]! as String, notes: userJsonMap[_UserDataJsonKeys.notes] as String?, + web: userJsonMap[_UserDataJsonKeys.web] as String?, + birthdate: DateTime.tryParse( + userJsonMap[_UserDataJsonKeys.birthdate] as String? ?? '', + ), ); } diff --git a/lib/core/models/profile/user_short_info.dart b/lib/core/models/profile/user_short_info.dart index e26e1bde..daff11e2 100644 --- a/lib/core/models/profile/user_short_info.dart +++ b/lib/core/models/profile/user_short_info.dart @@ -73,6 +73,12 @@ class UserShortInfo BlogPostJsonSerializable, MessageJsonSerializable, ProfileJsonSerializable { + static const _profileJsonKeys = _ProfileUserInfoKeys(); + static Set get profileJsonKeys => { + _profileJsonKeys.fullname, + _profileJsonKeys.id, + _profileJsonKeys.photoSrc, + }; final int? bitrixId; final String? fullname; final String? photoSrc; diff --git a/lib/core/services/implementations/profile/profile_service_impl.dart b/lib/core/services/implementations/profile/profile_service_impl.dart index cbdbd4ae..36d4f353 100644 --- a/lib/core/services/implementations/profile/profile_service_impl.dart +++ b/lib/core/services/implementations/profile/profile_service_impl.dart @@ -8,6 +8,7 @@ import 'package:unn_mobile/core/constants/profiles_strings.dart'; import 'package:unn_mobile/core/misc/dio_interceptor/response_data_type.dart'; import 'package:unn_mobile/core/misc/dio_interceptor/response_type_interceptor.dart'; import 'package:unn_mobile/core/misc/dio_options_factory/options_with_expected_type_factory.dart'; +import 'package:unn_mobile/core/misc/json/json_search.dart'; import 'package:unn_mobile/core/misc/json/json_utils.dart'; import 'package:unn_mobile/core/models/profile/employee/employee_data.dart'; import 'package:unn_mobile/core/models/profile/student/student_data.dart'; @@ -40,31 +41,32 @@ class ProfileServiceImpl implements ProfileService { return null; } - final data = response.data as JsonMap; - final userType = data[ProfilesStrings.type] ?? - ((data[ProfilesStrings.profilesKey]! as List)[0] + final responseData = response.data as JsonMap; + final userType = responseData[ProfilesStrings.type] ?? + ((responseData[ProfilesStrings.profilesKey]! as List)[0] as JsonMap)[ProfilesStrings.type]; UserData? userData; try { userData = switch (userType) { - ProfilesStrings.student => StudentData.fromJson(response.data), - ProfilesStrings.employee => EmployeeData.fromJson(response.data), - _ => UserData.fromJson(response.data), + ProfilesStrings.student => + StudentData.fromJson(_getStudentJson(responseData)), + ProfilesStrings.employee => + EmployeeData.fromJson(_getEmployeeJson(responseData)), + _ => UserData.fromJson(_getUserJson(responseData)), }; } catch (error, stackTrace) { _loggerService.logError( error, stackTrace, - information: [response.data.toString()], + information: [responseData.toString()], ); } - return userData; } @override - Future getProfileByBitrixId(int bitrixId) async { + Future getProfileByBitrixId({required int bitrixId}) async { final userId = await _getUserIdByBitrixId(bitrixId: bitrixId); if (userId == null) { return null; @@ -74,11 +76,11 @@ class ProfileServiceImpl implements ProfileService { @override Future getProfileByAuthorId({required int authorId}) => - getProfileByBitrixId(authorId); + getProfileByBitrixId(bitrixId: authorId); @override Future getProfileByDialogId({required int dialogId}) => - getProfileByBitrixId(dialogId); + getProfileByBitrixId(bitrixId: dialogId); Future _getUserIdByBitrixId({required int bitrixId}) async { final path = @@ -108,4 +110,18 @@ class ProfileServiceImpl implements ProfileService { return id; } + + JsonMap _extractJson(JsonMap json, Set keys) { + final JsonMap results = {}; + collectFirstValues(json, keys, results); + return results; + } + + JsonMap _getStudentJson(JsonMap json) => + _extractJson(json, StudentData.jsonKeys); + + JsonMap _getEmployeeJson(JsonMap json) => + _extractJson(json, EmployeeData.jsonKeys); + + JsonMap _getUserJson(JsonMap json) => _extractJson(json, UserData.jsonKeys); } diff --git a/lib/core/services/interfaces/profile/profile_service.dart b/lib/core/services/interfaces/profile/profile_service.dart index e69c07b2..18756804 100644 --- a/lib/core/services/interfaces/profile/profile_service.dart +++ b/lib/core/services/interfaces/profile/profile_service.dart @@ -23,7 +23,7 @@ abstract interface class ProfileService { /// Возвращает [StudentData] или [EmployeeData] — наследников [UserData], или null, если: /// 1. Не удалось получить userId по bitrixId (ошибка API, отсутствие id в ответе) /// 2. Не вышло получить профиль по найденному userId (ошибка API, некорректный ответ, ошибка декодирования) - Future getProfileByBitrixId(int bitrixId); + Future getProfileByBitrixId({required int bitrixId}); /// Получает профиль по id автора поста или комменатрия /// From 979f227f049cb91f44132ac9aa6981fe6c9681b1 Mon Sep 17 00:00:00 2001 From: UNN MOBILE runner Date: Fri, 5 Dec 2025 10:57:36 +0000 Subject: [PATCH 2/3] Change version in pubspec --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 126406a6..5f8ee076 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: unn_mobile description: A mobile application for UNN Portal website publish_to: 'none' -version: 0.6.0+375 +version: 0.6.0+376 environment: sdk: '>=3.1.2 <4.0.0' From 72573a6c2eec268f50e7c0219784db885b7d64c8 Mon Sep 17 00:00:00 2001 From: UNN MOBILE runner Date: Fri, 5 Dec 2025 10:57:52 +0000 Subject: [PATCH 3/3] Add license headers to files --- lib/core/misc/json/json_search.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/misc/json/json_search.dart b/lib/core/misc/json/json_search.dart index d52ec4e9..3148f268 100644 --- a/lib/core/misc/json/json_search.dart +++ b/lib/core/misc/json/json_search.dart @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 BitCodersNN + import 'package:unn_mobile/core/misc/json/json_utils.dart'; T? findFirstNonNullNotesDeep(dynamic data, String key) {