From 6997a65c96557034075e0f8e75b641666f637f4d Mon Sep 17 00:00:00 2001 From: jingyun <> Date: Tue, 8 Sep 2026 17:42:17 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E5=AF=B9=E9=BD=90=E9=87=91?= =?UTF-8?q?=E5=80=BC=EF=BC=8C=E4=B8=BB=E8=A6=81=E6=B3=A8=E9=94=80=EF=BC=8C?= =?UTF-8?q?=E4=B8=80=E9=94=AE=E7=99=BB=E5=BD=95=E7=AD=89=E6=96=B9=E9=9D=A2?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/files/yanting.jks | Bin 2714 -> 0 bytes lib/common/config/app_config.dart | 1 + lib/common/config/app_h5_urls.dart | 2 +- lib/common/config/umeng_config.dart | 9 +- .../network/kratos_error_interceptor.dart | 19 + lib/common/storage/storage_keys.dart | 4 +- lib/common/tracking/umeng_service.dart | 2 +- .../auth/application/auth_controller.dart | 159 ++- .../application/one_key_login_service.dart | 33 +- lib/features/auth/data/auth_api.dart | 166 +-- lib/features/auth/data/auth_models.dart | 8 +- lib/features/auth/data/one_key_login_api.dart | 92 -- .../presentation/delete_account_page.dart | 37 +- .../application/mall_entry_controller.dart | 15 +- .../mall/application/mall_tab_config.dart | 324 +++-- .../profile/presentation/profile_page.dart | 20 +- .../settings/presentation/settings_page.dart | 22 +- .../web/presentation/mall_web_host.dart | 2 +- lib/features/web/presentation/web_page.dart | 45 +- lib/models/pb/errors/errors.pb.dart | 113 ++ lib/models/pb/errors/errors.pbenum.dart | 11 + lib/models/pb/errors/errors.pbjson.dart | 44 + lib/models/pb/errors/errors.pbserver.dart | 14 + lib/models/pb/google/protobuf/empty.pb.dart | 58 + lib/models/pb/jinzhi/v1/account.pb.dart | 1043 +++++++++++++++++ lib/models/pb/jinzhi/v1/ads.pb.dart | 868 ++++++++++++++ lib/models/pb/jinzhi/v1/ads.pbenum.dart | 55 + lib/models/pb/jinzhi/v1/push.pb.dart | 336 ++++++ lib/models/pb/jinzhi/v1/push.pbenum.dart | 35 + pubspec.yaml | 2 +- .../kratos_error_interceptor_test.dart | 57 + test/features/auth/auth_api_test.dart | 40 + test/features/auth/auth_controller_test.dart | 216 ++++ .../mall/mall_bridge_arguments_test.dart | 13 +- .../mall/mall_tab_config_controller_test.dart | 66 ++ test/features/mall/mall_tab_config_test.dart | 22 + test/features/web/web_page_test.dart | 3 +- 37 files changed, 3442 insertions(+), 514 deletions(-) delete mode 100644 android/app/files/yanting.jks delete mode 100644 lib/features/auth/data/one_key_login_api.dart create mode 100644 lib/models/pb/errors/errors.pb.dart create mode 100644 lib/models/pb/errors/errors.pbenum.dart create mode 100644 lib/models/pb/errors/errors.pbjson.dart create mode 100644 lib/models/pb/errors/errors.pbserver.dart create mode 100644 lib/models/pb/google/protobuf/empty.pb.dart create mode 100644 lib/models/pb/jinzhi/v1/account.pb.dart create mode 100644 lib/models/pb/jinzhi/v1/ads.pb.dart create mode 100644 lib/models/pb/jinzhi/v1/ads.pbenum.dart create mode 100644 lib/models/pb/jinzhi/v1/push.pb.dart create mode 100644 lib/models/pb/jinzhi/v1/push.pbenum.dart create mode 100644 test/common/network/kratos_error_interceptor_test.dart create mode 100644 test/features/auth/auth_api_test.dart create mode 100644 test/features/auth/auth_controller_test.dart create mode 100644 test/features/mall/mall_tab_config_controller_test.dart diff --git a/android/app/files/yanting.jks b/android/app/files/yanting.jks deleted file mode 100644 index 37bf2aed3167a26a423b6df0e2a8c4b94aa032c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2714 zcma);S5(u962_B|00{&F5W4bWhf|7F4QpfuPYzvPTxk;=;P_ZBAt49TUz)PITR0m?rcFeE?% zl>cox0a+*?=#N5!Z)kPMc;><8B1jj1Z^ilt1R{lkfJFgLR`&l~gur1S;5aM%dW0VM z1`8A{14Xg;eE?D~A+?OGdwaT@;`E_3Xg~bA#m)!ud*m=mYui+Hhxw>tT}=g0_XdQ| zkaFC}Rhe2i)BLTT5;6TGW2RX(uf4=j>GJ2!7ZtdWbAupkQ$_Y2&&$<02C4h#$JoCgr(pxRe<<}A^Bxpbd5RHTyy|JJ%aXuQxoSV(vF(Yl$?XDjxH3yY)0mpCC4dN$ol>&d#bzujDnc>E0yyl0;2V#XD)VGh0>JH@_*0w*O8o zmiwq!GAuk)ym|v4@?oqqmt!q+&*FpJ6_}!D8XuSK8&@TcZiJ|;&sOGPgU#Itj=jlh zDk3&2>vnJaA!W(_Ybg3H2Hz+=8Hm?i2G*BK>U@xO_tAcr{O;$SWde_fcGV+$v$C&sTIUkJ7 zIZ-6dF)P@<-uk$hX>q=*YO52JEVKoS*Iwf%RB}_=cI!Sld;E4%Qw0jR7)zX9xOX)5 z(PGIR)=)*$iM|Q0f#M4fjC)h0G>^yU$B~9*d*ny(*u0@lf<6c)Ao=F5p8gybhLq{_SVRRBst12y}3 z=-gmP{;UyW&0Kk9Czi3Dg+q?d+jd94OF2BlUvh(}ce^zjX#r|y+jTe!;csD5(dfYV=o4>XOIygXHq+RqQpa2{z{#T7q* zuRAyIu9u#X>3WA}amTjJ)W4Ty-7BUV^p?WUj!2N1Hm5|KJlvXxH|@PfRc<77_g9gW z3nhar*XJpD+!hz-7IO5siOF8`r{4;!pmy&~bREPRw57g3S)D&FQ`>huB)FWAo)D!< zk5|+Is;T17G`<+^S+S2TbZ)_y&a|u+4GUAS#UkFf0~24Z+P9R1bl1-*xMHt7Y9TIP z%;77YwS%ID2~nu==Ul5fnv5EUjt-}07stDVagC07RGYw;P_TD6g8>nqK8E?!h(Yx101%$L#_Oxa=ofp~#>`D$(0ijE9&$^JFx zS0_gU-I#`BV_5^+A6L|!-tgwVyf!FF8O$aiDW_Rm! zv#A+y{wN$~E@bf(zfh5uRW>!!GGctIwJjR~|LDx|_!yutw-mW$tjb5HU8$V6$`!iU z>7J9~jgla*RnPI|Ft@eJWFZns-KlX2DcLTWW8%(f=ym(thCL9g)A3yCwzlF z>FI+^Wg~uI#ZQ8CUI;ao9CnKi2ngq+mNwpvC}H$qAicj zuP%u}-A>)dLm2#0U za`&+w4_cR}fq@TUp>)vw=;;0|4z@B#3Q>3Tn#E`A%oq_xid2mcW`oO|%coWo-LdSN zpdu4~WeDk3hctmy7SLUakgeEG7+4fIpAmJWzF%Yq!}C=Vicw26ELS+D>rbur(F;VT zWGBmuW|o~VC^FO<#c#)AE-n5%QOU`ih&^BOqDV_!MltB4X%ODR2PuGWG%a^H#F{Edw0DnN!@EXCNGrk1`9)j&F-JRftKvV1Mb#={j*CiO^^N`@v2zb93l2 zSweT;Y;XrWGK9Zi9z5m1c=9w`AY1yHSW>JBf1zu6*3YJ#2m4dS5T3n '$baseUrl/beginner/privacyPolicy.html'; diff --git a/lib/common/config/umeng_config.dart b/lib/common/config/umeng_config.dart index a748307..9350833 100644 --- a/lib/common/config/umeng_config.dart +++ b/lib/common/config/umeng_config.dart @@ -6,9 +6,12 @@ class UmengConfig { static String _androidAppKey = '6a27b1f46f259537c7b616be'; // Default placeholder static String _iosAppKey = '6a27b24e6f259537c7b617a8'; // Default placeholder - static String _androidPushSecret = 'xxx'; // Default placeholder - static String _androidLoginSecret = ''; - static String _iosLoginSecret = ''; + static String _androidPushSecret = + '261fc7656f0c34f4cf122a3c2c81f704'; // Default placeholder + static String _androidLoginSecret = + 'oNaEhNEwZGQHvngXI4kyxR90tavJRDHMyDDQhkQouty+SAUb66szdNQivqE99ZIQO+QyNabsyyTJ7CN8iA3vb8EgPwLF/iSUHMWpGVacHpukG1sqySo1cwbRmDpOFcdNzaO8PavGfqkkCse/oRPNFBW40LhCjSJaI/roAEVNGpepMXvufJtjnyB4OX6sav+isHU/Lsz84bcdwnzD6fFyDSyE+YqN9gjUoUOv8Ghcy3H1FXUMvw4iD9nD+tE4jODLXvvzlpVbHYknn6Y2UAj5dvGvNUszocXqGAr4j3z8naKPBhz7Liofog=='; + static String _iosLoginSecret = + 'vcutZmDFJ2HBdFk7herCz2M9dPHuoa303NPGrm8c9gkzu/Lr5njFa85y33w7htgp0cx9JCMbsk3ecrREhVd8yeq73kFFiVqgkk/BMzCQ6VG55gSATEoiRaLnqXkKvuv6X4kcnSqANMI07up5rTPdAtVQ5JGPh9ts7LUBzRZWQRnDvdX8pjPy8LKxXJKoNagz4sHd5m9MbX0o0uep2OaFFFBTOyI+g4vjK1oZkpaUnIKGU8cTH+wvL8qwp5BLtkc6XCXe5PXqClU='; static Future load() async { await dotenv.load( diff --git a/lib/common/network/kratos_error_interceptor.dart b/lib/common/network/kratos_error_interceptor.dart index 6be0acd..99c95c8 100644 --- a/lib/common/network/kratos_error_interceptor.dart +++ b/lib/common/network/kratos_error_interceptor.dart @@ -1,8 +1,27 @@ import 'package:dio/dio.dart'; +import '../../models/pb/errors/errors.pb.dart' as kerrors; + class KratosErrorInterceptor extends Interceptor { @override void onError(DioException err, ErrorInterceptorHandler handler) { + final response = err.response; + final raw = response?.data; + if (response != null && raw is List && raw.isNotEmpty) { + try { + final status = kerrors.Status.fromBuffer(raw); + if (status.code != 0 || status.reason.isNotEmpty) { + response.data = { + 'code': status.code, + 'reason': status.reason, + 'message': status.message, + 'metadata': status.metadata, + }; + } + } catch (_) { + // A non-Kratos error body keeps its original representation. + } + } handler.next(err); } } diff --git a/lib/common/storage/storage_keys.dart b/lib/common/storage/storage_keys.dart index 4688e07..03bfc9a 100644 --- a/lib/common/storage/storage_keys.dart +++ b/lib/common/storage/storage_keys.dart @@ -6,7 +6,9 @@ const storageJinzhiAmountHidden = 'jinzhi_amount_hidden'; const storageJinzhiRecycleDiscounts = 'jinzhi_recycle_discounts'; const storageJinzhiPurityPresets = 'jinzhi_purity_presets'; const storageJinzhiPriceColorMode = 'jinzhi_price_color_mode'; -const storageJinzhiMockLoggedIn = 'jinzhi_mock_logged_in'; +const storageUserId = 'jinzhi_user_id'; +const storageUserPhone = 'jinzhi_user_phone'; +const storageUserCreatedAt = 'jinzhi_user_created_at'; const storagePushPermissionPrompted = 'push_permission_prompted'; const storageMallEntryPromoOpened = 'jinzhi_mall_entry_promo_opened'; const storageMallHomeBadgeSeen = 'jinzhi_mall_home_badge_seen'; diff --git a/lib/common/tracking/umeng_service.dart b/lib/common/tracking/umeng_service.dart index 4d8a20d..d0a4c7e 100644 --- a/lib/common/tracking/umeng_service.dart +++ b/lib/common/tracking/umeng_service.dart @@ -32,7 +32,7 @@ abstract final class UmengService { // Keep the carrier-login launch path independent of UMeng Common's // network initialization. Waiting for this future is what left the login // overlay spinning forever in the base project; U-Verify is configured - // immediately below by initOneKeyLoginIfAllowed, as in Yanting. + // immediately below by initOneKeyLoginIfAllowed. unawaited( UmengCommonSdk.initCommon( UmengConfig.androidAppKey, diff --git a/lib/features/auth/application/auth_controller.dart b/lib/features/auth/application/auth_controller.dart index 7293d4c..5aaee7f 100644 --- a/lib/features/auth/application/auth_controller.dart +++ b/lib/features/auth/application/auth_controller.dart @@ -1,92 +1,143 @@ import 'package:dio/dio.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shared_preferences/shared_preferences.dart'; -import '../../../common/network/auth_interceptor.dart'; +import '../../../common/network/dio_provider.dart'; +import '../../../common/storage/secure_identity_storage.dart'; import '../../../common/storage/storage_keys.dart'; import '../../../common/storage/storage_service.dart'; -import '../../../common/network/dio_provider.dart'; -import '../data/auth_models.dart'; import '../data/auth_api.dart'; -import '../data/one_key_login_api.dart'; +import '../data/auth_models.dart'; + +const _legacyMockLoggedInKey = 'jinzhi_mock_logged_in'; final authProvider = - StateNotifierProvider>( - (ref) => AuthController(ref.read(dioProvider)), - ); + StateNotifierProvider>((ref) { + return AuthController( + ref.read(dioProvider), + identityStorage: ref.read(secureIdentityStorageProvider), + preferences: StorageService.to.preferences, + ); + }); class AuthController extends StateNotifier> { - AuthController(this._dio) : super(const AsyncLoading()) { - _bootstrap(); + AuthController( + this._dio, { + required SecureIdentityStorage identityStorage, + required SharedPreferences preferences, + bool autoBootstrap = true, + }) : _identityStorage = identityStorage, + _preferences = preferences, + super(const AsyncLoading()) { + if (autoBootstrap) _bootstrap(); } final Dio _dio; + final SecureIdentityStorage _identityStorage; + final SharedPreferences _preferences; + + AuthApi get _api => AuthApi(_dio); + + Future retryBootstrap() => _bootstrap(); Future _bootstrap() async { - final token = await loadJwt(); + state = const AsyncLoading(); + final token = await _identityStorage.readJwt(); if (token == null || token.isEmpty) { state = const AsyncData(GuestAuthState()); return; } - final phone = StorageService.to.getString(storageJinzhiMockLoggedIn) ?? ''; - state = AsyncData( - LoggedInAuthState( - user: JinzhiUser( - phone: phone.isEmpty ? '13800000000' : phone, - nickname: '金值用户', - createdAt: DateTime(2026, 6, 18), - ), - token: token, - ), - ); + try { + final user = await _api.getProfile(); + await _persistProfile(user); + state = AsyncData(LoggedInAuthState(user: user, token: token)); + } catch (error, stackTrace) { + if (_isUnauthorized(error)) { + await _clearLocalSession(); + state = const AsyncData(GuestAuthState()); + return; + } + final cached = _canUseCachedProfile(error) ? _readCachedProfile() : null; + if (cached != null) { + await _persistProfile(cached); + state = AsyncData(LoggedInAuthState(user: cached, token: token)); + } else { + state = AsyncError(error, stackTrace); + } + } } - Future sendCode(String phone) async { - await AuthApi(_dio).sendSmsCode(phone); - } + Future sendCode(String phone) => _api.sendSmsCode(phone); Future verifyCode(String phone, String code) async { - final reply = await AuthApi(_dio).verifySmsCode(phone, code); - await saveJwt(reply.accessToken); - await StorageService.to.setString(storageJinzhiMockLoggedIn, reply.phone); - state = AsyncData( - LoggedInAuthState( - user: JinzhiUser( - phone: reply.phone, - nickname: '金值用户', - createdAt: DateTime.now(), - ), - token: reply.accessToken, - ), - ); + final reply = await _api.verifySmsCode(phone, code); + await _persistLogin(reply); } - /// Test environment reuses Yanting's one-key verification endpoint until - /// 金值 has its own account contract. Future verifyOneKeyLogin(String loginToken) async { - final reply = await OneKeyLoginApi(_dio).verify(loginToken); - await saveJwt(reply.token); - await StorageService.to.setString(storageJinzhiMockLoggedIn, reply.phone); + final reply = await _api.verifyOneKeyLogin(loginToken); + await _persistLogin(reply); + } + + Future _persistLogin(AuthLoginResult reply) async { + await _identityStorage.saveJwt(reply.accessToken); + await _persistProfile(reply.user); state = AsyncData( - LoggedInAuthState( - user: JinzhiUser( - phone: reply.phone, - nickname: '金值用户', - createdAt: DateTime.now(), - ), - token: reply.token, - ), + LoggedInAuthState(user: reply.user, token: reply.accessToken), ); } Future logout() async { - await clearJwt(); - await StorageService.to.setString(storageJinzhiMockLoggedIn, ''); + await _clearLocalSession(); state = const AsyncData(GuestAuthState()); } - Future deleteAccount([String? code]) async { - await logout(); + Future deleteAccount(String code) async { + final normalized = code.trim(); + if (normalized.isEmpty) { + throw ArgumentError.value(code, 'code', 'must not be empty'); + } + await _api.deleteAccount(normalized); + await _clearLocalSession(); + state = const AsyncData(GuestAuthState()); + } + + Future _persistProfile(JinzhiUser user) async { + await _preferences.setString(storageUserId, user.id); + await _preferences.setString(storageUserPhone, user.phone); + await _preferences.setString(storageUserCreatedAt, user.createdAt); + await _preferences.remove(_legacyMockLoggedInKey); + } + + JinzhiUser? _readCachedProfile() { + final id = _preferences.getString(storageUserId)?.trim() ?? ''; + final phone = + (_preferences.getString(storageUserPhone) ?? + _preferences.getString(_legacyMockLoggedInKey) ?? + '') + .trim(); + final createdAt = + _preferences.getString(storageUserCreatedAt)?.trim() ?? ''; + if (id.isEmpty && phone.isEmpty) return null; + return JinzhiUser(id: id, phone: phone, createdAt: createdAt); + } + + Future _clearLocalSession() async { + await _identityStorage.clearJwt(); + await _preferences.remove(storageUserId); + await _preferences.remove(storageUserPhone); + await _preferences.remove(storageUserCreatedAt); + await _preferences.remove(_legacyMockLoggedInKey); } bool get isLoggedIn => state.valueOrNull is LoggedInAuthState; } + +bool _isUnauthorized(Object error) => + error is DioException && error.response?.statusCode == 401; + +bool _canUseCachedProfile(Object error) { + if (error is! DioException) return false; + final status = error.response?.statusCode; + return status == null || status == 408 || status == 429 || status >= 500; +} diff --git a/lib/features/auth/application/one_key_login_service.dart b/lib/features/auth/application/one_key_login_service.dart index cfc8f03..a6d1b88 100644 --- a/lib/features/auth/application/one_key_login_service.dart +++ b/lib/features/auth/application/one_key_login_service.dart @@ -24,6 +24,9 @@ abstract interface class OneKeyLoginGateway { void setWidgetCallback(OneKeyLoginCallback? callback); + /// 仅在用户点按一键登录后调用,预热运营商授权页。 + Future accelerate(); + Future requestLogin(UMCustomModel uiConfig); void closeAuthPage(); @@ -65,6 +68,11 @@ class UmengOneKeyLoginGateway implements OneKeyLoginGateway { } } + @override + Future accelerate() { + return UmengVerifySdk.accelerateLoginPageWithTimeout(3); + } + @override Future requestLogin(UMCustomModel uiConfig) async { await UmengVerifySdk.getLoginTokenWithTimeout(3, uiConfig); @@ -137,15 +145,25 @@ class OneKeyLoginSdkSession { return _result.future; } - // Availability checking already pre-accelerates the carrier page. Start - // the token request directly here; the SDK performs its own preflight when - // needed and reports its timeout/error through the registered callback. + // 预热必须由用户显式点击触发;不在启动或隐私同意阶段调用它。 unawaited(_startNativeLogin(uiConfig)); return _result.future; } Future _startNativeLogin(UMCustomModel uiConfig) async { try { + // 与 jinzhi-flutter-app 保持一致。未预热时 SDK 会在此处隐式预热, + // 在部分 Android 机型上更容易超时或直接回调失败。 + final preheat = await _gateway.accelerate(); + if (kDebugMode && preheat is Map && preheat['ret'] != null) { + // `ret` 是运营商预热失败码;插件原生层未将它输出到 logcat。 + // 预热失败不阻断接下来的 getLoginToken,SDK 会自行再试一次。 + debugPrint( + 'OneKeyLogin preheat failed: vendor=${preheat['vendor']}, ' + 'ret=${preheat['ret']}', + ); + } + if (_terminal) return; _gateway.setTokenCallback(_handleTokenResult); _gateway.setWidgetCallback((Object? _) {}); _gateway.setUiCallback(_handleUiResult); @@ -186,6 +204,15 @@ class OneKeyLoginSdkSession { void _handleTokenResult(Object? result) { final code = extractUmengLoginCode(result); + if (kDebugMode && !isUmengTokenSuccess(result)) { + final map = result is Map ? result : const {}; + // 本地插件原先丢弃了 onTokenFailed 的 ret;只记录诊断字段,绝不记录 token。 + debugPrint( + 'OneKeyLogin Android/iOS SDK result: code=$code, ' + 'msg=${map['msg']}, vendor=${map['vendorName']}, ' + 'carrierError=${map['carrierFailedResultData']}', + ); + } // On iOS, 700002 only means the user tapped the authorization page's // login button. It is followed by a separate 600000 (or 600xxx) token // callback, so completing the session here discards that callback. diff --git a/lib/features/auth/data/auth_api.dart b/lib/features/auth/data/auth_api.dart index 6224f09..9e8a049 100644 --- a/lib/features/auth/data/auth_api.dart +++ b/lib/features/auth/data/auth_api.dart @@ -1,14 +1,10 @@ -import 'dart:convert'; -import 'dart:typed_data'; - import 'package:dio/dio.dart'; import '../../../common/network/dio_provider.dart'; +import '../../../models/pb/jinzhi/v1/account.pb.dart' as pb; +import 'auth_models.dart'; -/// 金值账号服务的 protobuf HTTP 契约。 -/// -/// 这个轻量编解码只覆盖短信登录需要的字段,避免把整套历史生成模型带入 -/// 当前重建中的应用;字段号与金值 `account.proto` 保持一致。 +/// 金值账户的 protobuf-over-HTTP 客户端。 class AuthApi { const AuthApi(this._dio); @@ -17,124 +13,82 @@ class AuthApi { Future sendSmsCode(String phone) async { final response = await _dio.post>( '/api/v1/auth/sms/send', - // Dio may JSON-encode a List as `[10,11,...]`. The API expects - // the raw protobuf bytes, so preserve the binary payload explicitly. - data: Uint8List.fromList(_ProtoWriter.stringField(1, phone)), + data: pb.SendSmsCodeRequest(phone: phone).writeToBuffer(), options: protobufOptions(), ); - return _ProtoMessage(response.data ?? const []).integer(1); + return pb.SendSmsCodeReply.fromBuffer( + response.data ?? const [], + ).expiresIn; } Future verifySmsCode(String phone, String code) async { final response = await _dio.post>( '/api/v1/auth/sms/verify', - data: Uint8List.fromList([ - ..._ProtoWriter.stringField(1, phone), - ..._ProtoWriter.stringField(2, code), - ]), + data: pb.VerifySmsCodeRequest(phone: phone, code: code).writeToBuffer(), options: protobufOptions(), ); - final reply = _ProtoMessage(response.data ?? const []); - final accessToken = reply.string(1).trim(); - if (accessToken.isEmpty) { - throw const FormatException('登录响应缺少 access_token'); + return AuthLoginResult.fromProto( + pb.VerifySmsCodeReply.fromBuffer(response.data ?? const []), + ); + } + + Future verifyOneKeyLogin(String loginToken) async { + final response = await _dio.post>( + '/api/v1/auth/onekey/verify', + data: pb.VerifyOneKeyLoginRequest(loginToken: loginToken).writeToBuffer(), + options: protobufOptions(), + ); + return AuthLoginResult.fromProto( + pb.VerifySmsCodeReply.fromBuffer(response.data ?? const []), + ); + } + + Future getProfile() async { + final response = await _dio.get>( + '/api/v1/me', + options: protobufOptions(), + ); + final reply = pb.ProfileReply.fromBuffer(response.data ?? const []); + if (!reply.hasUser()) { + throw const FormatException('账号资料响应缺少 user'); } - final user = _ProtoMessage(reply.bytes(3)); - return AuthLoginResult( - accessToken: accessToken, - phone: user.string(2).trim().isEmpty ? phone : user.string(2).trim(), + return _userFromProto(reply.user); + } + + Future deleteAccount(String smsCode) async { + await _dio.delete>( + '/api/v1/me', + data: pb.DeleteAccountRequest(smsCode: smsCode).writeToBuffer(), + options: protobufOptions(), ); } } class AuthLoginResult { - const AuthLoginResult({required this.accessToken, required this.phone}); + const AuthLoginResult({ + required this.accessToken, + required this.expiresIn, + required this.user, + }); final String accessToken; - final String phone; -} + final int expiresIn; + final JinzhiUser user; -class _ProtoWriter { - static List stringField(int field, String value) => [ - ..._varint(field << 3 | 2), - ..._varint(utf8.encode(value).length), - ...utf8.encode(value), - ]; - - static List _varint(int value) { - final bytes = []; - var remaining = value; - while (remaining > 0x7f) { - bytes.add(remaining & 0x7f | 0x80); - remaining >>= 7; + factory AuthLoginResult.fromProto(pb.VerifySmsCodeReply reply) { + if (reply.accessToken.trim().isEmpty || !reply.hasUser()) { + throw const FormatException('登录响应缺少 access_token 或 user'); } - bytes.add(remaining); - return bytes; + return AuthLoginResult( + accessToken: reply.accessToken.trim(), + expiresIn: reply.expiresIn, + user: _userFromProto(reply.user), + ); } } -class _ProtoMessage { - _ProtoMessage(List bytes) : _fields = _parse(bytes); - - final Map _fields; - - int integer(int field) => _fields[field] is int ? _fields[field]! as int : 0; - - String string(int field) => _fields[field] is List - ? utf8.decode(_fields[field]! as List, allowMalformed: true) - : ''; - - List bytes(int field) => _fields[field] is List - ? _fields[field]! as List - : const []; - - static Map _parse(List bytes) { - final fields = {}; - var cursor = 0; - while (cursor < bytes.length) { - final tag = _readVarint(bytes, cursor); - if (tag == null) break; - cursor = tag.next; - final field = tag.value >> 3; - switch (tag.value & 7) { - case 0: - final value = _readVarint(bytes, cursor); - if (value == null) return fields; - fields[field] = value.value; - cursor = value.next; - case 2: - final length = _readVarint(bytes, cursor); - if (length == null || length.next + length.value > bytes.length) { - return fields; - } - fields[field] = bytes.sublist( - length.next, - length.next + length.value, - ); - cursor = length.next + length.value; - default: - return fields; - } - } - return fields; - } - - static _ProtoValue? _readVarint(List bytes, int cursor) { - var value = 0; - var shift = 0; - while (cursor < bytes.length && shift <= 63) { - final byte = bytes[cursor++]; - value |= (byte & 0x7f) << shift; - if (byte & 0x80 == 0) return _ProtoValue(value, cursor); - shift += 7; - } - return null; - } -} - -class _ProtoValue { - const _ProtoValue(this.value, this.next); - - final int value; - final int next; -} +JinzhiUser _userFromProto(pb.UserInfo user) => JinzhiUser( + id: user.id.trim(), + phone: user.phone.trim(), + createdAt: user.createdAt.trim(), +); diff --git a/lib/features/auth/data/auth_models.dart b/lib/features/auth/data/auth_models.dart index d5ffda4..ceeb25e 100644 --- a/lib/features/auth/data/auth_models.dart +++ b/lib/features/auth/data/auth_models.dart @@ -1,13 +1,15 @@ class JinzhiUser { const JinzhiUser({ + required this.id, required this.phone, - required this.nickname, required this.createdAt, }); + final String id; final String phone; - final String nickname; - final DateTime createdAt; + final String createdAt; + + String get nickname => '金值用户'; } sealed class AuthState { diff --git a/lib/features/auth/data/one_key_login_api.dart b/lib/features/auth/data/one_key_login_api.dart deleted file mode 100644 index 698ddce..0000000 --- a/lib/features/auth/data/one_key_login_api.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:dio/dio.dart'; - -import '../../../common/network/dio_provider.dart'; - -/// Compatible with Yanting's protobuf `/api/v1/auth/onekey/verify` contract. -class OneKeyLoginApi { - OneKeyLoginApi(this._dio); - final Dio _dio; - - Future verify(String loginToken) async { - final bytes = utf8.encode(loginToken); - final request = Uint8List.fromList([ - 0x0A, - ..._varint(bytes.length), - ...bytes, - ]); - final response = await _dio.post>( - '/api/v1/auth/onekey/verify', - data: request, - options: protobufOptions(), - ); - return _decode(Uint8List.fromList(response.data ?? const [])); - } - - OneKeyLoginReply _decode(Uint8List data) { - final fields = _fields(data); - final token = utf8.decode(fields[1] ?? const []); - final user = _fields(Uint8List.fromList(fields[3] ?? const [])); - if (token.isEmpty) throw StateError('一键登录服务未返回登录凭证'); - return OneKeyLoginReply( - token: token, - phone: utf8.decode(user[2] ?? const []), - ); - } - - Map> _fields(Uint8List data) { - final result = >{}; - var i = 0; - while (i < data.length) { - final tag = _readVarint(data, i); - i = tag.$2; - final wireType = tag.$1 & 7; - if (wireType == 0) { - i = _readVarint(data, i).$2; - continue; - } - if (wireType != 2) { - throw const FormatException('Unsupported protobuf field'); - } - final length = _readVarint(data, i); - i = length.$2; - final end = i + length.$1; - if (end > data.length) { - throw const FormatException('Invalid protobuf payload'); - } - result[tag.$1 >> 3] = data.sublist(i, end); - i = end; - } - return result; - } - - (int, int) _readVarint(Uint8List data, int index) { - var value = 0; - var shift = 0; - while (index < data.length) { - final byte = data[index++]; - value |= (byte & 0x7f) << shift; - if (byte & 0x80 == 0) return (value, index); - shift += 7; - } - throw const FormatException('Invalid protobuf varint'); - } - - List _varint(int value) { - final result = []; - while (value > 0x7f) { - result.add((value & 0x7f) | 0x80); - value >>= 7; - } - result.add(value); - return result; - } -} - -class OneKeyLoginReply { - const OneKeyLoginReply({required this.token, required this.phone}); - final String token; - final String phone; -} diff --git a/lib/features/auth/presentation/delete_account_page.dart b/lib/features/auth/presentation/delete_account_page.dart index 7267826..c55a062 100644 --- a/lib/features/auth/presentation/delete_account_page.dart +++ b/lib/features/auth/presentation/delete_account_page.dart @@ -31,7 +31,20 @@ class DeleteAccountPage extends ConsumerWidget { ), error: (e, _) => Scaffold( backgroundColor: cs.background, - body: Center(child: Text('加载失败:$e')), + body: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('账号状态加载失败'), + const SizedBox(height: 12), + FilledButton.tonal( + onPressed: () => + ref.read(authProvider.notifier).retryBootstrap(), + child: const Text('重试'), + ), + ], + ), + ), ), data: (authState) { if (authState is! LoggedInAuthState) { @@ -669,7 +682,9 @@ class _VerifySheetState extends ConsumerState<_VerifySheet> { final cs = ShadTheme.of(context).colorScheme; return Padding( // 键盘弹出时 sheet 跟随上移 - padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), child: SafeArea( child: Padding( padding: const EdgeInsets.fromLTRB(20, 8, 20, 20), @@ -715,11 +730,7 @@ class _VerifySheetState extends ConsumerState<_VerifySheet> { return [ Text( '正在清除账户数据,请稍候', - style: TextStyle( - fontSize: 13, - height: 1.55, - color: cs.mutedForeground, - ), + style: TextStyle(fontSize: 13, height: 1.55, color: cs.mutedForeground), ), const SizedBox(height: 28), Center( @@ -737,11 +748,7 @@ class _VerifySheetState extends ConsumerState<_VerifySheet> { return [ Text( '为保障账户安全,注销前需验证您的手机号。验证码将发送至 ${_maskPhone(widget.phone)}。', - style: TextStyle( - fontSize: 13, - height: 1.55, - color: cs.mutedForeground, - ), + style: TextStyle(fontSize: 13, height: 1.55, color: cs.mutedForeground), ), if (_error != null) ...[ const SizedBox(height: 10), @@ -768,11 +775,7 @@ class _VerifySheetState extends ConsumerState<_VerifySheet> { return [ Text( '验证码已发送至 ${_maskPhone(widget.phone)}', - style: TextStyle( - fontSize: 13, - height: 1.55, - color: cs.mutedForeground, - ), + style: TextStyle(fontSize: 13, height: 1.55, color: cs.mutedForeground), ), const SizedBox(height: 20), _PinInput( diff --git a/lib/features/mall/application/mall_entry_controller.dart b/lib/features/mall/application/mall_entry_controller.dart index 9c38381..4a8d794 100644 --- a/lib/features/mall/application/mall_entry_controller.dart +++ b/lib/features/mall/application/mall_entry_controller.dart @@ -27,23 +27,14 @@ class MallEntryController { if (config?.isVisible != true || !context.mounted) { return MallEntryOpenResult.unavailable; } - final clickId = await _ref + config = await _ref .read(mallTabConfigProvider.notifier) - .recordClick(); - if (clickId == null || !context.mounted) { + .recordClick(request: request); + if (!config.isVisible || !context.mounted) { return MallEntryOpenResult.unavailable; } - _ref - .read(mallClickAttributionProvider.notifier) - .state = MallClickAttribution( - adId: config!.adId, - trackingId: config.trackingId, - clickId: clickId, - ); final uri = config.attributedUri( - clickId: clickId, isDark: Theme.of(context).brightness == Brightness.dark, - entry: request, ); if (uri == null) return MallEntryOpenResult.unavailable; // The bottom mall entry is always resident. Server open-mode metadata is diff --git a/lib/features/mall/application/mall_tab_config.dart b/lib/features/mall/application/mall_tab_config.dart index d23e299..322b76b 100644 --- a/lib/features/mall/application/mall_tab_config.dart +++ b/lib/features/mall/application/mall_tab_config.dart @@ -1,18 +1,15 @@ -import 'dart:convert'; -import 'dart:typed_data'; - import 'package:dio/dio.dart'; +import 'package:fixnum/fixnum.dart'; import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:uuid/uuid.dart'; import '../../../common/network/dio_provider.dart'; +import '../../../models/pb/jinzhi/v1/ads.pb.dart' as pb; -/// 金值商城广告位的公共契约;商城是否展示完全由服务端配置决定。 +/// 标识打开商城的宿主项目。 const mallSourceApp = 'jinzhi'; -/// The native entry that opened the resident mall. H5 uses this context to -/// choose the appropriate landing flow while sharing the same WebView host. class MallEntryRequest { const MallEntryRequest({ required this.sourceEntry, @@ -45,11 +42,15 @@ class MallTabConfig { this.title = '商城', this.adId = '', this.trackingId = '', + this.clickId = '', this.openMode = 'in_app', this.entryPromoEnabled = false, this.entryBadgeText = '', this.entryTipTitle = '', this.entryTipBody = '', + this.quoteCtaEnabled = false, + this.entryRequest = MallEntryRequest.home, + this.allowedHosts = const {}, }); const MallTabConfig.disabled() : this(enabled: false, url: ''); @@ -59,11 +60,15 @@ class MallTabConfig { final String title; final String adId; final String trackingId; + final String clickId; final String openMode; final bool entryPromoEnabled; final String entryBadgeText; final String entryTipTitle; final String entryTipBody; + final bool quoteCtaEnabled; + final MallEntryRequest entryRequest; + final Set allowedHosts; bool get isVisible => enabled && initialUri != null; bool get opensInApp => openMode == 'in_app'; @@ -74,52 +79,80 @@ class MallTabConfig { entryPromoEnabled && (entryTipTitle.trim().isNotEmpty || entryTipBody.trim().isNotEmpty); - Uri? get initialUri => attributedUri(clickId: ''); + String get from { + final value = Uri.tryParse(url.trim())?.queryParameters['from']?.trim(); + return value == null || value.isEmpty ? mallSourceApp : value; + } + + Uri? get initialUri => attributedUri(); Uri? attributedUri({ - required String clickId, + String? clickId, bool isDark = false, - MallEntryRequest entry = MallEntryRequest.home, + MallEntryRequest? entry, }) { final uri = Uri.tryParse(url.trim()); if (!_isAllowed(uri)) return null; + final effectiveClickId = clickId ?? this.clickId; + final effectiveEntry = entry ?? entryRequest; return uri!.replace( queryParameters: { ...uri.queryParameters, - 'from': uri.queryParameters['from']?.trim().isNotEmpty == true - ? uri.queryParameters['from']! - : mallSourceApp, - 'source_app': mallSourceApp, - 'source_entry': entry.sourceEntry, + 'from': from, + 'source_app': from, + 'source_entry': effectiveEntry.sourceEntry, 'placement': 'mall_tab', - 'hook_type': entry.hookType, - if (entry.entryScene?.isNotEmpty == true) - 'entry_scene': entry.entryScene!, - if (entry.entryMetal?.isNotEmpty == true) - 'entry_metal': entry.entryMetal!, + 'hook_type': effectiveEntry.hookType, + 'entry_scene': ?effectiveEntry.entryScene, + 'entry_metal': ?effectiveEntry.entryMetal, 'target_app': 'mall', if (adId.isNotEmpty) 'ad_id': adId, if (trackingId.isNotEmpty) 'tracking_id': trackingId, - if (clickId.isNotEmpty) 'click_id': clickId, - if (clickId.isNotEmpty) + if (effectiveClickId.isNotEmpty) 'click_id': effectiveClickId, + if (effectiveClickId.isNotEmpty) 'ts': DateTime.now().millisecondsSinceEpoch.toString(), 'isNight': isDark ? '1' : '0', }, ); } + MallTabConfig withClick( + String value, { + MallEntryRequest request = MallEntryRequest.home, + }) => MallTabConfig( + enabled: enabled, + url: url, + title: title, + adId: adId, + trackingId: trackingId, + clickId: value, + openMode: openMode, + entryPromoEnabled: entryPromoEnabled, + entryBadgeText: entryBadgeText, + entryTipTitle: entryTipTitle, + entryTipBody: entryTipBody, + quoteCtaEnabled: quoteCtaEnabled, + entryRequest: request, + allowedHosts: allowedHosts, + ); + static bool _isAllowed(Uri? uri) { if (uri == null || uri.host.isEmpty) return false; - if (uri.scheme == 'https') return true; + if (uri.scheme.toLowerCase() == 'https') return true; return !kReleaseMode && - uri.scheme == 'http' && - const {'localhost', '127.0.0.1', '::1'}.contains(uri.host); + uri.scheme.toLowerCase() == 'http' && + const { + 'localhost', + '127.0.0.1', + '::1', + }.contains(uri.host.toLowerCase()); } } class MallTabConfigController extends StateNotifier { - MallTabConfigController(this._dio) : super(const MallTabConfig.disabled()) { - refresh(); + MallTabConfigController(this._dio, {bool autoRefresh = true}) + : super(const MallTabConfig.disabled()) { + if (autoRefresh) refresh(); } final Dio _dio; @@ -138,38 +171,51 @@ class MallTabConfigController extends StateNotifier { queryParameters: const {'placement': 'mall_tab'}, options: protobufOptions(), ); - state = parseMallTabConfigResponse(response.data ?? const []); + state = parseMallTabConfigResponse(response.data ?? const []); if (!state.isVisible) return; - await _report('ad_impression'); + await _report(state, 'ad_impression'); } catch (_) { state = const MallTabConfig.disabled(); } } - Future recordClick() async { - if (!state.isVisible) return null; - final clickId = const Uuid().v4(); + Future recordClick({ + MallEntryRequest request = MallEntryRequest.home, + }) async { + if (!state.isVisible) return state; + final clicked = state.withClick(const Uuid().v4(), request: request); + state = clicked; try { - await _report('ad_click', clickId: clickId); + await _report(clicked, 'ad_click'); } catch (_) { // 归因上报失败不能阻断商城入口。 } - return clickId; + return clicked; } - Future _report(String event, {String clickId = ''}) => - _dio.post>( - '/api/v1/ads/events', - data: _ProtoWriter() - .string(1, event) - .string(2, 'mall_tab') - .string(3, state.adId) - .string(4, state.trackingId) - .string(5, clickId) - .integer(6, DateTime.now().millisecondsSinceEpoch) - .toBytes(), - options: protobufOptions(), - ); + Future _report(MallTabConfig config, String event) async { + final response = await _dio.post>( + '/api/v1/ads/events', + data: pb.ReportAdEventRequest( + eventName: event, + placement: 'mall_tab', + adId: config.adId, + trackingId: config.trackingId, + clickId: config.clickId, + clientTsMs: Int64(DateTime.now().millisecondsSinceEpoch), + attrs: { + 'source_app': config.from, + 'source_entry': config.entryRequest.sourceEntry, + 'hook_type': config.entryRequest.hookType, + 'entry_scene': ?config.entryRequest.entryScene, + 'entry_metal': ?config.entryRequest.entryMetal, + 'open_mode': config.openMode, + }, + ).writeToBuffer(), + options: protobufOptions(), + ); + pb.ReportAdEventReply.fromBuffer(response.data ?? const []); + } } final mallTabConfigProvider = @@ -177,52 +223,21 @@ final mallTabConfigProvider = (ref) => MallTabConfigController(ref.watch(dioProvider)), ); -class MallClickAttribution { - const MallClickAttribution({ - required this.adId, - required this.trackingId, - required this.clickId, - }); - - final String adId; - final String trackingId; - final String clickId; -} - -final mallClickAttributionProvider = StateProvider( - (ref) => null, -); - Map buildEnterWebMallBridgeArguments({ - required Uri currentUri, + required MallTabConfig config, required bool logged, - required bool isDark, + Uri? currentUri, + bool isDark = false, String? authToken, String? scene, String? sourceApp, - MallClickAttribution? attribution, }) { - final query = currentUri.queryParameters; - final resolvedSourceApp = sourceApp?.trim().isNotEmpty == true - ? sourceApp!.trim() - : mallSourceApp; final arguments = { - ...query, - 'from': resolvedSourceApp, - 'source_app': resolvedSourceApp, - 'source_entry': query['source_entry'] ?? 'mall_tab', - 'placement': query['placement'] ?? 'mall_tab', - 'hook_type': query['hook_type'] ?? 'mall_tab', - 'target_app': query['target_app'] ?? 'mall', + ...?currentUri?.queryParameters, + ...?config.attributedUri(isDark: isDark)?.queryParameters, + 'from': config.from, 'logged': logged, - 'isNight': isDark ? '1' : '0', }; - final adId = attribution?.adId.trim() ?? ''; - if (adId.isNotEmpty) arguments['ad_id'] = adId; - final trackingId = attribution?.trackingId.trim() ?? ''; - if (trackingId.isNotEmpty) arguments['tracking_id'] = trackingId; - final clickId = attribution?.clickId.trim() ?? ''; - if (clickId.isNotEmpty) arguments['click_id'] = clickId; final token = authToken?.trim() ?? ''; if (token.isNotEmpty) { arguments['auth_token'] = token; @@ -230,119 +245,52 @@ Map buildEnterWebMallBridgeArguments({ } final resolvedScene = scene?.trim() ?? ''; if (resolvedScene.isNotEmpty) arguments['scene'] = resolvedScene; + final resolvedSourceApp = sourceApp?.trim() ?? ''; + if (resolvedSourceApp.isNotEmpty) { + arguments['from'] = resolvedSourceApp; + arguments['source_app'] = resolvedSourceApp; + } return arguments; } +MallTabConfig parseMallTabConfigResponse(List bytes) { + final reply = pb.GetAdReply.fromBuffer(bytes); + if (!reply.enabled || !reply.hasAd()) { + return const MallTabConfig.disabled(); + } + final creative = reply.ad; + final url = _firstAllowedUrl(creative.trackedUrl, creative.h5Url); + if (url == null) return const MallTabConfig.disabled(); + final promotion = creative.hasMallPromotion() ? creative.mallPromotion : null; + return MallTabConfig( + enabled: true, + url: url, + title: _nonEmpty(creative.title, '商城'), + adId: creative.adId, + trackingId: creative.trackingId, + openMode: _openMode(creative.openMode), + entryPromoEnabled: creative.entryPromoEnabled, + entryBadgeText: creative.entryBadgeText.trim(), + entryTipTitle: creative.entryTipTitle.trim(), + entryTipBody: creative.entryTipBody.trim(), + quoteCtaEnabled: promotion?.quoteCtaEnabled ?? false, + allowedHosts: {Uri.parse(url).host.toLowerCase()}, + ); +} + String _nonEmpty(String value, String fallback) => value.trim().isEmpty ? fallback : value.trim(); -String? _validUrl(String raw) { - final uri = Uri.tryParse(raw.trim()); - return MallTabConfig._isAllowed(uri) ? uri.toString() : null; -} - -String _openMode(int value) => switch (value) { - 1 => 'native_app', - 2 => 'in_app', +String _openMode(pb.AdOpenMode value) => switch (value) { + pb.AdOpenMode.AD_OPEN_MODE_NATIVE_APP => 'native_app', + pb.AdOpenMode.AD_OPEN_MODE_IN_APP => 'in_app', _ => 'external', }; -/// Parses the protobuf reply returned by `/api/v1/ads?placement=mall_tab`. -MallTabConfig parseMallTabConfigResponse(List bytes) { - final response = _ProtoMessage(bytes); - final reply = response.message(2); - final trackedUrl = reply.string(14); - final h5Url = reply.string(9); - final url = _validUrl(trackedUrl) ?? _validUrl(h5Url); - if (response.integer(1) == 0 || url == null) { - return const MallTabConfig.disabled(); - } - return MallTabConfig( - enabled: true, - url: url, - title: _nonEmpty(reply.string(4), '商城'), - adId: reply.string(1), - trackingId: reply.string(10), - openMode: _openMode(reply.integer(15)), - entryPromoEnabled: reply.integer(16) != 0, - entryBadgeText: reply.string(17).trim(), - entryTipTitle: reply.string(18).trim(), - entryTipBody: reply.string(19).trim(), - ); -} - -class _ProtoMessage { - _ProtoMessage(this._bytes); - final List _bytes; - - int integer(int wanted) => - _fields()[wanted] is int ? _fields()[wanted]! as int : 0; - String string(int wanted) => _fields()[wanted] is List - ? utf8.decode(_fields()[wanted]! as List, allowMalformed: true) - : ''; - _ProtoMessage message(int wanted) => _ProtoMessage( - _fields()[wanted] is List ? _fields()[wanted]! as List : const [], - ); - - Map _fields() { - final values = {}; - var index = 0; - int readVarint() { - var value = 0; - var shift = 0; - while (index < _bytes.length && shift < 64) { - final byte = _bytes[index++]; - value |= (byte & 127) << shift; - if (byte < 128) return value; - shift += 7; - } - throw const FormatException('Invalid protobuf'); - } - - while (index < _bytes.length) { - final key = readVarint(); - final tag = key >> 3; - switch (key & 7) { - case 0: - values[tag] = readVarint(); - case 2: - final length = readVarint(); - final end = index + length; - if (end > _bytes.length) { - throw const FormatException('Invalid protobuf'); - } - values[tag] = _bytes.sublist(index, end); - index = end; - default: - throw const FormatException('Unsupported protobuf'); - } - } - return values; - } -} - -class _ProtoWriter { - final _bytes = BytesBuilder(copy: false); - _ProtoWriter string(int tag, String value) { - final data = value.codeUnits; - _varint((tag << 3) | 2); - _varint(data.length); - _bytes.add(data); - return this; - } - - _ProtoWriter integer(int tag, int value) { - _varint(tag << 3); - _varint(value); - return this; - } - - Uint8List toBytes() => _bytes.toBytes(); - void _varint(int value) { - while (value > 127) { - _bytes.addByte((value & 127) | 128); - value >>= 7; - } - _bytes.addByte(value); +String? _firstAllowedUrl(String trackedUrl, String h5Url) { + for (final raw in [trackedUrl, h5Url]) { + final uri = Uri.tryParse(raw.trim()); + if (MallTabConfig._isAllowed(uri)) return uri.toString(); } + return null; } diff --git a/lib/features/profile/presentation/profile_page.dart b/lib/features/profile/presentation/profile_page.dart index 5bf049f..0217f20 100644 --- a/lib/features/profile/presentation/profile_page.dart +++ b/lib/features/profile/presentation/profile_page.dart @@ -19,6 +19,8 @@ class ProfilePage extends ConsumerWidget { final auth = ref.watch(authProvider); final cs = ShadTheme.of(context).colorScheme; final loggedIn = auth.valueOrNull is LoggedInAuthState; + final authLoading = auth.isLoading; + final authFailed = auth.hasError; final currentUser = auth.valueOrNull is LoggedInAuthState ? (auth.valueOrNull as LoggedInAuthState).user : null; @@ -100,8 +102,22 @@ class ProfilePage extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ FilledButton.tonal( - onPressed: () => LoginLauncher.start(context, ref), - child: Text(loggedIn ? '切换账号' : '去登录'), + onPressed: authLoading + ? null + : authFailed + ? () => ref + .read(authProvider.notifier) + .retryBootstrap() + : () => LoginLauncher.start(context, ref), + child: Text( + authLoading + ? '正在恢复账号…' + : authFailed + ? '账号恢复失败,点击重试' + : loggedIn + ? '切换账号' + : '去登录', + ), ), const SizedBox(height: 10), OutlinedButton( diff --git a/lib/features/settings/presentation/settings_page.dart b/lib/features/settings/presentation/settings_page.dart index 2efdffe..511f1c0 100644 --- a/lib/features/settings/presentation/settings_page.dart +++ b/lib/features/settings/presentation/settings_page.dart @@ -6,6 +6,7 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; +import 'package:url_launcher/url_launcher_string.dart'; import '../../../common/config/app_h5_urls.dart'; import '../../../common/push/notification_permission.dart'; @@ -32,6 +33,7 @@ class SettingsPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final cs = ShadTheme.of(context).colorScheme; + final isDark = Theme.of(context).brightness == Brightness.dark; final themeMode = ref.watch(themeModeControllerProvider); final profileSettings = ref.watch(profileSettingsControllerProvider); final authAsync = ref.watch(authProvider); @@ -198,16 +200,28 @@ class SettingsPage extends HookConsumerWidget { _NavRow( icon: FlutterRemix.file_list_3_line, label: '用户协议', - onTap: () => context.push( - '${AppRoutes.web}?url=${Uri.encodeComponent(AppH5UrlsHelper.withCacheBuster(AppH5Urls.userProtocolUrl))}', + onTap: () => launchUrlString( + AppH5UrlsHelper.withCacheBuster( + AppH5UrlsHelper.buildUrlWithNight( + AppH5Urls.userProtocolUrl, + isDark, + ), + ), + mode: LaunchMode.inAppBrowserView, ), ), Divider(height: 1, thickness: 1, color: cs.border), _NavRow( icon: FlutterRemix.shield_check_line, label: '隐私政策', - onTap: () => context.push( - '${AppRoutes.web}?url=${Uri.encodeComponent(AppH5UrlsHelper.withCacheBuster(AppH5Urls.privacyUrl))}', + onTap: () => launchUrlString( + AppH5UrlsHelper.withCacheBuster( + AppH5UrlsHelper.buildUrlWithNight( + AppH5Urls.privacyUrl, + isDark, + ), + ), + mode: LaunchMode.inAppBrowserView, ), ), Divider(height: 1, thickness: 1, color: cs.border), diff --git a/lib/features/web/presentation/mall_web_host.dart b/lib/features/web/presentation/mall_web_host.dart index fb9b5d1..6cd048b 100644 --- a/lib/features/web/presentation/mall_web_host.dart +++ b/lib/features/web/presentation/mall_web_host.dart @@ -571,13 +571,13 @@ class _MallWebHostState extends ConsumerState final mallAuth = logged ? await _issueMallAuthToken() : null; if (!mounted) return; final arguments = buildEnterWebMallBridgeArguments( + config: ref.read(mallTabConfigProvider), currentUri: uri!, logged: logged, isDark: Theme.of(context).brightness == Brightness.dark, authToken: mallAuth?.authToken, scene: mallAuth?.scene, sourceApp: mallAuth?.sourceApp, - attribution: ref.read(mallClickAttributionProvider), ); _callH5Handler(JsEvent('enterWebMall', [arguments])); } diff --git a/lib/features/web/presentation/web_page.dart b/lib/features/web/presentation/web_page.dart index 40fa3d8..15384f3 100644 --- a/lib/features/web/presentation/web_page.dart +++ b/lib/features/web/presentation/web_page.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; import 'package:go_router/go_router.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:webview_flutter/webview_flutter.dart'; +import '../application/web_navigation_policy.dart'; + /// Shared HTTPS WebView host. Mall-specific residency and attribution are /// layered above this page so ordinary H5 links never receive mall state. class AppWebPage extends StatefulWidget { @@ -10,12 +13,10 @@ class AppWebPage extends StatefulWidget { super.key, required this.initialUri, this.hideTitle = false, - this.allowedHosts = const {}, }); final Uri initialUri; final bool hideTitle; - final Set allowedHosts; @override State createState() => _AppWebPageState(); @@ -60,23 +61,19 @@ class _AppWebPageState extends State { NavigationDecision _onNavigationRequest(NavigationRequest request) { final uri = Uri.tryParse(request.url); - if (uri == null || !_isHttp(uri)) return NavigationDecision.prevent; - if (_allowsInApp(uri)) return NavigationDecision.navigate; - launchUrl(uri, mode: LaunchMode.externalApplication); - return NavigationDecision.prevent; + if (uri != null && shouldOpenWebUrlExternally(request.url)) { + launchUrl(uri, mode: LaunchMode.externalApplication); + return NavigationDecision.prevent; + } + if (uri == null || !_isAllowedWebUri(uri)) { + return NavigationDecision.prevent; + } + // Align with jinzhi-flutter-app: a normal HTTPS navigation stays in this + // WebView even when its host changes. H5 opts into an external handoff + // through `out`, which is handled above. + return NavigationDecision.navigate; } - bool _allowsInApp(Uri uri) { - final host = uri.host.toLowerCase(); - if (host == widget.initialUri.host.toLowerCase()) return true; - return widget.allowedHosts.any( - (allowed) => host == allowed || host.endsWith('.$allowed'), - ); - } - - bool _isHttp(Uri uri) => - uri.hasAuthority && (uri.scheme == 'https' || uri.scheme == 'http'); - Future _onPop() async { if (await _controller.canGoBack()) { await _controller.goBack(); @@ -118,6 +115,16 @@ class _AppWebPageState extends State { Uri? parseSafeWebUri(String? raw) { final uri = Uri.tryParse(raw?.trim() ?? ''); - if (uri == null || !uri.hasAuthority) return null; - return uri.scheme == 'https' || uri.scheme == 'http' ? uri : null; + return uri != null && _isAllowedWebUri(uri) ? uri : null; +} + +bool _isAllowedWebUri(Uri uri) { + if (!uri.hasAuthority || uri.host.isEmpty) return false; + if (uri.scheme.toLowerCase() == 'https') return true; + if (kReleaseMode || uri.scheme.toLowerCase() != 'http') return false; + return const { + 'localhost', + '127.0.0.1', + '::1', + }.contains(uri.host.toLowerCase()); } diff --git a/lib/models/pb/errors/errors.pb.dart b/lib/models/pb/errors/errors.pb.dart new file mode 100644 index 0000000..680f921 --- /dev/null +++ b/lib/models/pb/errors/errors.pb.dart @@ -0,0 +1,113 @@ +// +// Generated code. Do not modify. +// source: errors/errors.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +class Status extends $pb.GeneratedMessage { + factory Status({ + $core.int? code, + $core.String? reason, + $core.String? message, + $core.Map<$core.String, $core.String>? metadata, + }) { + final $result = create(); + if (code != null) { + $result.code = code; + } + if (reason != null) { + $result.reason = reason; + } + if (message != null) { + $result.message = message; + } + if (metadata != null) { + $result.metadata.addAll(metadata); + } + return $result; + } + Status._() : super(); + factory Status.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory Status.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Status', package: const $pb.PackageName(_omitMessageNames ? '' : 'errors'), createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'code', $pb.PbFieldType.O3) + ..aOS(2, _omitFieldNames ? '' : 'reason') + ..aOS(3, _omitFieldNames ? '' : 'message') + ..m<$core.String, $core.String>(4, _omitFieldNames ? '' : 'metadata', entryClassName: 'Status.MetadataEntry', keyFieldType: $pb.PbFieldType.OS, valueFieldType: $pb.PbFieldType.OS, packageName: const $pb.PackageName('errors')) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + Status clone() => Status()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + Status copyWith(void Function(Status) updates) => super.copyWith((message) => updates(message as Status)) as Status; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Status create() => Status._(); + Status createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static Status getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Status? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get code => $_getIZ(0); + @$pb.TagNumber(1) + set code($core.int v) { $_setSignedInt32(0, v); } + @$pb.TagNumber(1) + $core.bool hasCode() => $_has(0); + @$pb.TagNumber(1) + void clearCode() => clearField(1); + + @$pb.TagNumber(2) + $core.String get reason => $_getSZ(1); + @$pb.TagNumber(2) + set reason($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasReason() => $_has(1); + @$pb.TagNumber(2) + void clearReason() => clearField(2); + + @$pb.TagNumber(3) + $core.String get message => $_getSZ(2); + @$pb.TagNumber(3) + set message($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasMessage() => $_has(2); + @$pb.TagNumber(3) + void clearMessage() => clearField(3); + + @$pb.TagNumber(4) + $core.Map<$core.String, $core.String> get metadata => $_getMap(3); +} + +class Errors { + static final defaultCode = $pb.Extension<$core.int>(_omitMessageNames ? '' : 'google.protobuf.EnumOptions', _omitFieldNames ? '' : 'defaultCode', 1108, $pb.PbFieldType.O3); + static final code = $pb.Extension<$core.int>(_omitMessageNames ? '' : 'google.protobuf.EnumValueOptions', _omitFieldNames ? '' : 'code', 1109, $pb.PbFieldType.O3); + static void registerAllExtensions($pb.ExtensionRegistry registry) { + registry.add(defaultCode); + registry.add(code); + } +} + + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/models/pb/errors/errors.pbenum.dart b/lib/models/pb/errors/errors.pbenum.dart new file mode 100644 index 0000000..3fd5a2a --- /dev/null +++ b/lib/models/pb/errors/errors.pbenum.dart @@ -0,0 +1,11 @@ +// +// Generated code. Do not modify. +// source: errors/errors.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + diff --git a/lib/models/pb/errors/errors.pbjson.dart b/lib/models/pb/errors/errors.pbjson.dart new file mode 100644 index 0000000..f30edf0 --- /dev/null +++ b/lib/models/pb/errors/errors.pbjson.dart @@ -0,0 +1,44 @@ +// +// Generated code. Do not modify. +// source: errors/errors.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use statusDescriptor instead') +const Status$json = { + '1': 'Status', + '2': [ + {'1': 'code', '3': 1, '4': 1, '5': 5, '10': 'code'}, + {'1': 'reason', '3': 2, '4': 1, '5': 9, '10': 'reason'}, + {'1': 'message', '3': 3, '4': 1, '5': 9, '10': 'message'}, + {'1': 'metadata', '3': 4, '4': 3, '5': 11, '6': '.errors.Status.MetadataEntry', '10': 'metadata'}, + ], + '3': [Status_MetadataEntry$json], +}; + +@$core.Deprecated('Use statusDescriptor instead') +const Status_MetadataEntry$json = { + '1': 'MetadataEntry', + '2': [ + {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + ], + '7': {'7': true}, +}; + +/// Descriptor for `Status`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List statusDescriptor = $convert.base64Decode( + 'CgZTdGF0dXMSEgoEY29kZRgBIAEoBVIEY29kZRIWCgZyZWFzb24YAiABKAlSBnJlYXNvbhIYCg' + 'dtZXNzYWdlGAMgASgJUgdtZXNzYWdlEjgKCG1ldGFkYXRhGAQgAygLMhwuZXJyb3JzLlN0YXR1' + 'cy5NZXRhZGF0YUVudHJ5UghtZXRhZGF0YRo7Cg1NZXRhZGF0YUVudHJ5EhAKA2tleRgBIAEoCV' + 'IDa2V5EhQKBXZhbHVlGAIgASgJUgV2YWx1ZToCOAE='); + diff --git a/lib/models/pb/errors/errors.pbserver.dart b/lib/models/pb/errors/errors.pbserver.dart new file mode 100644 index 0000000..a3b2c2c --- /dev/null +++ b/lib/models/pb/errors/errors.pbserver.dart @@ -0,0 +1,14 @@ +// +// Generated code. Do not modify. +// source: errors/errors.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +export 'errors.pb.dart'; + diff --git a/lib/models/pb/google/protobuf/empty.pb.dart b/lib/models/pb/google/protobuf/empty.pb.dart new file mode 100644 index 0000000..fb65fac --- /dev/null +++ b/lib/models/pb/google/protobuf/empty.pb.dart @@ -0,0 +1,58 @@ +// +// Generated code. Do not modify. +// source: google/protobuf/empty.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +/// A generic empty message that you can re-use to avoid defining duplicated +/// empty messages in your APIs. A typical example is to use it as the request +/// or the response type of an API method. For instance: +/// +/// service Foo { +/// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +/// } +/// +/// The JSON representation for `Empty` is empty JSON object `{}`. +class Empty extends $pb.GeneratedMessage { + factory Empty() => create(); + Empty._() : super(); + factory Empty.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory Empty.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Empty', package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), createEmptyInstance: create) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + Empty clone() => Empty()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + Empty copyWith(void Function(Empty) updates) => super.copyWith((message) => updates(message as Empty)) as Empty; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Empty create() => Empty._(); + Empty createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static Empty getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Empty? _defaultInstance; +} + + +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/models/pb/jinzhi/v1/account.pb.dart b/lib/models/pb/jinzhi/v1/account.pb.dart new file mode 100644 index 0000000..266abe9 --- /dev/null +++ b/lib/models/pb/jinzhi/v1/account.pb.dart @@ -0,0 +1,1043 @@ +// +// Generated code. Do not modify. +// source: jinzhi/v1/account.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:async' as $async; +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +import '../../google/protobuf/empty.pb.dart' as $3; +import 'push.pb.dart' as $5; + +class SendSmsCodeRequest extends $pb.GeneratedMessage { + factory SendSmsCodeRequest({ + $core.String? phone, + }) { + final $result = create(); + if (phone != null) { + $result.phone = phone; + } + return $result; + } + SendSmsCodeRequest._() : super(); + factory SendSmsCodeRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory SendSmsCodeRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SendSmsCodeRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'phone') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + SendSmsCodeRequest clone() => SendSmsCodeRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + SendSmsCodeRequest copyWith(void Function(SendSmsCodeRequest) updates) => super.copyWith((message) => updates(message as SendSmsCodeRequest)) as SendSmsCodeRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SendSmsCodeRequest create() => SendSmsCodeRequest._(); + SendSmsCodeRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static SendSmsCodeRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static SendSmsCodeRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get phone => $_getSZ(0); + @$pb.TagNumber(1) + set phone($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasPhone() => $_has(0); + @$pb.TagNumber(1) + void clearPhone() => clearField(1); +} + +class SendSmsCodeReply extends $pb.GeneratedMessage { + factory SendSmsCodeReply({ + $core.int? expiresIn, + }) { + final $result = create(); + if (expiresIn != null) { + $result.expiresIn = expiresIn; + } + return $result; + } + SendSmsCodeReply._() : super(); + factory SendSmsCodeReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory SendSmsCodeReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SendSmsCodeReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'expiresIn', $pb.PbFieldType.O3) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + SendSmsCodeReply clone() => SendSmsCodeReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + SendSmsCodeReply copyWith(void Function(SendSmsCodeReply) updates) => super.copyWith((message) => updates(message as SendSmsCodeReply)) as SendSmsCodeReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SendSmsCodeReply create() => SendSmsCodeReply._(); + SendSmsCodeReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static SendSmsCodeReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static SendSmsCodeReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get expiresIn => $_getIZ(0); + @$pb.TagNumber(1) + set expiresIn($core.int v) { $_setSignedInt32(0, v); } + @$pb.TagNumber(1) + $core.bool hasExpiresIn() => $_has(0); + @$pb.TagNumber(1) + void clearExpiresIn() => clearField(1); +} + +class VerifySmsCodeRequest extends $pb.GeneratedMessage { + factory VerifySmsCodeRequest({ + $core.String? phone, + $core.String? code, + }) { + final $result = create(); + if (phone != null) { + $result.phone = phone; + } + if (code != null) { + $result.code = code; + } + return $result; + } + VerifySmsCodeRequest._() : super(); + factory VerifySmsCodeRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory VerifySmsCodeRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'VerifySmsCodeRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'phone') + ..aOS(2, _omitFieldNames ? '' : 'code') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + VerifySmsCodeRequest clone() => VerifySmsCodeRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + VerifySmsCodeRequest copyWith(void Function(VerifySmsCodeRequest) updates) => super.copyWith((message) => updates(message as VerifySmsCodeRequest)) as VerifySmsCodeRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static VerifySmsCodeRequest create() => VerifySmsCodeRequest._(); + VerifySmsCodeRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static VerifySmsCodeRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static VerifySmsCodeRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get phone => $_getSZ(0); + @$pb.TagNumber(1) + set phone($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasPhone() => $_has(0); + @$pb.TagNumber(1) + void clearPhone() => clearField(1); + + @$pb.TagNumber(2) + $core.String get code => $_getSZ(1); + @$pb.TagNumber(2) + set code($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasCode() => $_has(1); + @$pb.TagNumber(2) + void clearCode() => clearField(2); +} + +class VerifyOneKeyLoginRequest extends $pb.GeneratedMessage { + factory VerifyOneKeyLoginRequest({ + $core.String? loginToken, + }) { + final $result = create(); + if (loginToken != null) { + $result.loginToken = loginToken; + } + return $result; + } + VerifyOneKeyLoginRequest._() : super(); + factory VerifyOneKeyLoginRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory VerifyOneKeyLoginRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'VerifyOneKeyLoginRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'loginToken') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + VerifyOneKeyLoginRequest clone() => VerifyOneKeyLoginRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + VerifyOneKeyLoginRequest copyWith(void Function(VerifyOneKeyLoginRequest) updates) => super.copyWith((message) => updates(message as VerifyOneKeyLoginRequest)) as VerifyOneKeyLoginRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static VerifyOneKeyLoginRequest create() => VerifyOneKeyLoginRequest._(); + VerifyOneKeyLoginRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static VerifyOneKeyLoginRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static VerifyOneKeyLoginRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get loginToken => $_getSZ(0); + @$pb.TagNumber(1) + set loginToken($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasLoginToken() => $_has(0); + @$pb.TagNumber(1) + void clearLoginToken() => clearField(1); +} + +class UserInfo extends $pb.GeneratedMessage { + factory UserInfo({ + $core.String? id, + $core.String? phone, + $core.String? createdAt, + }) { + final $result = create(); + if (id != null) { + $result.id = id; + } + if (phone != null) { + $result.phone = phone; + } + if (createdAt != null) { + $result.createdAt = createdAt; + } + return $result; + } + UserInfo._() : super(); + factory UserInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory UserInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'UserInfo', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'id') + ..aOS(2, _omitFieldNames ? '' : 'phone') + ..aOS(3, _omitFieldNames ? '' : 'createdAt') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + UserInfo clone() => UserInfo()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + UserInfo copyWith(void Function(UserInfo) updates) => super.copyWith((message) => updates(message as UserInfo)) as UserInfo; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static UserInfo create() => UserInfo._(); + UserInfo createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static UserInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static UserInfo? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get id => $_getSZ(0); + @$pb.TagNumber(1) + set id($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get phone => $_getSZ(1); + @$pb.TagNumber(2) + set phone($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasPhone() => $_has(1); + @$pb.TagNumber(2) + void clearPhone() => clearField(2); + + @$pb.TagNumber(3) + $core.String get createdAt => $_getSZ(2); + @$pb.TagNumber(3) + set createdAt($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasCreatedAt() => $_has(2); + @$pb.TagNumber(3) + void clearCreatedAt() => clearField(3); +} + +class VerifySmsCodeReply extends $pb.GeneratedMessage { + factory VerifySmsCodeReply({ + $core.String? accessToken, + $core.int? expiresIn, + UserInfo? user, + }) { + final $result = create(); + if (accessToken != null) { + $result.accessToken = accessToken; + } + if (expiresIn != null) { + $result.expiresIn = expiresIn; + } + if (user != null) { + $result.user = user; + } + return $result; + } + VerifySmsCodeReply._() : super(); + factory VerifySmsCodeReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory VerifySmsCodeReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'VerifySmsCodeReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'accessToken') + ..a<$core.int>(2, _omitFieldNames ? '' : 'expiresIn', $pb.PbFieldType.O3) + ..aOM(3, _omitFieldNames ? '' : 'user', subBuilder: UserInfo.create) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + VerifySmsCodeReply clone() => VerifySmsCodeReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + VerifySmsCodeReply copyWith(void Function(VerifySmsCodeReply) updates) => super.copyWith((message) => updates(message as VerifySmsCodeReply)) as VerifySmsCodeReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static VerifySmsCodeReply create() => VerifySmsCodeReply._(); + VerifySmsCodeReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static VerifySmsCodeReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static VerifySmsCodeReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get accessToken => $_getSZ(0); + @$pb.TagNumber(1) + set accessToken($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasAccessToken() => $_has(0); + @$pb.TagNumber(1) + void clearAccessToken() => clearField(1); + + @$pb.TagNumber(2) + $core.int get expiresIn => $_getIZ(1); + @$pb.TagNumber(2) + set expiresIn($core.int v) { $_setSignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasExpiresIn() => $_has(1); + @$pb.TagNumber(2) + void clearExpiresIn() => clearField(2); + + @$pb.TagNumber(3) + UserInfo get user => $_getN(2); + @$pb.TagNumber(3) + set user(UserInfo v) { setField(3, v); } + @$pb.TagNumber(3) + $core.bool hasUser() => $_has(2); + @$pb.TagNumber(3) + void clearUser() => clearField(3); + @$pb.TagNumber(3) + UserInfo ensureUser() => $_ensure(2); +} + +class IssueJinzhiWebMallTokenRequest extends $pb.GeneratedMessage { + factory IssueJinzhiWebMallTokenRequest({ + $core.String? trackingId, + $core.String? clickId, + $core.String? scene, + }) { + final $result = create(); + if (trackingId != null) { + $result.trackingId = trackingId; + } + if (clickId != null) { + $result.clickId = clickId; + } + if (scene != null) { + $result.scene = scene; + } + return $result; + } + IssueJinzhiWebMallTokenRequest._() : super(); + factory IssueJinzhiWebMallTokenRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory IssueJinzhiWebMallTokenRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'IssueJinzhiWebMallTokenRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'trackingId') + ..aOS(2, _omitFieldNames ? '' : 'clickId') + ..aOS(3, _omitFieldNames ? '' : 'scene') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + IssueJinzhiWebMallTokenRequest clone() => IssueJinzhiWebMallTokenRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + IssueJinzhiWebMallTokenRequest copyWith(void Function(IssueJinzhiWebMallTokenRequest) updates) => super.copyWith((message) => updates(message as IssueJinzhiWebMallTokenRequest)) as IssueJinzhiWebMallTokenRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static IssueJinzhiWebMallTokenRequest create() => IssueJinzhiWebMallTokenRequest._(); + IssueJinzhiWebMallTokenRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static IssueJinzhiWebMallTokenRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static IssueJinzhiWebMallTokenRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get trackingId => $_getSZ(0); + @$pb.TagNumber(1) + set trackingId($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasTrackingId() => $_has(0); + @$pb.TagNumber(1) + void clearTrackingId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get clickId => $_getSZ(1); + @$pb.TagNumber(2) + set clickId($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasClickId() => $_has(1); + @$pb.TagNumber(2) + void clearClickId() => clearField(2); + + @$pb.TagNumber(3) + $core.String get scene => $_getSZ(2); + @$pb.TagNumber(3) + set scene($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasScene() => $_has(2); + @$pb.TagNumber(3) + void clearScene() => clearField(3); +} + +class IssueJinzhiWebMallTokenReply extends $pb.GeneratedMessage { + factory IssueJinzhiWebMallTokenReply({ + $core.String? authToken, + $core.int? expiresIn, + $core.String? phone, + }) { + final $result = create(); + if (authToken != null) { + $result.authToken = authToken; + } + if (expiresIn != null) { + $result.expiresIn = expiresIn; + } + if (phone != null) { + $result.phone = phone; + } + return $result; + } + IssueJinzhiWebMallTokenReply._() : super(); + factory IssueJinzhiWebMallTokenReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory IssueJinzhiWebMallTokenReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'IssueJinzhiWebMallTokenReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'authToken') + ..a<$core.int>(2, _omitFieldNames ? '' : 'expiresIn', $pb.PbFieldType.O3) + ..aOS(3, _omitFieldNames ? '' : 'phone') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + IssueJinzhiWebMallTokenReply clone() => IssueJinzhiWebMallTokenReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + IssueJinzhiWebMallTokenReply copyWith(void Function(IssueJinzhiWebMallTokenReply) updates) => super.copyWith((message) => updates(message as IssueJinzhiWebMallTokenReply)) as IssueJinzhiWebMallTokenReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static IssueJinzhiWebMallTokenReply create() => IssueJinzhiWebMallTokenReply._(); + IssueJinzhiWebMallTokenReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static IssueJinzhiWebMallTokenReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static IssueJinzhiWebMallTokenReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get authToken => $_getSZ(0); + @$pb.TagNumber(1) + set authToken($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasAuthToken() => $_has(0); + @$pb.TagNumber(1) + void clearAuthToken() => clearField(1); + + @$pb.TagNumber(2) + $core.int get expiresIn => $_getIZ(1); + @$pb.TagNumber(2) + set expiresIn($core.int v) { $_setSignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasExpiresIn() => $_has(1); + @$pb.TagNumber(2) + void clearExpiresIn() => clearField(2); + + @$pb.TagNumber(3) + $core.String get phone => $_getSZ(2); + @$pb.TagNumber(3) + set phone($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasPhone() => $_has(2); + @$pb.TagNumber(3) + void clearPhone() => clearField(3); +} + +class IssueAppMallAuthTokenRequest extends $pb.GeneratedMessage { + factory IssueAppMallAuthTokenRequest() => create(); + IssueAppMallAuthTokenRequest._() : super(); + factory IssueAppMallAuthTokenRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory IssueAppMallAuthTokenRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'IssueAppMallAuthTokenRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + IssueAppMallAuthTokenRequest clone() => IssueAppMallAuthTokenRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + IssueAppMallAuthTokenRequest copyWith(void Function(IssueAppMallAuthTokenRequest) updates) => super.copyWith((message) => updates(message as IssueAppMallAuthTokenRequest)) as IssueAppMallAuthTokenRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static IssueAppMallAuthTokenRequest create() => IssueAppMallAuthTokenRequest._(); + IssueAppMallAuthTokenRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static IssueAppMallAuthTokenRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static IssueAppMallAuthTokenRequest? _defaultInstance; +} + +class IssueAppMallAuthTokenReply extends $pb.GeneratedMessage { + factory IssueAppMallAuthTokenReply({ + $core.String? authToken, + $core.int? expiresIn, + $core.String? sourceApp, + $core.String? scene, + }) { + final $result = create(); + if (authToken != null) { + $result.authToken = authToken; + } + if (expiresIn != null) { + $result.expiresIn = expiresIn; + } + if (sourceApp != null) { + $result.sourceApp = sourceApp; + } + if (scene != null) { + $result.scene = scene; + } + return $result; + } + IssueAppMallAuthTokenReply._() : super(); + factory IssueAppMallAuthTokenReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory IssueAppMallAuthTokenReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'IssueAppMallAuthTokenReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'authToken') + ..a<$core.int>(2, _omitFieldNames ? '' : 'expiresIn', $pb.PbFieldType.O3) + ..aOS(3, _omitFieldNames ? '' : 'sourceApp') + ..aOS(4, _omitFieldNames ? '' : 'scene') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + IssueAppMallAuthTokenReply clone() => IssueAppMallAuthTokenReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + IssueAppMallAuthTokenReply copyWith(void Function(IssueAppMallAuthTokenReply) updates) => super.copyWith((message) => updates(message as IssueAppMallAuthTokenReply)) as IssueAppMallAuthTokenReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static IssueAppMallAuthTokenReply create() => IssueAppMallAuthTokenReply._(); + IssueAppMallAuthTokenReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static IssueAppMallAuthTokenReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static IssueAppMallAuthTokenReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get authToken => $_getSZ(0); + @$pb.TagNumber(1) + set authToken($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasAuthToken() => $_has(0); + @$pb.TagNumber(1) + void clearAuthToken() => clearField(1); + + @$pb.TagNumber(2) + $core.int get expiresIn => $_getIZ(1); + @$pb.TagNumber(2) + set expiresIn($core.int v) { $_setSignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasExpiresIn() => $_has(1); + @$pb.TagNumber(2) + void clearExpiresIn() => clearField(2); + + @$pb.TagNumber(3) + $core.String get sourceApp => $_getSZ(2); + @$pb.TagNumber(3) + set sourceApp($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasSourceApp() => $_has(2); + @$pb.TagNumber(3) + void clearSourceApp() => clearField(3); + + @$pb.TagNumber(4) + $core.String get scene => $_getSZ(3); + @$pb.TagNumber(4) + set scene($core.String v) { $_setString(3, v); } + @$pb.TagNumber(4) + $core.bool hasScene() => $_has(3); + @$pb.TagNumber(4) + void clearScene() => clearField(4); +} + +class ProfileReply extends $pb.GeneratedMessage { + factory ProfileReply({ + UserInfo? user, + }) { + final $result = create(); + if (user != null) { + $result.user = user; + } + return $result; + } + ProfileReply._() : super(); + factory ProfileReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ProfileReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ProfileReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'user', subBuilder: UserInfo.create) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ProfileReply clone() => ProfileReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ProfileReply copyWith(void Function(ProfileReply) updates) => super.copyWith((message) => updates(message as ProfileReply)) as ProfileReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ProfileReply create() => ProfileReply._(); + ProfileReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ProfileReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ProfileReply? _defaultInstance; + + @$pb.TagNumber(1) + UserInfo get user => $_getN(0); + @$pb.TagNumber(1) + set user(UserInfo v) { setField(1, v); } + @$pb.TagNumber(1) + $core.bool hasUser() => $_has(0); + @$pb.TagNumber(1) + void clearUser() => clearField(1); + @$pb.TagNumber(1) + UserInfo ensureUser() => $_ensure(0); +} + +class DeleteAccountRequest extends $pb.GeneratedMessage { + factory DeleteAccountRequest({ + $core.String? smsCode, + }) { + final $result = create(); + if (smsCode != null) { + $result.smsCode = smsCode; + } + return $result; + } + DeleteAccountRequest._() : super(); + factory DeleteAccountRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory DeleteAccountRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DeleteAccountRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'smsCode') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + DeleteAccountRequest clone() => DeleteAccountRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + DeleteAccountRequest copyWith(void Function(DeleteAccountRequest) updates) => super.copyWith((message) => updates(message as DeleteAccountRequest)) as DeleteAccountRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeleteAccountRequest create() => DeleteAccountRequest._(); + DeleteAccountRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static DeleteAccountRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static DeleteAccountRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get smsCode => $_getSZ(0); + @$pb.TagNumber(1) + set smsCode($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasSmsCode() => $_has(0); + @$pb.TagNumber(1) + void clearSmsCode() => clearField(1); +} + +class DeleteAccountReply extends $pb.GeneratedMessage { + factory DeleteAccountReply() => create(); + DeleteAccountReply._() : super(); + factory DeleteAccountReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory DeleteAccountReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DeleteAccountReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + DeleteAccountReply clone() => DeleteAccountReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + DeleteAccountReply copyWith(void Function(DeleteAccountReply) updates) => super.copyWith((message) => updates(message as DeleteAccountReply)) as DeleteAccountReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeleteAccountReply create() => DeleteAccountReply._(); + DeleteAccountReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static DeleteAccountReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static DeleteAccountReply? _defaultInstance; +} + +/// 云备份:payload 为客户端序列化的资产 JSON(用户本人数据)。 +class PutBackupRequest extends $pb.GeneratedMessage { + factory PutBackupRequest({ + $core.String? payload, + }) { + final $result = create(); + if (payload != null) { + $result.payload = payload; + } + return $result; + } + PutBackupRequest._() : super(); + factory PutBackupRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory PutBackupRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'PutBackupRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'payload') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + PutBackupRequest clone() => PutBackupRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + PutBackupRequest copyWith(void Function(PutBackupRequest) updates) => super.copyWith((message) => updates(message as PutBackupRequest)) as PutBackupRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static PutBackupRequest create() => PutBackupRequest._(); + PutBackupRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static PutBackupRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static PutBackupRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get payload => $_getSZ(0); + @$pb.TagNumber(1) + set payload($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasPayload() => $_has(0); + @$pb.TagNumber(1) + void clearPayload() => clearField(1); +} + +class PutBackupReply extends $pb.GeneratedMessage { + factory PutBackupReply({ + $core.String? updatedAt, + $core.int? version, + }) { + final $result = create(); + if (updatedAt != null) { + $result.updatedAt = updatedAt; + } + if (version != null) { + $result.version = version; + } + return $result; + } + PutBackupReply._() : super(); + factory PutBackupReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory PutBackupReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'PutBackupReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'updatedAt') + ..a<$core.int>(2, _omitFieldNames ? '' : 'version', $pb.PbFieldType.O3) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + PutBackupReply clone() => PutBackupReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + PutBackupReply copyWith(void Function(PutBackupReply) updates) => super.copyWith((message) => updates(message as PutBackupReply)) as PutBackupReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static PutBackupReply create() => PutBackupReply._(); + PutBackupReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static PutBackupReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static PutBackupReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get updatedAt => $_getSZ(0); + @$pb.TagNumber(1) + set updatedAt($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasUpdatedAt() => $_has(0); + @$pb.TagNumber(1) + void clearUpdatedAt() => clearField(1); + + @$pb.TagNumber(2) + $core.int get version => $_getIZ(1); + @$pb.TagNumber(2) + set version($core.int v) { $_setSignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasVersion() => $_has(1); + @$pb.TagNumber(2) + void clearVersion() => clearField(2); +} + +class GetBackupReply extends $pb.GeneratedMessage { + factory GetBackupReply({ + $core.String? payload, + $core.String? updatedAt, + $core.int? version, + }) { + final $result = create(); + if (payload != null) { + $result.payload = payload; + } + if (updatedAt != null) { + $result.updatedAt = updatedAt; + } + if (version != null) { + $result.version = version; + } + return $result; + } + GetBackupReply._() : super(); + factory GetBackupReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory GetBackupReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GetBackupReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'payload') + ..aOS(2, _omitFieldNames ? '' : 'updatedAt') + ..a<$core.int>(3, _omitFieldNames ? '' : 'version', $pb.PbFieldType.O3) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + GetBackupReply clone() => GetBackupReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + GetBackupReply copyWith(void Function(GetBackupReply) updates) => super.copyWith((message) => updates(message as GetBackupReply)) as GetBackupReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetBackupReply create() => GetBackupReply._(); + GetBackupReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static GetBackupReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static GetBackupReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get payload => $_getSZ(0); + @$pb.TagNumber(1) + set payload($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasPayload() => $_has(0); + @$pb.TagNumber(1) + void clearPayload() => clearField(1); + + @$pb.TagNumber(2) + $core.String get updatedAt => $_getSZ(1); + @$pb.TagNumber(2) + set updatedAt($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasUpdatedAt() => $_has(1); + @$pb.TagNumber(2) + void clearUpdatedAt() => clearField(2); + + @$pb.TagNumber(3) + $core.int get version => $_getIZ(2); + @$pb.TagNumber(3) + set version($core.int v) { $_setSignedInt32(2, v); } + @$pb.TagNumber(3) + $core.bool hasVersion() => $_has(2); + @$pb.TagNumber(3) + void clearVersion() => clearField(3); +} + +class AccountServiceApi { + $pb.RpcClient _client; + AccountServiceApi(this._client); + + $async.Future sendSmsCode($pb.ClientContext? ctx, SendSmsCodeRequest request) => + _client.invoke(ctx, 'AccountService', 'SendSmsCode', request, SendSmsCodeReply()) + ; + $async.Future verifySmsCode($pb.ClientContext? ctx, VerifySmsCodeRequest request) => + _client.invoke(ctx, 'AccountService', 'VerifySmsCode', request, VerifySmsCodeReply()) + ; + $async.Future verifyOneKeyLogin($pb.ClientContext? ctx, VerifyOneKeyLoginRequest request) => + _client.invoke(ctx, 'AccountService', 'VerifyOneKeyLogin', request, VerifySmsCodeReply()) + ; + $async.Future issueJinzhiWebMallToken($pb.ClientContext? ctx, IssueJinzhiWebMallTokenRequest request) => + _client.invoke(ctx, 'AccountService', 'IssueJinzhiWebMallToken', request, IssueJinzhiWebMallTokenReply()) + ; + $async.Future issueAppMallAuthToken($pb.ClientContext? ctx, IssueAppMallAuthTokenRequest request) => + _client.invoke(ctx, 'AccountService', 'IssueAppMallAuthToken', request, IssueAppMallAuthTokenReply()) + ; + $async.Future getProfile($pb.ClientContext? ctx, $3.Empty request) => + _client.invoke(ctx, 'AccountService', 'GetProfile', request, ProfileReply()) + ; + $async.Future deleteAccount($pb.ClientContext? ctx, DeleteAccountRequest request) => + _client.invoke(ctx, 'AccountService', 'DeleteAccount', request, DeleteAccountReply()) + ; + $async.Future putBackup($pb.ClientContext? ctx, PutBackupRequest request) => + _client.invoke(ctx, 'AccountService', 'PutBackup', request, PutBackupReply()) + ; + $async.Future getBackup($pb.ClientContext? ctx, $3.Empty request) => + _client.invoke(ctx, 'AccountService', 'GetBackup', request, GetBackupReply()) + ; + $async.Future<$5.RegisterPushDeviceReply> registerPushDevice($pb.ClientContext? ctx, $5.RegisterPushDeviceRequest request) => + _client.invoke<$5.RegisterPushDeviceReply>(ctx, 'AccountService', 'RegisterPushDevice', request, $5.RegisterPushDeviceReply()) + ; + $async.Future<$5.PushPreferenceReply> updatePushPreference($pb.ClientContext? ctx, $5.UpdatePushPreferenceRequest request) => + _client.invoke<$5.PushPreferenceReply>(ctx, 'AccountService', 'UpdatePushPreference', request, $5.PushPreferenceReply()) + ; + $async.Future<$5.PushPreferenceReply> getPushPreference($pb.ClientContext? ctx, $3.Empty request) => + _client.invoke<$5.PushPreferenceReply>(ctx, 'AccountService', 'GetPushPreference', request, $5.PushPreferenceReply()) + ; +} + + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/models/pb/jinzhi/v1/ads.pb.dart b/lib/models/pb/jinzhi/v1/ads.pb.dart new file mode 100644 index 0000000..40daf1d --- /dev/null +++ b/lib/models/pb/jinzhi/v1/ads.pb.dart @@ -0,0 +1,868 @@ +// +// Generated code. Do not modify. +// source: jinzhi/v1/ads.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:async' as $async; +import 'dart:core' as $core; + +import 'package:fixnum/fixnum.dart' as $fixnum; +import 'package:protobuf/protobuf.dart' as $pb; + +import 'ads.pbenum.dart'; + +export 'ads.pbenum.dart'; + +class GetAdRequest extends $pb.GeneratedMessage { + factory GetAdRequest({ + $core.String? placement, + }) { + final $result = create(); + if (placement != null) { + $result.placement = placement; + } + return $result; + } + GetAdRequest._() : super(); + factory GetAdRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory GetAdRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GetAdRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'placement') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + GetAdRequest clone() => GetAdRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + GetAdRequest copyWith(void Function(GetAdRequest) updates) => super.copyWith((message) => updates(message as GetAdRequest)) as GetAdRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetAdRequest create() => GetAdRequest._(); + GetAdRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static GetAdRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static GetAdRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get placement => $_getSZ(0); + @$pb.TagNumber(1) + set placement($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasPlacement() => $_has(0); + @$pb.TagNumber(1) + void clearPlacement() => clearField(1); +} + +class GetAdReply extends $pb.GeneratedMessage { + factory GetAdReply({ + $core.bool? enabled, + AdCreative? ad, + $core.int? dailyCap, + }) { + final $result = create(); + if (enabled != null) { + $result.enabled = enabled; + } + if (ad != null) { + $result.ad = ad; + } + if (dailyCap != null) { + $result.dailyCap = dailyCap; + } + return $result; + } + GetAdReply._() : super(); + factory GetAdReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory GetAdReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GetAdReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOB(1, _omitFieldNames ? '' : 'enabled') + ..aOM(2, _omitFieldNames ? '' : 'ad', subBuilder: AdCreative.create) + ..a<$core.int>(3, _omitFieldNames ? '' : 'dailyCap', $pb.PbFieldType.O3) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + GetAdReply clone() => GetAdReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + GetAdReply copyWith(void Function(GetAdReply) updates) => super.copyWith((message) => updates(message as GetAdReply)) as GetAdReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetAdReply create() => GetAdReply._(); + GetAdReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static GetAdReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static GetAdReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.bool get enabled => $_getBF(0); + @$pb.TagNumber(1) + set enabled($core.bool v) { $_setBool(0, v); } + @$pb.TagNumber(1) + $core.bool hasEnabled() => $_has(0); + @$pb.TagNumber(1) + void clearEnabled() => clearField(1); + + @$pb.TagNumber(2) + AdCreative get ad => $_getN(1); + @$pb.TagNumber(2) + set ad(AdCreative v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasAd() => $_has(1); + @$pb.TagNumber(2) + void clearAd() => clearField(2); + @$pb.TagNumber(2) + AdCreative ensureAd() => $_ensure(1); + + @$pb.TagNumber(3) + $core.int get dailyCap => $_getIZ(2); + @$pb.TagNumber(3) + set dailyCap($core.int v) { $_setSignedInt32(2, v); } + @$pb.TagNumber(3) + $core.bool hasDailyCap() => $_has(2); + @$pb.TagNumber(3) + void clearDailyCap() => clearField(3); +} + +class ListAdsRequest extends $pb.GeneratedMessage { + factory ListAdsRequest({ + $core.String? placement, + }) { + final $result = create(); + if (placement != null) { + $result.placement = placement; + } + return $result; + } + ListAdsRequest._() : super(); + factory ListAdsRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ListAdsRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ListAdsRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'placement') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ListAdsRequest clone() => ListAdsRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ListAdsRequest copyWith(void Function(ListAdsRequest) updates) => super.copyWith((message) => updates(message as ListAdsRequest)) as ListAdsRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ListAdsRequest create() => ListAdsRequest._(); + ListAdsRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ListAdsRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ListAdsRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get placement => $_getSZ(0); + @$pb.TagNumber(1) + set placement($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasPlacement() => $_has(0); + @$pb.TagNumber(1) + void clearPlacement() => clearField(1); +} + +class ListAdsReply extends $pb.GeneratedMessage { + factory ListAdsReply({ + $core.bool? enabled, + $core.Iterable? ads, + $core.String? trackingId, + }) { + final $result = create(); + if (enabled != null) { + $result.enabled = enabled; + } + if (ads != null) { + $result.ads.addAll(ads); + } + if (trackingId != null) { + $result.trackingId = trackingId; + } + return $result; + } + ListAdsReply._() : super(); + factory ListAdsReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ListAdsReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ListAdsReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOB(1, _omitFieldNames ? '' : 'enabled') + ..pc(2, _omitFieldNames ? '' : 'ads', $pb.PbFieldType.PM, subBuilder: AdCreative.create) + ..aOS(3, _omitFieldNames ? '' : 'trackingId') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ListAdsReply clone() => ListAdsReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ListAdsReply copyWith(void Function(ListAdsReply) updates) => super.copyWith((message) => updates(message as ListAdsReply)) as ListAdsReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ListAdsReply create() => ListAdsReply._(); + ListAdsReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ListAdsReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ListAdsReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.bool get enabled => $_getBF(0); + @$pb.TagNumber(1) + set enabled($core.bool v) { $_setBool(0, v); } + @$pb.TagNumber(1) + $core.bool hasEnabled() => $_has(0); + @$pb.TagNumber(1) + void clearEnabled() => clearField(1); + + @$pb.TagNumber(2) + $core.List get ads => $_getList(1); + + @$pb.TagNumber(3) + $core.String get trackingId => $_getSZ(2); + @$pb.TagNumber(3) + set trackingId($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasTrackingId() => $_has(2); + @$pb.TagNumber(3) + void clearTrackingId() => clearField(3); +} + +class AdCreative extends $pb.GeneratedMessage { + factory AdCreative({ + $core.String? adId, + $core.String? placement, + $core.String? targetApp, + $core.String? title, + $core.String? subtitle, + $core.String? imageUrl, + MediaType? mediaType, + $core.String? ctaText, + $core.String? h5Url, + $core.String? trackingId, + $core.int? countdownSeconds, + $core.int? priority, + $fixnum.Int64? endAtMs, + $core.String? trackedUrl, + AdOpenMode? openMode, + $core.bool? entryPromoEnabled, + $core.String? entryBadgeText, + $core.String? entryTipTitle, + $core.String? entryTipBody, + MallPromotion? mallPromotion, + }) { + final $result = create(); + if (adId != null) { + $result.adId = adId; + } + if (placement != null) { + $result.placement = placement; + } + if (targetApp != null) { + $result.targetApp = targetApp; + } + if (title != null) { + $result.title = title; + } + if (subtitle != null) { + $result.subtitle = subtitle; + } + if (imageUrl != null) { + $result.imageUrl = imageUrl; + } + if (mediaType != null) { + $result.mediaType = mediaType; + } + if (ctaText != null) { + $result.ctaText = ctaText; + } + if (h5Url != null) { + $result.h5Url = h5Url; + } + if (trackingId != null) { + $result.trackingId = trackingId; + } + if (countdownSeconds != null) { + $result.countdownSeconds = countdownSeconds; + } + if (priority != null) { + $result.priority = priority; + } + if (endAtMs != null) { + $result.endAtMs = endAtMs; + } + if (trackedUrl != null) { + $result.trackedUrl = trackedUrl; + } + if (openMode != null) { + $result.openMode = openMode; + } + if (entryPromoEnabled != null) { + $result.entryPromoEnabled = entryPromoEnabled; + } + if (entryBadgeText != null) { + $result.entryBadgeText = entryBadgeText; + } + if (entryTipTitle != null) { + $result.entryTipTitle = entryTipTitle; + } + if (entryTipBody != null) { + $result.entryTipBody = entryTipBody; + } + if (mallPromotion != null) { + $result.mallPromotion = mallPromotion; + } + return $result; + } + AdCreative._() : super(); + factory AdCreative.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory AdCreative.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'AdCreative', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'adId') + ..aOS(2, _omitFieldNames ? '' : 'placement') + ..aOS(3, _omitFieldNames ? '' : 'targetApp') + ..aOS(4, _omitFieldNames ? '' : 'title') + ..aOS(5, _omitFieldNames ? '' : 'subtitle') + ..aOS(6, _omitFieldNames ? '' : 'imageUrl') + ..e(7, _omitFieldNames ? '' : 'mediaType', $pb.PbFieldType.OE, defaultOrMaker: MediaType.MEDIA_TYPE_UNSPECIFIED, valueOf: MediaType.valueOf, enumValues: MediaType.values) + ..aOS(8, _omitFieldNames ? '' : 'ctaText') + ..aOS(9, _omitFieldNames ? '' : 'h5Url') + ..aOS(10, _omitFieldNames ? '' : 'trackingId') + ..a<$core.int>(11, _omitFieldNames ? '' : 'countdownSeconds', $pb.PbFieldType.O3) + ..a<$core.int>(12, _omitFieldNames ? '' : 'priority', $pb.PbFieldType.O3) + ..aInt64(13, _omitFieldNames ? '' : 'endAtMs') + ..aOS(14, _omitFieldNames ? '' : 'trackedUrl') + ..e(15, _omitFieldNames ? '' : 'openMode', $pb.PbFieldType.OE, defaultOrMaker: AdOpenMode.AD_OPEN_MODE_UNSPECIFIED, valueOf: AdOpenMode.valueOf, enumValues: AdOpenMode.values) + ..aOB(16, _omitFieldNames ? '' : 'entryPromoEnabled') + ..aOS(17, _omitFieldNames ? '' : 'entryBadgeText') + ..aOS(18, _omitFieldNames ? '' : 'entryTipTitle') + ..aOS(19, _omitFieldNames ? '' : 'entryTipBody') + ..aOM(20, _omitFieldNames ? '' : 'mallPromotion', subBuilder: MallPromotion.create) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + AdCreative clone() => AdCreative()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + AdCreative copyWith(void Function(AdCreative) updates) => super.copyWith((message) => updates(message as AdCreative)) as AdCreative; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AdCreative create() => AdCreative._(); + AdCreative createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static AdCreative getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static AdCreative? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get adId => $_getSZ(0); + @$pb.TagNumber(1) + set adId($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasAdId() => $_has(0); + @$pb.TagNumber(1) + void clearAdId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get placement => $_getSZ(1); + @$pb.TagNumber(2) + set placement($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasPlacement() => $_has(1); + @$pb.TagNumber(2) + void clearPlacement() => clearField(2); + + @$pb.TagNumber(3) + $core.String get targetApp => $_getSZ(2); + @$pb.TagNumber(3) + set targetApp($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasTargetApp() => $_has(2); + @$pb.TagNumber(3) + void clearTargetApp() => clearField(3); + + @$pb.TagNumber(4) + $core.String get title => $_getSZ(3); + @$pb.TagNumber(4) + set title($core.String v) { $_setString(3, v); } + @$pb.TagNumber(4) + $core.bool hasTitle() => $_has(3); + @$pb.TagNumber(4) + void clearTitle() => clearField(4); + + @$pb.TagNumber(5) + $core.String get subtitle => $_getSZ(4); + @$pb.TagNumber(5) + set subtitle($core.String v) { $_setString(4, v); } + @$pb.TagNumber(5) + $core.bool hasSubtitle() => $_has(4); + @$pb.TagNumber(5) + void clearSubtitle() => clearField(5); + + @$pb.TagNumber(6) + $core.String get imageUrl => $_getSZ(5); + @$pb.TagNumber(6) + set imageUrl($core.String v) { $_setString(5, v); } + @$pb.TagNumber(6) + $core.bool hasImageUrl() => $_has(5); + @$pb.TagNumber(6) + void clearImageUrl() => clearField(6); + + @$pb.TagNumber(7) + MediaType get mediaType => $_getN(6); + @$pb.TagNumber(7) + set mediaType(MediaType v) { setField(7, v); } + @$pb.TagNumber(7) + $core.bool hasMediaType() => $_has(6); + @$pb.TagNumber(7) + void clearMediaType() => clearField(7); + + @$pb.TagNumber(8) + $core.String get ctaText => $_getSZ(7); + @$pb.TagNumber(8) + set ctaText($core.String v) { $_setString(7, v); } + @$pb.TagNumber(8) + $core.bool hasCtaText() => $_has(7); + @$pb.TagNumber(8) + void clearCtaText() => clearField(8); + + @$pb.TagNumber(9) + $core.String get h5Url => $_getSZ(8); + @$pb.TagNumber(9) + set h5Url($core.String v) { $_setString(8, v); } + @$pb.TagNumber(9) + $core.bool hasH5Url() => $_has(8); + @$pb.TagNumber(9) + void clearH5Url() => clearField(9); + + @$pb.TagNumber(10) + $core.String get trackingId => $_getSZ(9); + @$pb.TagNumber(10) + set trackingId($core.String v) { $_setString(9, v); } + @$pb.TagNumber(10) + $core.bool hasTrackingId() => $_has(9); + @$pb.TagNumber(10) + void clearTrackingId() => clearField(10); + + @$pb.TagNumber(11) + $core.int get countdownSeconds => $_getIZ(10); + @$pb.TagNumber(11) + set countdownSeconds($core.int v) { $_setSignedInt32(10, v); } + @$pb.TagNumber(11) + $core.bool hasCountdownSeconds() => $_has(10); + @$pb.TagNumber(11) + void clearCountdownSeconds() => clearField(11); + + @$pb.TagNumber(12) + $core.int get priority => $_getIZ(11); + @$pb.TagNumber(12) + set priority($core.int v) { $_setSignedInt32(11, v); } + @$pb.TagNumber(12) + $core.bool hasPriority() => $_has(11); + @$pb.TagNumber(12) + void clearPriority() => clearField(12); + + @$pb.TagNumber(13) + $fixnum.Int64 get endAtMs => $_getI64(12); + @$pb.TagNumber(13) + set endAtMs($fixnum.Int64 v) { $_setInt64(12, v); } + @$pb.TagNumber(13) + $core.bool hasEndAtMs() => $_has(12); + @$pb.TagNumber(13) + void clearEndAtMs() => clearField(13); + + @$pb.TagNumber(14) + $core.String get trackedUrl => $_getSZ(13); + @$pb.TagNumber(14) + set trackedUrl($core.String v) { $_setString(13, v); } + @$pb.TagNumber(14) + $core.bool hasTrackedUrl() => $_has(13); + @$pb.TagNumber(14) + void clearTrackedUrl() => clearField(14); + + @$pb.TagNumber(15) + AdOpenMode get openMode => $_getN(14); + @$pb.TagNumber(15) + set openMode(AdOpenMode v) { setField(15, v); } + @$pb.TagNumber(15) + $core.bool hasOpenMode() => $_has(14); + @$pb.TagNumber(15) + void clearOpenMode() => clearField(15); + + @$pb.TagNumber(16) + $core.bool get entryPromoEnabled => $_getBF(15); + @$pb.TagNumber(16) + set entryPromoEnabled($core.bool v) { $_setBool(15, v); } + @$pb.TagNumber(16) + $core.bool hasEntryPromoEnabled() => $_has(15); + @$pb.TagNumber(16) + void clearEntryPromoEnabled() => clearField(16); + + @$pb.TagNumber(17) + $core.String get entryBadgeText => $_getSZ(16); + @$pb.TagNumber(17) + set entryBadgeText($core.String v) { $_setString(16, v); } + @$pb.TagNumber(17) + $core.bool hasEntryBadgeText() => $_has(16); + @$pb.TagNumber(17) + void clearEntryBadgeText() => clearField(17); + + @$pb.TagNumber(18) + $core.String get entryTipTitle => $_getSZ(17); + @$pb.TagNumber(18) + set entryTipTitle($core.String v) { $_setString(17, v); } + @$pb.TagNumber(18) + $core.bool hasEntryTipTitle() => $_has(17); + @$pb.TagNumber(18) + void clearEntryTipTitle() => clearField(18); + + @$pb.TagNumber(19) + $core.String get entryTipBody => $_getSZ(18); + @$pb.TagNumber(19) + set entryTipBody($core.String v) { $_setString(18, v); } + @$pb.TagNumber(19) + $core.bool hasEntryTipBody() => $_has(18); + @$pb.TagNumber(19) + void clearEntryTipBody() => clearField(19); + + @$pb.TagNumber(20) + MallPromotion get mallPromotion => $_getN(19); + @$pb.TagNumber(20) + set mallPromotion(MallPromotion v) { setField(20, v); } + @$pb.TagNumber(20) + $core.bool hasMallPromotion() => $_has(19); + @$pb.TagNumber(20) + void clearMallPromotion() => clearField(20); + @$pb.TagNumber(20) + MallPromotion ensureMallPromotion() => $_ensure(19); +} + +/// MallPromotion is an optional, version-gated set of low-impact entry points +/// layered on top of the existing mall_tab creative. Older clients safely +/// ignore this message. +class MallPromotion extends $pb.GeneratedMessage { + factory MallPromotion({ + $core.bool? quoteCtaEnabled, + }) { + final $result = create(); + if (quoteCtaEnabled != null) { + $result.quoteCtaEnabled = quoteCtaEnabled; + } + return $result; + } + MallPromotion._() : super(); + factory MallPromotion.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory MallPromotion.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'MallPromotion', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOB(1, _omitFieldNames ? '' : 'quoteCtaEnabled') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + MallPromotion clone() => MallPromotion()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + MallPromotion copyWith(void Function(MallPromotion) updates) => super.copyWith((message) => updates(message as MallPromotion)) as MallPromotion; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static MallPromotion create() => MallPromotion._(); + MallPromotion createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static MallPromotion getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static MallPromotion? _defaultInstance; + + @$pb.TagNumber(1) + $core.bool get quoteCtaEnabled => $_getBF(0); + @$pb.TagNumber(1) + set quoteCtaEnabled($core.bool v) { $_setBool(0, v); } + @$pb.TagNumber(1) + $core.bool hasQuoteCtaEnabled() => $_has(0); + @$pb.TagNumber(1) + void clearQuoteCtaEnabled() => clearField(1); +} + +class ReportAdEventRequest extends $pb.GeneratedMessage { + factory ReportAdEventRequest({ + $core.String? eventName, + $core.String? placement, + $core.String? adId, + $core.String? trackingId, + $core.String? clickId, + $fixnum.Int64? clientTsMs, + $core.Map<$core.String, $core.String>? attrs, + $core.String? articleId, + }) { + final $result = create(); + if (eventName != null) { + $result.eventName = eventName; + } + if (placement != null) { + $result.placement = placement; + } + if (adId != null) { + $result.adId = adId; + } + if (trackingId != null) { + $result.trackingId = trackingId; + } + if (clickId != null) { + $result.clickId = clickId; + } + if (clientTsMs != null) { + $result.clientTsMs = clientTsMs; + } + if (attrs != null) { + $result.attrs.addAll(attrs); + } + if (articleId != null) { + $result.articleId = articleId; + } + return $result; + } + ReportAdEventRequest._() : super(); + factory ReportAdEventRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ReportAdEventRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ReportAdEventRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'eventName') + ..aOS(2, _omitFieldNames ? '' : 'placement') + ..aOS(3, _omitFieldNames ? '' : 'adId') + ..aOS(4, _omitFieldNames ? '' : 'trackingId') + ..aOS(5, _omitFieldNames ? '' : 'clickId') + ..aInt64(6, _omitFieldNames ? '' : 'clientTsMs') + ..m<$core.String, $core.String>(7, _omitFieldNames ? '' : 'attrs', entryClassName: 'ReportAdEventRequest.AttrsEntry', keyFieldType: $pb.PbFieldType.OS, valueFieldType: $pb.PbFieldType.OS, packageName: const $pb.PackageName('jinzhi.v1')) + ..aOS(8, _omitFieldNames ? '' : 'articleId') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ReportAdEventRequest clone() => ReportAdEventRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ReportAdEventRequest copyWith(void Function(ReportAdEventRequest) updates) => super.copyWith((message) => updates(message as ReportAdEventRequest)) as ReportAdEventRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ReportAdEventRequest create() => ReportAdEventRequest._(); + ReportAdEventRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ReportAdEventRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ReportAdEventRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get eventName => $_getSZ(0); + @$pb.TagNumber(1) + set eventName($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasEventName() => $_has(0); + @$pb.TagNumber(1) + void clearEventName() => clearField(1); + + @$pb.TagNumber(2) + $core.String get placement => $_getSZ(1); + @$pb.TagNumber(2) + set placement($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasPlacement() => $_has(1); + @$pb.TagNumber(2) + void clearPlacement() => clearField(2); + + @$pb.TagNumber(3) + $core.String get adId => $_getSZ(2); + @$pb.TagNumber(3) + set adId($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasAdId() => $_has(2); + @$pb.TagNumber(3) + void clearAdId() => clearField(3); + + @$pb.TagNumber(4) + $core.String get trackingId => $_getSZ(3); + @$pb.TagNumber(4) + set trackingId($core.String v) { $_setString(3, v); } + @$pb.TagNumber(4) + $core.bool hasTrackingId() => $_has(3); + @$pb.TagNumber(4) + void clearTrackingId() => clearField(4); + + @$pb.TagNumber(5) + $core.String get clickId => $_getSZ(4); + @$pb.TagNumber(5) + set clickId($core.String v) { $_setString(4, v); } + @$pb.TagNumber(5) + $core.bool hasClickId() => $_has(4); + @$pb.TagNumber(5) + void clearClickId() => clearField(5); + + @$pb.TagNumber(6) + $fixnum.Int64 get clientTsMs => $_getI64(5); + @$pb.TagNumber(6) + set clientTsMs($fixnum.Int64 v) { $_setInt64(5, v); } + @$pb.TagNumber(6) + $core.bool hasClientTsMs() => $_has(5); + @$pb.TagNumber(6) + void clearClientTsMs() => clearField(6); + + @$pb.TagNumber(7) + $core.Map<$core.String, $core.String> get attrs => $_getMap(6); + + @$pb.TagNumber(8) + $core.String get articleId => $_getSZ(7); + @$pb.TagNumber(8) + set articleId($core.String v) { $_setString(7, v); } + @$pb.TagNumber(8) + $core.bool hasArticleId() => $_has(7); + @$pb.TagNumber(8) + void clearArticleId() => clearField(8); +} + +class ReportAdEventReply extends $pb.GeneratedMessage { + factory ReportAdEventReply({ + $core.String? eventId, + $core.bool? accepted, + }) { + final $result = create(); + if (eventId != null) { + $result.eventId = eventId; + } + if (accepted != null) { + $result.accepted = accepted; + } + return $result; + } + ReportAdEventReply._() : super(); + factory ReportAdEventReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ReportAdEventReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ReportAdEventReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'eventId') + ..aOB(2, _omitFieldNames ? '' : 'accepted') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ReportAdEventReply clone() => ReportAdEventReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ReportAdEventReply copyWith(void Function(ReportAdEventReply) updates) => super.copyWith((message) => updates(message as ReportAdEventReply)) as ReportAdEventReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ReportAdEventReply create() => ReportAdEventReply._(); + ReportAdEventReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ReportAdEventReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ReportAdEventReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get eventId => $_getSZ(0); + @$pb.TagNumber(1) + set eventId($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasEventId() => $_has(0); + @$pb.TagNumber(1) + void clearEventId() => clearField(1); + + @$pb.TagNumber(2) + $core.bool get accepted => $_getBF(1); + @$pb.TagNumber(2) + set accepted($core.bool v) { $_setBool(1, v); } + @$pb.TagNumber(2) + $core.bool hasAccepted() => $_has(1); + @$pb.TagNumber(2) + void clearAccepted() => clearField(2); +} + +class AdsServiceApi { + $pb.RpcClient _client; + AdsServiceApi(this._client); + + $async.Future getAd($pb.ClientContext? ctx, GetAdRequest request) => + _client.invoke(ctx, 'AdsService', 'GetAd', request, GetAdReply()) + ; + $async.Future listAds($pb.ClientContext? ctx, ListAdsRequest request) => + _client.invoke(ctx, 'AdsService', 'ListAds', request, ListAdsReply()) + ; + $async.Future reportAdEvent($pb.ClientContext? ctx, ReportAdEventRequest request) => + _client.invoke(ctx, 'AdsService', 'ReportAdEvent', request, ReportAdEventReply()) + ; +} + + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/models/pb/jinzhi/v1/ads.pbenum.dart b/lib/models/pb/jinzhi/v1/ads.pbenum.dart new file mode 100644 index 0000000..2d06975 --- /dev/null +++ b/lib/models/pb/jinzhi/v1/ads.pbenum.dart @@ -0,0 +1,55 @@ +// +// Generated code. Do not modify. +// source: jinzhi/v1/ads.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +class AdOpenMode extends $pb.ProtobufEnum { + static const AdOpenMode AD_OPEN_MODE_UNSPECIFIED = AdOpenMode._(0, _omitEnumNames ? '' : 'AD_OPEN_MODE_UNSPECIFIED'); + static const AdOpenMode AD_OPEN_MODE_EXTERNAL = AdOpenMode._(1, _omitEnumNames ? '' : 'AD_OPEN_MODE_EXTERNAL'); + static const AdOpenMode AD_OPEN_MODE_IN_APP = AdOpenMode._(2, _omitEnumNames ? '' : 'AD_OPEN_MODE_IN_APP'); + static const AdOpenMode AD_OPEN_MODE_NATIVE_APP = AdOpenMode._(3, _omitEnumNames ? '' : 'AD_OPEN_MODE_NATIVE_APP'); + + static const $core.List values = [ + AD_OPEN_MODE_UNSPECIFIED, + AD_OPEN_MODE_EXTERNAL, + AD_OPEN_MODE_IN_APP, + AD_OPEN_MODE_NATIVE_APP, + ]; + + static final $core.Map<$core.int, AdOpenMode> _byValue = $pb.ProtobufEnum.initByValue(values); + static AdOpenMode? valueOf($core.int value) => _byValue[value]; + + const AdOpenMode._($core.int v, $core.String n) : super(v, n); +} + +class MediaType extends $pb.ProtobufEnum { + static const MediaType MEDIA_TYPE_UNSPECIFIED = MediaType._(0, _omitEnumNames ? '' : 'MEDIA_TYPE_UNSPECIFIED'); + static const MediaType MEDIA_TYPE_IMAGE = MediaType._(1, _omitEnumNames ? '' : 'MEDIA_TYPE_IMAGE'); + static const MediaType MEDIA_TYPE_GIF = MediaType._(2, _omitEnumNames ? '' : 'MEDIA_TYPE_GIF'); + static const MediaType MEDIA_TYPE_VIDEO = MediaType._(3, _omitEnumNames ? '' : 'MEDIA_TYPE_VIDEO'); + + static const $core.List values = [ + MEDIA_TYPE_UNSPECIFIED, + MEDIA_TYPE_IMAGE, + MEDIA_TYPE_GIF, + MEDIA_TYPE_VIDEO, + ]; + + static final $core.Map<$core.int, MediaType> _byValue = $pb.ProtobufEnum.initByValue(values); + static MediaType? valueOf($core.int value) => _byValue[value]; + + const MediaType._($core.int v, $core.String n) : super(v, n); +} + + +const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/lib/models/pb/jinzhi/v1/push.pb.dart b/lib/models/pb/jinzhi/v1/push.pb.dart new file mode 100644 index 0000000..2ba6a80 --- /dev/null +++ b/lib/models/pb/jinzhi/v1/push.pb.dart @@ -0,0 +1,336 @@ +// +// Generated code. Do not modify. +// source: jinzhi/v1/push.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +import 'push.pbenum.dart'; + +export 'push.pbenum.dart'; + +class RegisterPushDeviceRequest extends $pb.GeneratedMessage { + factory RegisterPushDeviceRequest({ + $core.String? umengDeviceToken, + Platform? platform, + $core.String? appVersion, + $core.bool? permissionGranted, + }) { + final $result = create(); + if (umengDeviceToken != null) { + $result.umengDeviceToken = umengDeviceToken; + } + if (platform != null) { + $result.platform = platform; + } + if (appVersion != null) { + $result.appVersion = appVersion; + } + if (permissionGranted != null) { + $result.permissionGranted = permissionGranted; + } + return $result; + } + RegisterPushDeviceRequest._() : super(); + factory RegisterPushDeviceRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RegisterPushDeviceRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'RegisterPushDeviceRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'umengDeviceToken') + ..e(2, _omitFieldNames ? '' : 'platform', $pb.PbFieldType.OE, defaultOrMaker: Platform.PLATFORM_UNSPECIFIED, valueOf: Platform.valueOf, enumValues: Platform.values) + ..aOS(3, _omitFieldNames ? '' : 'appVersion') + ..aOB(4, _omitFieldNames ? '' : 'permissionGranted') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + RegisterPushDeviceRequest clone() => RegisterPushDeviceRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RegisterPushDeviceRequest copyWith(void Function(RegisterPushDeviceRequest) updates) => super.copyWith((message) => updates(message as RegisterPushDeviceRequest)) as RegisterPushDeviceRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RegisterPushDeviceRequest create() => RegisterPushDeviceRequest._(); + RegisterPushDeviceRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RegisterPushDeviceRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RegisterPushDeviceRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get umengDeviceToken => $_getSZ(0); + @$pb.TagNumber(1) + set umengDeviceToken($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasUmengDeviceToken() => $_has(0); + @$pb.TagNumber(1) + void clearUmengDeviceToken() => clearField(1); + + @$pb.TagNumber(2) + Platform get platform => $_getN(1); + @$pb.TagNumber(2) + set platform(Platform v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasPlatform() => $_has(1); + @$pb.TagNumber(2) + void clearPlatform() => clearField(2); + + @$pb.TagNumber(3) + $core.String get appVersion => $_getSZ(2); + @$pb.TagNumber(3) + set appVersion($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasAppVersion() => $_has(2); + @$pb.TagNumber(3) + void clearAppVersion() => clearField(3); + + @$pb.TagNumber(4) + $core.bool get permissionGranted => $_getBF(3); + @$pb.TagNumber(4) + set permissionGranted($core.bool v) { $_setBool(3, v); } + @$pb.TagNumber(4) + $core.bool hasPermissionGranted() => $_has(3); + @$pb.TagNumber(4) + void clearPermissionGranted() => clearField(4); +} + +class RegisterPushDeviceReply extends $pb.GeneratedMessage { + factory RegisterPushDeviceReply({ + $core.String? deviceRecordId, + $core.bool? firstRegistration, + }) { + final $result = create(); + if (deviceRecordId != null) { + $result.deviceRecordId = deviceRecordId; + } + if (firstRegistration != null) { + $result.firstRegistration = firstRegistration; + } + return $result; + } + RegisterPushDeviceReply._() : super(); + factory RegisterPushDeviceReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RegisterPushDeviceReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'RegisterPushDeviceReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'deviceRecordId') + ..aOB(2, _omitFieldNames ? '' : 'firstRegistration') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + RegisterPushDeviceReply clone() => RegisterPushDeviceReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RegisterPushDeviceReply copyWith(void Function(RegisterPushDeviceReply) updates) => super.copyWith((message) => updates(message as RegisterPushDeviceReply)) as RegisterPushDeviceReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RegisterPushDeviceReply create() => RegisterPushDeviceReply._(); + RegisterPushDeviceReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RegisterPushDeviceReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RegisterPushDeviceReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get deviceRecordId => $_getSZ(0); + @$pb.TagNumber(1) + set deviceRecordId($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasDeviceRecordId() => $_has(0); + @$pb.TagNumber(1) + void clearDeviceRecordId() => clearField(1); + + @$pb.TagNumber(2) + $core.bool get firstRegistration => $_getBF(1); + @$pb.TagNumber(2) + set firstRegistration($core.bool v) { $_setBool(1, v); } + @$pb.TagNumber(2) + $core.bool hasFirstRegistration() => $_has(1); + @$pb.TagNumber(2) + void clearFirstRegistration() => clearField(2); +} + +/// 成对的 *_set 字段保留 proto3 bool 的局部更新语义。 +class UpdatePushPreferenceRequest extends $pb.GeneratedMessage { + factory UpdatePushPreferenceRequest({ + $core.bool? watchlistUpdatesEnabled, + $core.bool? watchlistUpdatesEnabledSet, + $core.bool? recallPushEnabled, + $core.bool? recallPushEnabledSet, + }) { + final $result = create(); + if (watchlistUpdatesEnabled != null) { + $result.watchlistUpdatesEnabled = watchlistUpdatesEnabled; + } + if (watchlistUpdatesEnabledSet != null) { + $result.watchlistUpdatesEnabledSet = watchlistUpdatesEnabledSet; + } + if (recallPushEnabled != null) { + $result.recallPushEnabled = recallPushEnabled; + } + if (recallPushEnabledSet != null) { + $result.recallPushEnabledSet = recallPushEnabledSet; + } + return $result; + } + UpdatePushPreferenceRequest._() : super(); + factory UpdatePushPreferenceRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory UpdatePushPreferenceRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'UpdatePushPreferenceRequest', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOB(1, _omitFieldNames ? '' : 'watchlistUpdatesEnabled') + ..aOB(2, _omitFieldNames ? '' : 'watchlistUpdatesEnabledSet') + ..aOB(3, _omitFieldNames ? '' : 'recallPushEnabled') + ..aOB(4, _omitFieldNames ? '' : 'recallPushEnabledSet') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + UpdatePushPreferenceRequest clone() => UpdatePushPreferenceRequest()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + UpdatePushPreferenceRequest copyWith(void Function(UpdatePushPreferenceRequest) updates) => super.copyWith((message) => updates(message as UpdatePushPreferenceRequest)) as UpdatePushPreferenceRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static UpdatePushPreferenceRequest create() => UpdatePushPreferenceRequest._(); + UpdatePushPreferenceRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static UpdatePushPreferenceRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static UpdatePushPreferenceRequest? _defaultInstance; + + /// 沿用研值/研听契约名;在金值中表示“资产与关注内容更新”。 + @$pb.TagNumber(1) + $core.bool get watchlistUpdatesEnabled => $_getBF(0); + @$pb.TagNumber(1) + set watchlistUpdatesEnabled($core.bool v) { $_setBool(0, v); } + @$pb.TagNumber(1) + $core.bool hasWatchlistUpdatesEnabled() => $_has(0); + @$pb.TagNumber(1) + void clearWatchlistUpdatesEnabled() => clearField(1); + + @$pb.TagNumber(2) + $core.bool get watchlistUpdatesEnabledSet => $_getBF(1); + @$pb.TagNumber(2) + set watchlistUpdatesEnabledSet($core.bool v) { $_setBool(1, v); } + @$pb.TagNumber(2) + $core.bool hasWatchlistUpdatesEnabledSet() => $_has(1); + @$pb.TagNumber(2) + void clearWatchlistUpdatesEnabledSet() => clearField(2); + + @$pb.TagNumber(3) + $core.bool get recallPushEnabled => $_getBF(2); + @$pb.TagNumber(3) + set recallPushEnabled($core.bool v) { $_setBool(2, v); } + @$pb.TagNumber(3) + $core.bool hasRecallPushEnabled() => $_has(2); + @$pb.TagNumber(3) + void clearRecallPushEnabled() => clearField(3); + + @$pb.TagNumber(4) + $core.bool get recallPushEnabledSet => $_getBF(3); + @$pb.TagNumber(4) + set recallPushEnabledSet($core.bool v) { $_setBool(3, v); } + @$pb.TagNumber(4) + $core.bool hasRecallPushEnabledSet() => $_has(3); + @$pb.TagNumber(4) + void clearRecallPushEnabledSet() => clearField(4); +} + +class PushPreferenceReply extends $pb.GeneratedMessage { + factory PushPreferenceReply({ + $core.bool? watchlistUpdatesEnabled, + $core.bool? recallPushEnabled, + }) { + final $result = create(); + if (watchlistUpdatesEnabled != null) { + $result.watchlistUpdatesEnabled = watchlistUpdatesEnabled; + } + if (recallPushEnabled != null) { + $result.recallPushEnabled = recallPushEnabled; + } + return $result; + } + PushPreferenceReply._() : super(); + factory PushPreferenceReply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory PushPreferenceReply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'PushPreferenceReply', package: const $pb.PackageName(_omitMessageNames ? '' : 'jinzhi.v1'), createEmptyInstance: create) + ..aOB(1, _omitFieldNames ? '' : 'watchlistUpdatesEnabled') + ..aOB(2, _omitFieldNames ? '' : 'recallPushEnabled') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + PushPreferenceReply clone() => PushPreferenceReply()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + PushPreferenceReply copyWith(void Function(PushPreferenceReply) updates) => super.copyWith((message) => updates(message as PushPreferenceReply)) as PushPreferenceReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static PushPreferenceReply create() => PushPreferenceReply._(); + PushPreferenceReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static PushPreferenceReply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static PushPreferenceReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.bool get watchlistUpdatesEnabled => $_getBF(0); + @$pb.TagNumber(1) + set watchlistUpdatesEnabled($core.bool v) { $_setBool(0, v); } + @$pb.TagNumber(1) + $core.bool hasWatchlistUpdatesEnabled() => $_has(0); + @$pb.TagNumber(1) + void clearWatchlistUpdatesEnabled() => clearField(1); + + @$pb.TagNumber(2) + $core.bool get recallPushEnabled => $_getBF(1); + @$pb.TagNumber(2) + set recallPushEnabled($core.bool v) { $_setBool(1, v); } + @$pb.TagNumber(2) + $core.bool hasRecallPushEnabled() => $_has(1); + @$pb.TagNumber(2) + void clearRecallPushEnabled() => clearField(2); +} + + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/models/pb/jinzhi/v1/push.pbenum.dart b/lib/models/pb/jinzhi/v1/push.pbenum.dart new file mode 100644 index 0000000..38ab6f1 --- /dev/null +++ b/lib/models/pb/jinzhi/v1/push.pbenum.dart @@ -0,0 +1,35 @@ +// +// Generated code. Do not modify. +// source: jinzhi/v1/push.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +/// Platform 标识 U-Push token 所属原生平台。 +class Platform extends $pb.ProtobufEnum { + static const Platform PLATFORM_UNSPECIFIED = Platform._(0, _omitEnumNames ? '' : 'PLATFORM_UNSPECIFIED'); + static const Platform PLATFORM_IOS = Platform._(1, _omitEnumNames ? '' : 'PLATFORM_IOS'); + static const Platform PLATFORM_ANDROID = Platform._(2, _omitEnumNames ? '' : 'PLATFORM_ANDROID'); + + static const $core.List values = [ + PLATFORM_UNSPECIFIED, + PLATFORM_IOS, + PLATFORM_ANDROID, + ]; + + static final $core.Map<$core.int, Platform> _byValue = $pb.ProtobufEnum.initByValue(values); + static Platform? valueOf($core.int value) => _byValue[value]; + + const Platform._($core.int v, $core.String n) : super(v, n); +} + + +const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/pubspec.yaml b/pubspec.yaml index 157c243..df6846e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: jinzhi_flutter_app description: "金值 - 贵金属资产管家" publish_to: 'none' -version: 1.3.0+1 +version: 1.1.0+1 environment: sdk: ^3.9.2 diff --git a/test/common/network/kratos_error_interceptor_test.dart b/test/common/network/kratos_error_interceptor_test.dart new file mode 100644 index 0000000..30280e2 --- /dev/null +++ b/test/common/network/kratos_error_interceptor_test.dart @@ -0,0 +1,57 @@ +import 'dart:typed_data'; + +import 'package:dio/dio.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jinzhi_flutter_app/common/network/api_error_message.dart'; +import 'package:jinzhi_flutter_app/common/network/kratos_error_interceptor.dart'; +import 'package:jinzhi_flutter_app/models/pb/errors/errors.pb.dart' as errors; + +void main() { + test('decodes a protobuf error body before it reaches the UI', () async { + final dio = Dio() + ..httpClientAdapter = _ErrorAdapter( + errors.Status( + code: 401, + reason: 'INVALID_CODE', + message: '验证码错误', + ).writeToBuffer(), + ) + ..interceptors.add(KratosErrorInterceptor()); + + await expectLater( + dio.delete( + '/api/v1/me', + options: Options(responseType: ResponseType.bytes), + ), + throwsA( + isA().having( + (error) => kratosDisplayMessage(error, fallback: '注销失败,请重试'), + 'display message', + '验证码错误', + ), + ), + ); + }); +} + +class _ErrorAdapter implements HttpClientAdapter { + _ErrorAdapter(this._body); + + final List _body; + + @override + Future fetch( + RequestOptions options, + Stream? requestStream, + Future? cancelFuture, + ) async => ResponseBody.fromBytes( + _body, + 401, + headers: { + 'content-type': ['application/x-protobuf'], + }, + ); + + @override + void close({bool force = false}) {} +} diff --git a/test/features/auth/auth_api_test.dart b/test/features/auth/auth_api_test.dart new file mode 100644 index 0000000..80b2b31 --- /dev/null +++ b/test/features/auth/auth_api_test.dart @@ -0,0 +1,40 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jinzhi_flutter_app/features/auth/data/auth_api.dart'; +import 'package:jinzhi_flutter_app/models/pb/jinzhi/v1/account.pb.dart' as pb; + +void main() { + test('keeps the verified one-key login contract', () async { + String? requestToken; + final dio = Dio() + ..interceptors.add( + InterceptorsWrapper( + onRequest: (options, handler) { + requestToken = pb.VerifyOneKeyLoginRequest.fromBuffer( + options.data as List, + ).loginToken; + handler.resolve( + Response>( + requestOptions: options, + statusCode: 200, + data: pb.VerifySmsCodeReply( + accessToken: 'access-token', + user: pb.UserInfo( + id: 'user-1', + phone: '13800138000', + createdAt: '2026-09-08T00:00:00Z', + ), + ).writeToBuffer(), + ), + ); + }, + ), + ); + + final result = await AuthApi(dio).verifyOneKeyLogin('umeng-token'); + + expect(requestToken, 'umeng-token'); + expect(result.accessToken, 'access-token'); + expect(result.user.phone, '13800138000'); + }); +} diff --git a/test/features/auth/auth_controller_test.dart b/test/features/auth/auth_controller_test.dart new file mode 100644 index 0000000..3ff2c92 --- /dev/null +++ b/test/features/auth/auth_controller_test.dart @@ -0,0 +1,216 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jinzhi_flutter_app/common/storage/secure_identity_storage.dart'; +import 'package:jinzhi_flutter_app/common/storage/storage_keys.dart'; +import 'package:jinzhi_flutter_app/features/auth/application/auth_controller.dart'; +import 'package:jinzhi_flutter_app/features/auth/data/auth_models.dart'; +import 'package:jinzhi_flutter_app/models/pb/jinzhi/v1/account.pb.dart' as pb; +import 'package:shared_preferences/shared_preferences.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() => SharedPreferences.setMockInitialValues({})); + + test('restores a token by loading the real profile', () async { + final fixture = await _Fixture.create(); + await fixture.identity.saveJwt('token'); + + await fixture.controller.retryBootstrap(); + + final auth = fixture.controller.state.value! as LoggedInAuthState; + expect(auth.token, 'token'); + expect(auth.user.id, 'user-1'); + expect(auth.user.phone, '13800138000'); + expect(fixture.preferences.getString(storageUserId), 'user-1'); + }); + + test('deletes the server account before clearing local identity', () async { + final fixture = await _Fixture.create(); + await fixture.identity.saveJwt('token'); + await fixture.controller.retryBootstrap(); + + await fixture.controller.deleteAccount('123456'); + + expect(fixture.deletedWithCode, '123456'); + expect(fixture.controller.state.value, isA()); + expect(await fixture.identity.readJwt(), isNull); + expect(fixture.preferences.getString(storageUserId), isNull); + }); + + test('keeps the local session when server deletion fails', () async { + final fixture = await _Fixture.create(); + await fixture.identity.saveJwt('token'); + await fixture.controller.retryBootstrap(); + fixture.failDeletion = true; + + await expectLater( + fixture.controller.deleteAccount('123456'), + throwsA(isA()), + ); + + expect(fixture.controller.state.value, isA()); + expect(await fixture.identity.readJwt(), 'token'); + expect(fixture.preferences.getString(storageUserId), 'user-1'); + }); + + test('clears an unauthorized restored session', () async { + final fixture = await _Fixture.create(); + await fixture.identity.saveJwt('expired'); + fixture.profileUnauthorized = true; + + await fixture.controller.retryBootstrap(); + + expect(fixture.controller.state.value, isA()); + expect(await fixture.identity.readJwt(), isNull); + }); + + test('keeps a valid token and cached profile on a network failure', () async { + final fixture = await _Fixture.create(); + await fixture.identity.saveJwt('token'); + await fixture.preferences.setString('jinzhi_mock_logged_in', '13900139000'); + fixture.failProfile = true; + + await fixture.controller.retryBootstrap(); + + final auth = fixture.controller.state.value! as LoggedInAuthState; + expect(auth.user.id, isEmpty); + expect(auth.user.phone, '13900139000'); + expect(await fixture.identity.readJwt(), 'token'); + expect(fixture.preferences.getString(storageUserPhone), '13900139000'); + expect(fixture.preferences.containsKey('jinzhi_mock_logged_in'), isFalse); + }); + + test('keeps the verified one-key login state shape', () async { + final fixture = await _Fixture.create(); + + await fixture.controller.verifyOneKeyLogin('umeng-token'); + + final auth = fixture.controller.state.value! as LoggedInAuthState; + expect(auth.token, 'one-key-token'); + expect(auth.user.phone, '13800138000'); + expect(await fixture.identity.readJwt(), 'one-key-token'); + }); +} + +class _Fixture { + _Fixture({ + required this.preferences, + required this.identity, + required this.controller, + }); + + final SharedPreferences preferences; + final SecureIdentityStorage identity; + final AuthController controller; + bool failDeletion = false; + bool failProfile = false; + bool profileUnauthorized = false; + String? deletedWithCode; + + static Future<_Fixture> create() async { + final preferences = await SharedPreferences.getInstance(); + final identity = SecureIdentityStorage( + preferences, + secureStorage: _MemorySecureStorage(), + ); + late _Fixture fixture; + final dio = Dio() + ..interceptors.add( + InterceptorsWrapper( + onRequest: (options, handler) { + if (options.path == '/api/v1/me' && options.method == 'GET') { + if (fixture.failProfile) { + handler.reject(DioException(requestOptions: options)); + return; + } + if (fixture.profileUnauthorized) { + handler.reject( + DioException( + requestOptions: options, + response: Response( + requestOptions: options, + statusCode: 401, + ), + ), + ); + return; + } + handler.resolve( + Response>( + requestOptions: options, + statusCode: 200, + data: pb.ProfileReply( + user: pb.UserInfo( + id: 'user-1', + phone: '13800138000', + createdAt: '2026-09-08T00:00:00Z', + ), + ).writeToBuffer(), + ), + ); + return; + } + if (options.path == '/api/v1/me' && options.method == 'DELETE') { + fixture.deletedWithCode = pb.DeleteAccountRequest.fromBuffer( + options.data as List, + ).smsCode; + if (fixture.failDeletion) { + handler.reject(DioException(requestOptions: options)); + } else { + handler.resolve( + Response>( + requestOptions: options, + statusCode: 200, + data: const [], + ), + ); + } + return; + } + if (options.path == '/api/v1/auth/onekey/verify') { + handler.resolve( + Response>( + requestOptions: options, + statusCode: 200, + data: pb.VerifySmsCodeReply( + accessToken: 'one-key-token', + user: pb.UserInfo(phone: '13800138000'), + ).writeToBuffer(), + ), + ); + return; + } + handler.next(options); + }, + ), + ); + final controller = AuthController( + dio, + identityStorage: identity, + preferences: preferences, + autoBootstrap: false, + ); + fixture = _Fixture( + preferences: preferences, + identity: identity, + controller: controller, + ); + return fixture; + } +} + +class _MemorySecureStorage implements SecureKeyValueStorage { + final Map _values = {}; + + @override + Future delete(String key) async => _values.remove(key); + + @override + Future read(String key) async => _values[key]; + + @override + Future write(String key, String value) async { + _values[key] = value; + } +} diff --git a/test/features/mall/mall_bridge_arguments_test.dart b/test/features/mall/mall_bridge_arguments_test.dart index a137cfe..7017383 100644 --- a/test/features/mall/mall_bridge_arguments_test.dart +++ b/test/features/mall/mall_bridge_arguments_test.dart @@ -4,6 +4,14 @@ import 'package:jinzhi_flutter_app/features/mall/application/mall_tab_config.dar void main() { test('preserves Jinzhi attribution while syncing H5 login', () { final arguments = buildEnterWebMallBridgeArguments( + config: const MallTabConfig( + enabled: true, + url: 'https://mall.example.com/', + adId: 'latest-ad', + trackingId: 'latest-tracking', + clickId: 'latest-click', + entryRequest: MallEntryRequest.quoteRetailGold, + ), currentUri: Uri.parse( 'https://mall.example.com/?from=jinzhi&source_app=jinzhi' '&source_entry=market_quote&placement=mall_tab&hook_type=quote_cta' @@ -15,11 +23,6 @@ void main() { authToken: 'mall-token', scene: 'web_mall', sourceApp: 'jinzhi', - attribution: const MallClickAttribution( - adId: 'latest-ad', - trackingId: 'latest-tracking', - clickId: 'latest-click', - ), ); expect(arguments['from'], 'jinzhi'); diff --git a/test/features/mall/mall_tab_config_controller_test.dart b/test/features/mall/mall_tab_config_controller_test.dart new file mode 100644 index 0000000..a64574e --- /dev/null +++ b/test/features/mall/mall_tab_config_controller_test.dart @@ -0,0 +1,66 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jinzhi_flutter_app/features/mall/application/mall_tab_config.dart'; +import 'package:jinzhi_flutter_app/models/pb/jinzhi/v1/ads.pb.dart' as pb; + +void main() { + test('keeps click attribution and reports the full generated event', () async { + pb.ReportAdEventRequest? clickEvent; + final dio = Dio() + ..interceptors.add( + InterceptorsWrapper( + onRequest: (options, handler) { + if (options.path == '/api/v1/ads') { + handler.resolve( + Response>( + requestOptions: options, + statusCode: 200, + data: pb.GetAdReply( + enabled: true, + ad: pb.AdCreative( + adId: 'ad-1', + h5Url: 'https://mall.example.com', + trackingId: 'tracking-1', + openMode: pb.AdOpenMode.AD_OPEN_MODE_IN_APP, + ), + ).writeToBuffer(), + ), + ); + return; + } + if (options.path == '/api/v1/ads/events') { + final event = pb.ReportAdEventRequest.fromBuffer( + options.data as List, + ); + if (event.eventName == 'ad_click') clickEvent = event; + handler.resolve( + Response>( + requestOptions: options, + statusCode: 200, + data: pb.ReportAdEventReply().writeToBuffer(), + ), + ); + return; + } + handler.next(options); + }, + ), + ); + final controller = MallTabConfigController(dio, autoRefresh: false); + await controller.refresh(); + + final clicked = await controller.recordClick( + request: MallEntryRequest.quoteRetailGold, + ); + + expect(clicked.clickId, isNotEmpty); + expect(controller.state.clickId, clicked.clickId); + expect(clickEvent?.clickId, clicked.clickId); + expect(clickEvent?.attrs['source_app'], 'jinzhi'); + expect(clickEvent?.attrs['source_entry'], 'market_quote'); + expect(clickEvent?.attrs['hook_type'], 'quote_cta'); + expect(clickEvent?.attrs['entry_scene'], 'retail'); + expect(clickEvent?.attrs['entry_metal'], 'au'); + expect(clickEvent?.attrs['open_mode'], 'in_app'); + }); +} diff --git a/test/features/mall/mall_tab_config_test.dart b/test/features/mall/mall_tab_config_test.dart index 622b764..fac9f17 100644 --- a/test/features/mall/mall_tab_config_test.dart +++ b/test/features/mall/mall_tab_config_test.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; import 'package:jinzhi_flutter_app/features/mall/application/mall_tab_config.dart'; +import 'package:jinzhi_flutter_app/models/pb/jinzhi/v1/ads.pb.dart' as pb; void main() { test('builds the resident mall URL with Jinzhi attribution and theme', () { @@ -102,6 +103,27 @@ void main() { expect(config.entryTipTitle, '新人礼:¥360 铂金订购券'); expect(config.entryTipBody, '点击这里进商城'); }); + + test('parses generated mall promotion and open-mode fields', () { + final response = pb.GetAdReply( + enabled: true, + ad: pb.AdCreative( + adId: 'ad-1', + title: '会员商城', + h5Url: 'https://mall.example.com', + trackingId: 'tracking-1', + openMode: pb.AdOpenMode.AD_OPEN_MODE_NATIVE_APP, + mallPromotion: pb.MallPromotion(quoteCtaEnabled: true), + ), + ); + + final config = parseMallTabConfigResponse(response.writeToBuffer()); + + expect(config.isVisible, isTrue); + expect(config.openMode, 'native_app'); + expect(config.quoteCtaEnabled, isTrue); + expect(config.allowedHosts, {'mall.example.com'}); + }); } List _stringField(int tag, String value) => diff --git a/test/features/web/web_page_test.dart b/test/features/web/web_page_test.dart index 55990d6..31de902 100644 --- a/test/features/web/web_page_test.dart +++ b/test/features/web/web_page_test.dart @@ -2,13 +2,14 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:jinzhi_flutter_app/features/web/presentation/web_page.dart'; void main() { - test('accepts only http and https URLs with a host', () { + test('accepts HTTPS and rejects unsafe or remote HTTP URLs', () { expect( parseSafeWebUri('https://h5.example.com/page')?.host, 'h5.example.com', ); expect(parseSafeWebUri('mailto:test@example.com'), isNull); expect(parseSafeWebUri('javascript:alert(1)'), isNull); + expect(parseSafeWebUri('http://example.com/page'), isNull); expect(parseSafeWebUri(''), isNull); }); }