完善移动端错误提示中文化
This commit is contained in:
@@ -1,8 +1,79 @@
|
|||||||
|
// 功能描述:封装工作人员端 HTTP 请求,并将服务端错误转换为安全、可读的中文提示。
|
||||||
|
// 版本:1.1.0
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
|
||||||
|
const Map<int, String> _apiErrorMessages = {
|
||||||
|
404: '记录不存在',
|
||||||
|
1001: '请求标识缺失',
|
||||||
|
1002: '登录信息缺失,请重新登录',
|
||||||
|
1003: '接口密钥缺失',
|
||||||
|
1004: '请求头参数缺失',
|
||||||
|
1101: '暂无数据',
|
||||||
|
1102: '请求解析失败',
|
||||||
|
1103: '请填写必填参数',
|
||||||
|
1104: '无权执行此操作',
|
||||||
|
1105: '服务端数据处理失败',
|
||||||
|
1106: '服务端数据处理失败',
|
||||||
|
1107: '服务内部错误,请稍后重试',
|
||||||
|
1108: '密码错误',
|
||||||
|
1110: '账号已禁用',
|
||||||
|
1111: '当前记录已停用',
|
||||||
|
1112: '记录不存在',
|
||||||
|
1301: '登录状态已失效,请重新登录',
|
||||||
|
1302: '登录状态已失效,请重新登录',
|
||||||
|
1303: '登录状态已失效,请重新登录',
|
||||||
|
1304: '登录状态已失效,请重新登录',
|
||||||
|
1305: '登录状态已失效,请重新登录',
|
||||||
|
1306: '登录状态已失效,请重新登录',
|
||||||
|
1307: '登录状态已变更,请重新登录',
|
||||||
|
1308: '登录已过期,请重新登录',
|
||||||
|
1309: '登录状态已失效,请重新登录',
|
||||||
|
1310: '登录状态已失效,请重新登录',
|
||||||
|
1311: '登录状态已失效,请重新登录',
|
||||||
|
1312: '登录状态已失效,请重新登录',
|
||||||
|
1313: '登录失败,请稍后重试',
|
||||||
|
1314: '登录状态已失效,请重新登录',
|
||||||
|
1501: '服务暂时不可用,请稍后重试',
|
||||||
|
1502: '服务暂时不可用,请稍后重试',
|
||||||
|
1503: '服务暂时不可用,请稍后重试',
|
||||||
|
1504: '服务暂时不可用,请稍后重试',
|
||||||
|
1505: '服务暂时不可用,请稍后重试',
|
||||||
|
1506: '服务暂时不可用,请稍后重试',
|
||||||
|
1507: '服务暂时不可用,请稍后重试',
|
||||||
|
1702: '操作已取消',
|
||||||
|
1703: '操作失败,请稍后重试',
|
||||||
|
1704: '请求参数错误',
|
||||||
|
1705: '请求超时,请稍后重试',
|
||||||
|
1706: '记录已存在',
|
||||||
|
1707: '无权执行此操作',
|
||||||
|
1708: '系统繁忙,请稍后重试',
|
||||||
|
1709: '当前状态不支持此操作',
|
||||||
|
1710: '操作已中止',
|
||||||
|
1711: '输入内容超出允许范围',
|
||||||
|
1712: '当前功能暂不支持',
|
||||||
|
1713: '服务暂时不可用,请稍后重试',
|
||||||
|
1714: '服务数据异常,请稍后重试',
|
||||||
|
1715: '请先登录',
|
||||||
|
};
|
||||||
|
|
||||||
|
final RegExp _chineseCharacterPattern = RegExp(r'[\u3400-\u9fff]');
|
||||||
|
|
||||||
|
/// 将服务端错误转换为中文用户提示。
|
||||||
|
///
|
||||||
|
/// [code] 是稳定错误码,[message] 是服务端原始消息;返回值不会透传未知英文技术信息。
|
||||||
|
String localizeApiErrorMessage(int code, String? message) {
|
||||||
|
final mappedMessage = _apiErrorMessages[code];
|
||||||
|
if (mappedMessage != null) return mappedMessage;
|
||||||
|
|
||||||
|
final originalMessage = message?.trim() ?? '';
|
||||||
|
if (_chineseCharacterPattern.hasMatch(originalMessage)) return originalMessage;
|
||||||
|
return '操作失败,请稍后重试';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 表示客户端可识别并可安全展示的接口异常。
|
||||||
class ApiException implements Exception {
|
class ApiException implements Exception {
|
||||||
const ApiException(this.code, this.message);
|
const ApiException(this.code, this.message);
|
||||||
|
|
||||||
@@ -13,6 +84,7 @@ class ApiException implements Exception {
|
|||||||
String toString() => message;
|
String toString() => message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 负责工作人员端统一 HTTP 请求、鉴权头和响应解析。
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
ApiClient(this._tokenProvider, {http.Client? client, String? baseUrl})
|
ApiClient(this._tokenProvider, {http.Client? client, String? baseUrl})
|
||||||
: _client = client ?? http.Client(),
|
: _client = client ?? http.Client(),
|
||||||
@@ -53,7 +125,7 @@ class ApiClient {
|
|||||||
filename: file.name,
|
filename: file.name,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final response = await http.Response.fromStream(await request.send());
|
final response = await _sendRequest(request);
|
||||||
final details = _decode(response);
|
final details = _decode(response);
|
||||||
return jsonMap(details)['uri'] as String? ?? '';
|
return jsonMap(details)['uri'] as String? ?? '';
|
||||||
}
|
}
|
||||||
@@ -73,22 +145,40 @@ class ApiClient {
|
|||||||
request.headers['content-type'] = 'application/json; charset=UTF-8';
|
request.headers['content-type'] = 'application/json; charset=UTF-8';
|
||||||
request.body = jsonEncode(body);
|
request.body = jsonEncode(body);
|
||||||
}
|
}
|
||||||
final response = await http.Response.fromStream(
|
final response = await _sendRequest(request);
|
||||||
await _client.send(request),
|
|
||||||
);
|
|
||||||
return _decode(response);
|
return _decode(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 发送请求并统一处理网络连接异常。
|
||||||
|
Future<http.Response> _sendRequest(http.BaseRequest request) async {
|
||||||
|
try {
|
||||||
|
return await http.Response.fromStream(await _client.send(request));
|
||||||
|
} catch (_) {
|
||||||
|
throw const ApiException(500, '网络连接失败,请稍后重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解析统一响应结构,并按错误码生成中文提示。
|
||||||
Object? _decode(http.Response response) {
|
Object? _decode(http.Response response) {
|
||||||
if (response.statusCode < 200 || response.statusCode >= 300) {
|
if (response.statusCode < 200 || response.statusCode >= 300) {
|
||||||
throw ApiException(response.statusCode, '网络请求失败(${response.statusCode})');
|
throw ApiException(response.statusCode, '网络请求失败(${response.statusCode})');
|
||||||
}
|
}
|
||||||
final decoded = jsonDecode(response.body);
|
Object? decoded;
|
||||||
|
try {
|
||||||
|
decoded = jsonDecode(response.body);
|
||||||
|
} on FormatException {
|
||||||
|
throw const ApiException(500, '服务端响应格式错误');
|
||||||
|
}
|
||||||
if (decoded is! Map<String, Object?>) {
|
if (decoded is! Map<String, Object?>) {
|
||||||
throw const ApiException(500, '服务端响应格式错误');
|
throw const ApiException(500, '服务端响应格式错误');
|
||||||
}
|
}
|
||||||
final code = (decoded['code'] as num?)?.toInt() ?? 500;
|
final code = (decoded['code'] as num?)?.toInt() ?? 500;
|
||||||
if (code != 0) throw ApiException(code, decoded['message'] as String? ?? '操作失败');
|
if (code != 0) {
|
||||||
|
throw ApiException(
|
||||||
|
code,
|
||||||
|
localizeApiErrorMessage(code, decoded['message'] as String?),
|
||||||
|
);
|
||||||
|
}
|
||||||
return decoded['details'];
|
return decoded['details'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
|
// 功能描述:提供工作人员端手机号密码登录界面及本地输入校验。
|
||||||
|
// 版本:1.1.0
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../../../app/dependencies.dart';
|
import '../../../app/dependencies.dart';
|
||||||
|
import '../../../data/services/api_client.dart';
|
||||||
|
|
||||||
|
/// 工作人员端手机号密码登录页面。
|
||||||
class LoginPage extends StatefulWidget {
|
class LoginPage extends StatefulWidget {
|
||||||
const LoginPage({required this.session, super.key});
|
const LoginPage({required this.session, super.key});
|
||||||
|
|
||||||
@@ -11,22 +15,41 @@ class LoginPage extends StatefulWidget {
|
|||||||
State<LoginPage> createState() => _LoginPageState();
|
State<LoginPage> createState() => _LoginPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 管理登录输入、提交状态和错误提示。
|
||||||
class _LoginPageState extends State<LoginPage> {
|
class _LoginPageState extends State<LoginPage> {
|
||||||
|
static final RegExp _phonePattern = RegExp(r'^1[3-9]\d{9}$');
|
||||||
|
|
||||||
final _phone = TextEditingController();
|
final _phone = TextEditingController();
|
||||||
final _password = TextEditingController();
|
final _password = TextEditingController();
|
||||||
bool _busy = false;
|
bool _busy = false;
|
||||||
String? _error;
|
String? _error;
|
||||||
|
String? _phoneError;
|
||||||
bool _obscurePassword = true;
|
bool _obscurePassword = true;
|
||||||
|
|
||||||
|
/// 校验手机号并提交登录请求。
|
||||||
Future<void> _login() async {
|
Future<void> _login() async {
|
||||||
|
final phone = _phone.text.trim();
|
||||||
|
if (!_phonePattern.hasMatch(phone)) {
|
||||||
|
setState(() {
|
||||||
|
_phoneError = '请输入正确的11位手机号';
|
||||||
|
_error = null;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_busy = true;
|
_busy = true;
|
||||||
|
_phoneError = null;
|
||||||
_error = null;
|
_error = null;
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await widget.session.login(_phone.text.trim(), _password.text);
|
await widget.session.login(phone, _password.text);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (mounted) setState(() => _error = error.toString());
|
// 登录接口使用统一密码错误码,页面不区分账号是否存在,避免泄露账号状态。
|
||||||
|
final message = error is ApiException
|
||||||
|
? (error.code == 1108 ? '手机号或密码错误' : error.toString())
|
||||||
|
: '登录失败,请稍后重试';
|
||||||
|
if (mounted) setState(() => _error = message);
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _busy = false);
|
if (mounted) setState(() => _busy = false);
|
||||||
}
|
}
|
||||||
@@ -84,9 +107,13 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
TextField(
|
TextField(
|
||||||
controller: _phone,
|
controller: _phone,
|
||||||
keyboardType: TextInputType.phone,
|
keyboardType: TextInputType.phone,
|
||||||
decoration: const InputDecoration(
|
onChanged: (_) {
|
||||||
|
if (_phoneError != null) setState(() => _phoneError = null);
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
labelText: '工作人员手机号',
|
labelText: '工作人员手机号',
|
||||||
prefixIcon: Icon(Icons.phone_outlined),
|
prefixIcon: const Icon(Icons.phone_outlined),
|
||||||
|
errorText: _phoneError,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 14),
|
const SizedBox(height: 14),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.0"
|
version: "2.7.0"
|
||||||
async:
|
async:
|
||||||
@@ -14,7 +14,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.13.1"
|
version: "2.13.1"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
@@ -22,7 +22,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
characters:
|
characters:
|
||||||
@@ -30,7 +30,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.1"
|
version: "1.4.1"
|
||||||
clock:
|
clock:
|
||||||
@@ -38,7 +38,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: clock
|
name: clock
|
||||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.2"
|
version: "1.1.2"
|
||||||
code_assets:
|
code_assets:
|
||||||
@@ -46,7 +46,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: code_assets
|
name: code_assets
|
||||||
sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8
|
sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.1"
|
||||||
collection:
|
collection:
|
||||||
@@ -54,39 +54,39 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.19.1"
|
version: "1.19.1"
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: connectivity_plus
|
name: connectivity_plus
|
||||||
sha256: "25cebb79dfe304022550e0c3c893ae051ded07183d2607f7b88473cb3ade33cb"
|
sha256: "762c99f890ca8bf87f7337236f99edd42793843bc6c3631da294a76653a54bd0"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.3.0"
|
version: "7.3.1"
|
||||||
connectivity_plus_platform_interface:
|
connectivity_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: connectivity_plus_platform_interface
|
name: connectivity_plus_platform_interface
|
||||||
sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed"
|
sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
cross_file:
|
cross_file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cross_file
|
name: cross_file
|
||||||
sha256: "92c9c43c383bfa1c32079d3bc492d55d6d4318044b7b47edaff8971cbb555c51"
|
sha256: f141ea4f277af142a0356955707f6556f37b03947d39d55585981a06ca437bd6
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.5+4"
|
version: "0.3.5+5"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.7"
|
version: "3.0.7"
|
||||||
cryptography:
|
cryptography:
|
||||||
@@ -94,7 +94,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: cryptography
|
name: cryptography
|
||||||
sha256: "3eda3029d34ec9095a27a198ac9785630fe525c0eb6a49f3d575272f8e792ef0"
|
sha256: "3eda3029d34ec9095a27a198ac9785630fe525c0eb6a49f3d575272f8e792ef0"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.9.0"
|
version: "2.9.0"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
@@ -102,23 +102,23 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
|
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.9"
|
version: "1.0.9"
|
||||||
dbus:
|
dbus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dbus
|
name: dbus
|
||||||
sha256: "0ce9b0a839e6dee59a37a623d2fc26a35bbbe6404213e419b0d6411023d62645"
|
sha256: a48d5da28e89bd02196e80d81ed8d7954923d00a0f4a68cc20b575038f023383
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.14"
|
version: "0.7.15"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.3"
|
||||||
ffi:
|
ffi:
|
||||||
@@ -126,7 +126,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
|
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
ffi_leak_tracker:
|
ffi_leak_tracker:
|
||||||
@@ -134,47 +134,47 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: ffi_leak_tracker
|
name: ffi_leak_tracker
|
||||||
sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97"
|
sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.2"
|
version: "0.1.2"
|
||||||
file_selector_linux:
|
file_selector_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file_selector_linux
|
name: file_selector_linux
|
||||||
sha256: "2567f398e06ac72dcf2e98a0c95df2a9edd03c2c2e0cacd4780f20cdf56263a0"
|
sha256: da76400e7872ce7637ffdce12749ec24169c25f6195c28372208e65a24bcd2ab
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.4"
|
version: "0.9.4+1"
|
||||||
file_selector_macos:
|
file_selector_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file_selector_macos
|
name: file_selector_macos
|
||||||
sha256: "5e0bbe9c312416f1787a68259ea1505b52f258c587f12920422671807c4d618a"
|
sha256: d57c62362766b5e7ae739448650b66c6aab7a68ba7ecc65e04018652645ae0f4
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.5"
|
version: "0.9.5+1"
|
||||||
file_selector_platform_interface:
|
file_selector_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file_selector_platform_interface
|
name: file_selector_platform_interface
|
||||||
sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85"
|
sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.0"
|
version: "2.7.0"
|
||||||
file_selector_windows:
|
file_selector_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file_selector_windows
|
name: file_selector_windows
|
||||||
sha256: "62197474ae75893a62df75939c777763d39c2bc5f73ce5b88497208bc269abfd"
|
sha256: fbefc5fb92c6d3cbe8d284a2cd971b593bb07d2cd6da8557b81a862250b4acec
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.3+5"
|
version: "0.9.3+6"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: fixnum
|
name: fixnum
|
||||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
flutter:
|
flutter:
|
||||||
@@ -187,7 +187,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: flutter_lints
|
name: flutter_lints
|
||||||
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
|
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
version: "6.0.0"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
@@ -195,7 +195,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: flutter_plugin_android_lifecycle
|
name: flutter_plugin_android_lifecycle
|
||||||
sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785"
|
sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.35"
|
version: "2.0.35"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
@@ -203,7 +203,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: flutter_secure_storage
|
name: flutter_secure_storage
|
||||||
sha256: "7686b1d6a29985dcbb808c59518226e603e3bfa7c0ddfd1a0d00e4cda77c868e"
|
sha256: "7686b1d6a29985dcbb808c59518226e603e3bfa7c0ddfd1a0d00e4cda77c868e"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.3.1"
|
version: "10.3.1"
|
||||||
flutter_secure_storage_darwin:
|
flutter_secure_storage_darwin:
|
||||||
@@ -211,31 +211,31 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: flutter_secure_storage_darwin
|
name: flutter_secure_storage_darwin
|
||||||
sha256: "82329fa5cdf343773b1b6897dea959105a29f092454259edff92f9f6637e8149"
|
sha256: "82329fa5cdf343773b1b6897dea959105a29f092454259edff92f9f6637e8149"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.2"
|
version: "0.3.2"
|
||||||
flutter_secure_storage_linux:
|
flutter_secure_storage_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_secure_storage_linux
|
name: flutter_secure_storage_linux
|
||||||
sha256: a5f35ddab43cf5c8215d2feb4ce1957851f28c5c37e6f04335066a0602087bf5
|
sha256: "76fa9c841b3b1619fc5b5bc36efc7d158fa2356f223b6caeb1d0c80a54168546"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.2"
|
||||||
flutter_secure_storage_platform_interface:
|
flutter_secure_storage_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_secure_storage_platform_interface
|
name: flutter_secure_storage_platform_interface
|
||||||
sha256: "8ceea1223bee3c6ac1a22dabd8feefc550e4729b3675de4b5900f55afcb435d6"
|
sha256: "788060052712555182aba55ecb5f8b6e5cb9cfe8f776c83249a61fe3ce877db4"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.3"
|
||||||
flutter_secure_storage_web:
|
flutter_secure_storage_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_secure_storage_web
|
name: flutter_secure_storage_web
|
||||||
sha256: "073a62b3aeb866ab4ce795f960413948e51e5a42a9b0c8333b6daf5bb3208a1c"
|
sha256: "073a62b3aeb866ab4ce795f960413948e51e5a42a9b0c8333b6daf5bb3208a1c"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
flutter_secure_storage_windows:
|
flutter_secure_storage_windows:
|
||||||
@@ -243,7 +243,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: flutter_secure_storage_windows
|
name: flutter_secure_storage_windows
|
||||||
sha256: "471951813a97006d899db4948acc654a4f28c440083ea08178935ce20b173ec1"
|
sha256: "471951813a97006d899db4948acc654a4f28c440083ea08178935ce20b173ec1"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.2"
|
version: "4.2.2"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -261,7 +261,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: geoclue
|
name: geoclue
|
||||||
sha256: c2a998c77474fc57aa00c6baa2928e58f4b267649057a1c76738656e9dbd2a7f
|
sha256: c2a998c77474fc57aa00c6baa2928e58f4b267649057a1c76738656e9dbd2a7f
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.1"
|
version: "0.1.1"
|
||||||
geolocator:
|
geolocator:
|
||||||
@@ -269,7 +269,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: geolocator
|
name: geolocator
|
||||||
sha256: e146a6d63776582651e97a79cbe459f8e1211b100101fadcd84db83361fa599f
|
sha256: e146a6d63776582651e97a79cbe459f8e1211b100101fadcd84db83361fa599f
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.0.3"
|
version: "14.0.3"
|
||||||
geolocator_android:
|
geolocator_android:
|
||||||
@@ -277,7 +277,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: geolocator_android
|
name: geolocator_android
|
||||||
sha256: "86ea1654e4f61ff51466848e91c116b422d6010ea269fda0fbe1af7e9e742ce1"
|
sha256: "86ea1654e4f61ff51466848e91c116b422d6010ea269fda0fbe1af7e9e742ce1"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.3"
|
version: "5.0.3"
|
||||||
geolocator_apple:
|
geolocator_apple:
|
||||||
@@ -285,7 +285,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: geolocator_apple
|
name: geolocator_apple
|
||||||
sha256: "853803d6bb1713c094e935b4a5ae5f19c0308acf81da13fa9ff84fb4c70c0b73"
|
sha256: "853803d6bb1713c094e935b4a5ae5f19c0308acf81da13fa9ff84fb4c70c0b73"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.14"
|
version: "2.3.14"
|
||||||
geolocator_linux:
|
geolocator_linux:
|
||||||
@@ -293,23 +293,23 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: geolocator_linux
|
name: geolocator_linux
|
||||||
sha256: "3da7420f11c3496511a5bd3c18fd67b88e5659f12e46b7ce00a788f6996e850a"
|
sha256: "3da7420f11c3496511a5bd3c18fd67b88e5659f12e46b7ce00a788f6996e850a"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.6"
|
version: "0.2.6"
|
||||||
geolocator_platform_interface:
|
geolocator_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: geolocator_platform_interface
|
name: geolocator_platform_interface
|
||||||
sha256: cdb082e4f048b69da244117b7914cc60d2a8897546ffaa4f2529c786ded7aee2
|
sha256: "94db8255dc183d268765df682580440617ca35877fc82cacb5420ad03b86198d"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.8"
|
version: "4.3.0"
|
||||||
geolocator_web:
|
geolocator_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: geolocator_web
|
name: geolocator_web
|
||||||
sha256: "19e485a0f8d6a88abcf9c53cba3a4105e14b7435ed8ac1c108c067b938fe8429"
|
sha256: "19e485a0f8d6a88abcf9c53cba3a4105e14b7435ed8ac1c108c067b938fe8429"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.4"
|
version: "4.1.4"
|
||||||
geolocator_windows:
|
geolocator_windows:
|
||||||
@@ -317,23 +317,23 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: geolocator_windows
|
name: geolocator_windows
|
||||||
sha256: "175435404d20278ffd220de83c2ca293b73db95eafbdc8131fe8609be1421eb6"
|
sha256: "175435404d20278ffd220de83c2ca293b73db95eafbdc8131fe8609be1421eb6"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.5"
|
version: "0.2.5"
|
||||||
go_router:
|
go_router:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: go_router
|
name: go_router
|
||||||
sha256: "5922b2861e2235a3504896f0d6fa07d84141b480cf52eecd2f42cd25585a9e8a"
|
sha256: d7a3576cb312649eaa51f2356450aed686085fb58fcdebda5b359aa951eef7ea
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "17.3.0"
|
version: "17.5.0"
|
||||||
gsettings:
|
gsettings:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: gsettings
|
name: gsettings
|
||||||
sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c"
|
sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.8"
|
version: "0.2.8"
|
||||||
heqi_design_system:
|
heqi_design_system:
|
||||||
@@ -348,7 +348,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: hooks
|
name: hooks
|
||||||
sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba"
|
sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.2"
|
||||||
http:
|
http:
|
||||||
@@ -356,7 +356,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.0"
|
version: "1.6.0"
|
||||||
http_parser:
|
http_parser:
|
||||||
@@ -364,7 +364,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.2"
|
version: "4.1.2"
|
||||||
image_picker:
|
image_picker:
|
||||||
@@ -372,39 +372,39 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: image_picker
|
name: image_picker
|
||||||
sha256: d8402284df184bc05f4a2210c6c23983b0720f4cd87cbd05c5390a78af602667
|
sha256: d8402284df184bc05f4a2210c6c23983b0720f4cd87cbd05c5390a78af602667
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.3"
|
version: "1.2.3"
|
||||||
image_picker_android:
|
image_picker_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_android
|
name: image_picker_android
|
||||||
sha256: "6f3a1995eafb000333174fae92202622033b0ee7fd917a6cd3730295264df84a"
|
sha256: f71f4f3a9c5dbbe39800b31839cb3b305aac403a7cfbddf8331cf179d813b72a
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.13+19"
|
version: "0.8.13+21"
|
||||||
image_picker_for_web:
|
image_picker_for_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_for_web
|
name: image_picker_for_web
|
||||||
sha256: "66257a3191ab360d23a55c8241c91a6e329d31e94efa7be9cf7a212e65850214"
|
sha256: "66257a3191ab360d23a55c8241c91a6e329d31e94efa7be9cf7a212e65850214"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
image_picker_ios:
|
image_picker_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_ios
|
name: image_picker_ios
|
||||||
sha256: b9c4a438a9ff4f60808c9cf0039b93a42bb6c2211ef6ebb647394b2b3fa84588
|
sha256: ee3885b6fcd71958fbc79770dd194c63371439d536d69c47b279171a486482ae
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.13+6"
|
version: "0.8.13+7"
|
||||||
image_picker_linux:
|
image_picker_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_linux
|
name: image_picker_linux
|
||||||
sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4"
|
sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.2"
|
version: "0.2.2"
|
||||||
image_picker_macos:
|
image_picker_macos:
|
||||||
@@ -412,7 +412,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: image_picker_macos
|
name: image_picker_macos
|
||||||
sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91"
|
sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.2+1"
|
version: "0.2.2+1"
|
||||||
image_picker_platform_interface:
|
image_picker_platform_interface:
|
||||||
@@ -420,7 +420,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: image_picker_platform_interface
|
name: image_picker_platform_interface
|
||||||
sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c"
|
sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.1"
|
version: "2.11.1"
|
||||||
image_picker_windows:
|
image_picker_windows:
|
||||||
@@ -428,31 +428,39 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: image_picker_windows
|
name: image_picker_windows
|
||||||
sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae
|
sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.2"
|
version: "0.2.2"
|
||||||
jni:
|
jni:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: jni
|
name: jni
|
||||||
sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f
|
sha256: f038e58b4dc2c9037f50e233175086337e0b305e356d28211bf55f21c504cbd3
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.3"
|
||||||
jni_flutter:
|
jni_flutter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: jni_flutter
|
name: jni_flutter
|
||||||
sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6"
|
sha256: b2310cdd4c18c65c081ab141a41efa94aa26c65431803703ece51996f174f351
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.3"
|
||||||
|
jni_util:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: jni_util
|
||||||
|
sha256: "1ba86da04a5f2bf18fde2edb235587e70c5b0fc5bd4ba955f46b00942c3fc35f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.0.2"
|
version: "11.0.2"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
@@ -460,7 +468,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.10"
|
version: "3.0.10"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
@@ -468,7 +476,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: leak_tracker_testing
|
name: leak_tracker_testing
|
||||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.2"
|
||||||
lints:
|
lints:
|
||||||
@@ -476,7 +484,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: lints
|
name: lints
|
||||||
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
|
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.0"
|
version: "6.1.0"
|
||||||
logging:
|
logging:
|
||||||
@@ -484,7 +492,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: logging
|
name: logging
|
||||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
matcher:
|
matcher:
|
||||||
@@ -492,7 +500,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.19"
|
version: "0.12.19"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
@@ -500,7 +508,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.0"
|
version: "0.13.0"
|
||||||
meta:
|
meta:
|
||||||
@@ -508,47 +516,47 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
|
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: mime
|
name: mime
|
||||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
sha256: bd47de35f07e27267e69c8c8b22edf9473bfee170a60d60fcc93730c5144b7f6
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.1.0"
|
||||||
nm:
|
nm:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: nm
|
name: nm
|
||||||
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
|
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.0"
|
version: "0.5.0"
|
||||||
objective_c:
|
objective_c:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: objective_c
|
name: objective_c
|
||||||
sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed"
|
sha256: b7fb95a6d9a4f009edd63dc5ac69f07420b23a16161c6dd8660290b59c602e8e
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.4.1"
|
version: "9.5.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_config
|
name: package_config
|
||||||
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
|
sha256: ffcf4cf3d6c0b74ac43708d9f56625506e8a68aa935abe9d267a7330f320eb5d
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "3.0.0"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
sha256: "127e1751e37ffb2ff4658beeaca77bad0c27bf5f932bd3a501c2296926d4b481"
|
sha256: "127e1751e37ffb2ff4658beeaca77bad0c27bf5f932bd3a501c2296926d4b481"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.2.1"
|
version: "10.2.1"
|
||||||
package_info_plus_platform_interface:
|
package_info_plus_platform_interface:
|
||||||
@@ -556,7 +564,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: package_info_plus_platform_interface
|
name: package_info_plus_platform_interface
|
||||||
sha256: db762cb2f4f25ee60fb6359773861b0f199e00b90d237bd85a76a1e806b46ef4
|
sha256: db762cb2f4f25ee60fb6359773861b0f199e00b90d237bd85a76a1e806b46ef4
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.1.0"
|
||||||
path:
|
path:
|
||||||
@@ -564,7 +572,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
path_provider:
|
path_provider:
|
||||||
@@ -572,7 +580,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825
|
sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.6"
|
version: "2.1.6"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
@@ -580,7 +588,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
|
sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.1"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
@@ -588,7 +596,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: path_provider_foundation
|
name: path_provider_foundation
|
||||||
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.0"
|
version: "2.6.0"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
@@ -596,7 +604,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: path_provider_linux
|
name: path_provider_linux
|
||||||
sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16"
|
sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.2"
|
version: "2.2.2"
|
||||||
path_provider_platform_interface:
|
path_provider_platform_interface:
|
||||||
@@ -604,7 +612,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda"
|
sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
@@ -612,7 +620,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: path_provider_windows
|
name: path_provider_windows
|
||||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
petitparser:
|
petitparser:
|
||||||
@@ -620,7 +628,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
|
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.2"
|
version: "7.0.2"
|
||||||
platform:
|
platform:
|
||||||
@@ -628,7 +636,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.6"
|
version: "3.1.6"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
@@ -636,23 +644,23 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
sha256: "261236774e8b1d69cfc6b9eabbc96c40f25e7a2d6b171f3385d4f65d5734fb24"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.1"
|
||||||
record_use:
|
record_use:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: record_use
|
name: record_use
|
||||||
sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
|
sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.6.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
@@ -665,7 +673,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: source_span
|
name: source_span
|
||||||
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.2"
|
version: "1.10.2"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
@@ -673,7 +681,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.12.1"
|
version: "1.12.1"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
@@ -681,7 +689,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
@@ -689,7 +697,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: string_scanner
|
name: string_scanner
|
||||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.1"
|
version: "1.4.1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
@@ -697,7 +705,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: term_glyph
|
name: term_glyph
|
||||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.2"
|
version: "1.2.2"
|
||||||
test_api:
|
test_api:
|
||||||
@@ -705,7 +713,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
|
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.11"
|
version: "0.7.11"
|
||||||
typed_data:
|
typed_data:
|
||||||
@@ -713,7 +721,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: typed_data
|
name: typed_data
|
||||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
uuid:
|
uuid:
|
||||||
@@ -721,7 +729,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd"
|
sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.6.0"
|
version: "4.6.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
@@ -729,57 +737,57 @@ packages:
|
|||||||
description:
|
description:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
vm_service:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
|
sha256: "5f37239c4851efcef929cea7824e76df7f2f0970aef85d66bbc430afa40e72f0"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "15.2.0"
|
version: "15.3.0"
|
||||||
web:
|
web:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: ba6f4bba816c8d7e3c1580e170f3786d216951cc6b94babc3b814c08d2cb2738
|
sha256: a0b93865d5644f11cf6a8c3f6db909f1ec168958b5805f6cc684adea957cd63d
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.0"
|
version: "6.4.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xml
|
name: xml
|
||||||
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
|
sha256: "67f0aff7be013d107995e9b75bf4e7f2c3ef2dfdb2c8e68024bba0a7fd5756a4"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.6.1"
|
version: "7.0.1"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: yaml
|
name: yaml
|
||||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
sha256: f67cdd8e07d3c6329146aaef1ba043542b3134c12489f553ca9a7435d1068aea
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.4"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.12.2 <4.0.0"
|
dart: ">=3.12.2 <4.0.0"
|
||||||
flutter: ">=3.44.0"
|
flutter: ">=3.44.0"
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
// 功能描述:验证工作人员端登录页字段、手机号校验和接口错误中文化。
|
||||||
|
// 版本:1.1.0
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:service_app/app/dependencies.dart';
|
import 'package:service_app/app/dependencies.dart';
|
||||||
|
import 'package:service_app/data/services/api_client.dart';
|
||||||
import 'package:service_app/data/services/secure_session_store.dart';
|
import 'package:service_app/data/services/secure_session_store.dart';
|
||||||
import 'package:service_app/ui/features/auth/login_page.dart';
|
import 'package:service_app/ui/features/auth/login_page.dart';
|
||||||
|
|
||||||
|
/// 验证工作人员端登录界面的关键交互。
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('staff login explains the single shared entry', (tester) async {
|
testWidgets('staff login explains the single shared entry', (tester) async {
|
||||||
final session = StaffSession(SecureSessionStore());
|
final session = StaffSession(SecureSessionStore());
|
||||||
@@ -13,4 +17,23 @@ void main() {
|
|||||||
expect(find.text('配送、安装维修与安检共用入口'), findsOneWidget);
|
expect(find.text('配送、安装维修与安检共用入口'), findsOneWidget);
|
||||||
expect(find.byType(TextField), findsNWidgets(2));
|
expect(find.byType(TextField), findsNWidgets(2));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('手机号格式错误显示输入框中文提示', (tester) async {
|
||||||
|
final session = StaffSession(SecureSessionStore());
|
||||||
|
await tester.pumpWidget(MaterialApp(home: LoginPage(session: session)));
|
||||||
|
|
||||||
|
await tester.enterText(find.byType(TextField).first, '+8613800138000');
|
||||||
|
await tester.tap(find.text('进入工作台'));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('请输入正确的11位手机号'), findsOneWidget);
|
||||||
|
expect(find.text('Invalid Argument'), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('接口错误优先按错误码中文化并屏蔽未知英文', () {
|
||||||
|
expect(localizeApiErrorMessage(1704, 'Invalid Argument'), '请求参数错误');
|
||||||
|
expect(localizeApiErrorMessage(1108, 'Password Incorrect'), '密码错误');
|
||||||
|
expect(localizeApiErrorMessage(9999, '业务处理失败'), '业务处理失败');
|
||||||
|
expect(localizeApiErrorMessage(9999, 'Unexpected Error'), '操作失败,请稍后重试');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,78 @@
|
|||||||
|
// 功能描述:封装用户端 HTTP 请求,并将服务端错误转换为安全、可读的中文提示。
|
||||||
|
// 版本:1.1.0
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
const Map<int, String> _apiErrorMessages = {
|
||||||
|
404: '记录不存在',
|
||||||
|
1001: '请求标识缺失',
|
||||||
|
1002: '登录信息缺失,请重新登录',
|
||||||
|
1003: '接口密钥缺失',
|
||||||
|
1004: '请求头参数缺失',
|
||||||
|
1101: '暂无数据',
|
||||||
|
1102: '请求解析失败',
|
||||||
|
1103: '请填写必填参数',
|
||||||
|
1104: '无权执行此操作',
|
||||||
|
1105: '服务端数据处理失败',
|
||||||
|
1106: '服务端数据处理失败',
|
||||||
|
1107: '服务内部错误,请稍后重试',
|
||||||
|
1108: '密码错误',
|
||||||
|
1110: '账号已禁用',
|
||||||
|
1111: '当前记录已停用',
|
||||||
|
1112: '记录不存在',
|
||||||
|
1301: '登录状态已失效,请重新登录',
|
||||||
|
1302: '登录状态已失效,请重新登录',
|
||||||
|
1303: '登录状态已失效,请重新登录',
|
||||||
|
1304: '登录状态已失效,请重新登录',
|
||||||
|
1305: '登录状态已失效,请重新登录',
|
||||||
|
1306: '登录状态已失效,请重新登录',
|
||||||
|
1307: '登录状态已变更,请重新登录',
|
||||||
|
1308: '登录已过期,请重新登录',
|
||||||
|
1309: '登录状态已失效,请重新登录',
|
||||||
|
1310: '登录状态已失效,请重新登录',
|
||||||
|
1311: '登录状态已失效,请重新登录',
|
||||||
|
1312: '登录状态已失效,请重新登录',
|
||||||
|
1313: '登录失败,请稍后重试',
|
||||||
|
1314: '登录状态已失效,请重新登录',
|
||||||
|
1501: '服务暂时不可用,请稍后重试',
|
||||||
|
1502: '服务暂时不可用,请稍后重试',
|
||||||
|
1503: '服务暂时不可用,请稍后重试',
|
||||||
|
1504: '服务暂时不可用,请稍后重试',
|
||||||
|
1505: '服务暂时不可用,请稍后重试',
|
||||||
|
1506: '服务暂时不可用,请稍后重试',
|
||||||
|
1507: '服务暂时不可用,请稍后重试',
|
||||||
|
1702: '操作已取消',
|
||||||
|
1703: '操作失败,请稍后重试',
|
||||||
|
1704: '请求参数错误',
|
||||||
|
1705: '请求超时,请稍后重试',
|
||||||
|
1706: '记录已存在',
|
||||||
|
1707: '无权执行此操作',
|
||||||
|
1708: '系统繁忙,请稍后重试',
|
||||||
|
1709: '当前状态不支持此操作',
|
||||||
|
1710: '操作已中止',
|
||||||
|
1711: '输入内容超出允许范围',
|
||||||
|
1712: '当前功能暂不支持',
|
||||||
|
1713: '服务暂时不可用,请稍后重试',
|
||||||
|
1714: '服务数据异常,请稍后重试',
|
||||||
|
1715: '请先登录',
|
||||||
|
};
|
||||||
|
|
||||||
|
final RegExp _chineseCharacterPattern = RegExp(r'[\u3400-\u9fff]');
|
||||||
|
|
||||||
|
/// 将服务端错误转换为中文用户提示。
|
||||||
|
///
|
||||||
|
/// [code] 是稳定错误码,[message] 是服务端原始消息;返回值不会透传未知英文技术信息。
|
||||||
|
String localizeApiErrorMessage(int code, String? message) {
|
||||||
|
final mappedMessage = _apiErrorMessages[code];
|
||||||
|
if (mappedMessage != null) return mappedMessage;
|
||||||
|
|
||||||
|
final originalMessage = message?.trim() ?? '';
|
||||||
|
if (_chineseCharacterPattern.hasMatch(originalMessage)) return originalMessage;
|
||||||
|
return '操作失败,请稍后重试';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 表示客户端可识别并可安全展示的接口异常。
|
||||||
class ApiException implements Exception {
|
class ApiException implements Exception {
|
||||||
const ApiException(this.code, this.message);
|
const ApiException(this.code, this.message);
|
||||||
|
|
||||||
@@ -12,6 +83,7 @@ class ApiException implements Exception {
|
|||||||
String toString() => message;
|
String toString() => message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 负责用户端统一 HTTP 请求、鉴权头和响应解析。
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
ApiClient(
|
ApiClient(
|
||||||
this._tokenProvider, {
|
this._tokenProvider, {
|
||||||
@@ -59,18 +131,39 @@ class ApiClient {
|
|||||||
request.headers['content-type'] = 'application/json; charset=UTF-8';
|
request.headers['content-type'] = 'application/json; charset=UTF-8';
|
||||||
request.body = jsonEncode(body);
|
request.body = jsonEncode(body);
|
||||||
}
|
}
|
||||||
final streamed = await _client.send(request);
|
final response = await _sendRequest(request);
|
||||||
final response = await http.Response.fromStream(streamed);
|
return _decode(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送请求并统一处理网络连接异常。
|
||||||
|
Future<http.Response> _sendRequest(http.BaseRequest request) async {
|
||||||
|
try {
|
||||||
|
return await http.Response.fromStream(await _client.send(request));
|
||||||
|
} catch (_) {
|
||||||
|
throw const ApiException(500, '网络连接失败,请稍后重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解析统一响应结构,并按错误码生成中文提示。
|
||||||
|
Object? _decode(http.Response response) {
|
||||||
if (response.statusCode < 200 || response.statusCode >= 300) {
|
if (response.statusCode < 200 || response.statusCode >= 300) {
|
||||||
throw ApiException(response.statusCode, '网络请求失败(${response.statusCode})');
|
throw ApiException(response.statusCode, '网络请求失败(${response.statusCode})');
|
||||||
}
|
}
|
||||||
final decoded = jsonDecode(response.body);
|
Object? decoded;
|
||||||
|
try {
|
||||||
|
decoded = jsonDecode(response.body);
|
||||||
|
} on FormatException {
|
||||||
|
throw const ApiException(500, '服务端响应格式错误');
|
||||||
|
}
|
||||||
if (decoded is! Map<String, Object?>) {
|
if (decoded is! Map<String, Object?>) {
|
||||||
throw const ApiException(500, '服务端响应格式错误');
|
throw const ApiException(500, '服务端响应格式错误');
|
||||||
}
|
}
|
||||||
final code = (decoded['code'] as num?)?.toInt() ?? 500;
|
final code = (decoded['code'] as num?)?.toInt() ?? 500;
|
||||||
if (code != 0) {
|
if (code != 0) {
|
||||||
throw ApiException(code, decoded['message'] as String? ?? '操作失败');
|
throw ApiException(
|
||||||
|
code,
|
||||||
|
localizeApiErrorMessage(code, decoded['message'] as String?),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return decoded['details'];
|
return decoded['details'];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
|
// 功能描述:提供用户端手机号密码登录界面及本地输入校验。
|
||||||
|
// 版本:1.1.0
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import '../../../app/dependencies.dart';
|
import '../../../app/dependencies.dart';
|
||||||
|
import '../../../data/services/api_client.dart';
|
||||||
|
|
||||||
|
/// 用户端手机号密码登录页面。
|
||||||
class LoginPage extends StatefulWidget {
|
class LoginPage extends StatefulWidget {
|
||||||
const LoginPage({required this.session, super.key});
|
const LoginPage({required this.session, super.key});
|
||||||
|
|
||||||
@@ -12,11 +16,15 @@ class LoginPage extends StatefulWidget {
|
|||||||
State<LoginPage> createState() => _LoginPageState();
|
State<LoginPage> createState() => _LoginPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 管理登录输入、提交状态和错误提示。
|
||||||
class _LoginPageState extends State<LoginPage> {
|
class _LoginPageState extends State<LoginPage> {
|
||||||
|
static final RegExp _phonePattern = RegExp(r'^1[3-9]\d{9}$');
|
||||||
|
|
||||||
final _phone = TextEditingController();
|
final _phone = TextEditingController();
|
||||||
final _password = TextEditingController();
|
final _password = TextEditingController();
|
||||||
bool _submitting = false;
|
bool _submitting = false;
|
||||||
String? _error;
|
String? _error;
|
||||||
|
String? _phoneError;
|
||||||
bool _obscurePassword = true;
|
bool _obscurePassword = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -26,15 +34,30 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 校验手机号并提交登录请求。
|
||||||
Future<void> _login() async {
|
Future<void> _login() async {
|
||||||
|
final phone = _phone.text.trim();
|
||||||
|
if (!_phonePattern.hasMatch(phone)) {
|
||||||
|
setState(() {
|
||||||
|
_phoneError = '请输入正确的11位手机号';
|
||||||
|
_error = null;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_submitting = true;
|
_submitting = true;
|
||||||
|
_phoneError = null;
|
||||||
_error = null;
|
_error = null;
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await widget.session.login(phone: _phone.text.trim(), password: _password.text);
|
await widget.session.login(phone: phone, password: _password.text);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (mounted) setState(() => _error = error.toString());
|
// 登录接口使用统一密码错误码,页面不区分账号是否存在,避免泄露账号状态。
|
||||||
|
final message = error is ApiException
|
||||||
|
? (error.code == 1108 ? '手机号或密码错误' : error.toString())
|
||||||
|
: '登录失败,请稍后重试';
|
||||||
|
if (mounted) setState(() => _error = message);
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _submitting = false);
|
if (mounted) setState(() => _submitting = false);
|
||||||
}
|
}
|
||||||
@@ -86,9 +109,13 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
controller: _phone,
|
controller: _phone,
|
||||||
keyboardType: TextInputType.phone,
|
keyboardType: TextInputType.phone,
|
||||||
autofillHints: const [AutofillHints.telephoneNumber],
|
autofillHints: const [AutofillHints.telephoneNumber],
|
||||||
decoration: const InputDecoration(
|
onChanged: (_) {
|
||||||
|
if (_phoneError != null) setState(() => _phoneError = null);
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
labelText: '手机号',
|
labelText: '手机号',
|
||||||
prefixIcon: Icon(Icons.phone_outlined),
|
prefixIcon: const Icon(Icons.phone_outlined),
|
||||||
|
errorText: _phoneError,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 14),
|
const SizedBox(height: 14),
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
// 功能描述:验证用户端登录页字段、手机号校验和接口错误中文化。
|
||||||
|
// 版本:1.1.0
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:user_app/app/dependencies.dart';
|
import 'package:user_app/app/dependencies.dart';
|
||||||
|
import 'package:user_app/data/services/api_client.dart';
|
||||||
import 'package:user_app/data/services/secure_session_store.dart';
|
import 'package:user_app/data/services/secure_session_store.dart';
|
||||||
import 'package:user_app/ui/features/auth/login_page.dart';
|
import 'package:user_app/ui/features/auth/login_page.dart';
|
||||||
|
|
||||||
|
/// 验证用户端登录界面的关键交互。
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('login page exposes phone and password fields', (tester) async {
|
testWidgets('login page exposes phone and password fields', (tester) async {
|
||||||
final session = UserSession(SecureSessionStore());
|
final session = UserSession(SecureSessionStore());
|
||||||
@@ -13,4 +17,23 @@ void main() {
|
|||||||
expect(find.byType(TextField), findsNWidgets(2));
|
expect(find.byType(TextField), findsNWidgets(2));
|
||||||
expect(find.text('安全登录'), findsOneWidget);
|
expect(find.text('安全登录'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('手机号格式错误显示输入框中文提示', (tester) async {
|
||||||
|
final session = UserSession(SecureSessionStore());
|
||||||
|
await tester.pumpWidget(MaterialApp(home: LoginPage(session: session)));
|
||||||
|
|
||||||
|
await tester.enterText(find.byType(TextField).first, '+8613800138000');
|
||||||
|
await tester.tap(find.text('安全登录'));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('请输入正确的11位手机号'), findsOneWidget);
|
||||||
|
expect(find.text('Invalid Argument'), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('接口错误优先按错误码中文化并屏蔽未知英文', () {
|
||||||
|
expect(localizeApiErrorMessage(1704, 'Invalid Argument'), '请求参数错误');
|
||||||
|
expect(localizeApiErrorMessage(1108, 'Password Incorrect'), '密码错误');
|
||||||
|
expect(localizeApiErrorMessage(9999, '业务处理失败'), '业务处理失败');
|
||||||
|
expect(localizeApiErrorMessage(9999, 'Unexpected Error'), '操作失败,请稍后重试');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ apps/user_app/lib/
|
|||||||
- 设备命令 UI 至少表示待发送、已发送、设备已确认、执行失败、超时待确认、已撤销。创建命令成功后按命令 `identity` 查询回执;超时只能显示“待确认”,不能回退为“已成功”。
|
- 设备命令 UI 至少表示待发送、已发送、设备已确认、执行失败、超时待确认、已撤销。创建命令成功后按命令 `identity` 查询回执;超时只能显示“待确认”,不能回退为“已成功”。
|
||||||
- 写请求生成并持久化幂等键;重试复用原键。余额支付、充值、提现、订单提交、报修和设备命令均不得因页面重建或网络重连重复创建事实。
|
- 写请求生成并持久化幂等键;重试复用原键。余额支付、充值、提现、订单提交、报修和设备命令均不得因页面重建或网络重连重复创建事实。
|
||||||
- ViewModel 根据稳定错误码映射可操作文案与恢复入口;不得解析后端错误文案驱动流程。401/403、状态冲突、限流、外部依赖不可用和未知错误分别处理。
|
- ViewModel 根据稳定错误码映射可操作文案与恢复入口;不得解析后端错误文案驱动流程。401/403、状态冲突、限流、外部依赖不可用和未知错误分别处理。
|
||||||
|
- 所有面向用户的接口错误必须显示中文;已知错误按稳定错误码映射,未知英文技术信息统一收敛为安全的中文失败提示。登录页在请求前校验纯 11 位中国大陆手机号,并在输入框下显示格式错误原因。
|
||||||
- 金额以最小货币单位整数进入领域层,只在格式化组件中转换为展示文本;时间统一解析为 UTC 并按用户时区展示,同时保留数据更新时间。
|
- 金额以最小货币单位整数进入领域层,只在格式化组件中转换为展示文本;时间统一解析为 UTC 并按用户时区展示,同时保留数据更新时间。
|
||||||
|
|
||||||
### 7.5 本地数据、安全与平台能力
|
### 7.5 本地数据、安全与平台能力
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ apps/service_app/lib/
|
|||||||
- 不合格、高风险、测试失败、围栏异常、金额差异、证据缺失和设备回执不确定时只能进入异常、待整改或待确认,不能由客户端跳到已完成。
|
- 不合格、高风险、测试失败、围栏异常、金额差异、证据缺失和设备回执不确定时只能进入异常、待整改或待确认,不能由客户端跳到已完成。
|
||||||
- 关阀、设备激活、任务完结、收款和提现均显示服务端确认状态。请求成功、文件进入上传队列或本地步骤完成不得显示为最终成功。
|
- 关阀、设备激活、任务完结、收款和提现均显示服务端确认状态。请求成功、文件进入上传队列或本地步骤完成不得显示为最终成功。
|
||||||
- 检查项使用稳定代码;照片类型、材料、风险等级和错误原因由契约映射,禁止根据中文标题或错误文案驱动状态流转。
|
- 检查项使用稳定代码;照片类型、材料、风险等级和错误原因由契约映射,禁止根据中文标题或错误文案驱动状态流转。
|
||||||
|
- 所有面向工作人员的接口错误必须显示中文;已知错误按稳定错误码映射,未知英文技术信息统一收敛为安全的中文失败提示。登录页在请求前校验纯 11 位中国大陆手机号,并在输入框下显示格式错误原因。
|
||||||
|
|
||||||
### 9.5 离线队列、定位与证据
|
### 9.5 离线队列、定位与证据
|
||||||
|
|
||||||
|
|||||||
72
docs/操作日志_移动端错误提示中文化_20260902.md
Normal file
72
docs/操作日志_移动端错误提示中文化_20260902.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# 移动端错误提示中文化操作日志
|
||||||
|
|
||||||
|
操作时间:2026-09-02 20:56:00
|
||||||
|
操作类型:修改、扩展
|
||||||
|
影响模块:用户端 App、工作人员端 App、移动端需求文档
|
||||||
|
|
||||||
|
## 操作前状态
|
||||||
|
|
||||||
|
用户端和工作人员端的 HTTP 客户端会直接展示服务端 `message`。当接口返回 `Invalid Argument`、`Password Incorrect` 等 SDK 英文错误时,页面直接向用户暴露英文技术信息。登录页未在本地校验后端要求的纯 11 位中国大陆手机号,输入带 `+86` 的号码后只能得到服务端通用参数错误。
|
||||||
|
|
||||||
|
工作区原有 `apps/service_app/pubspec.lock` 修改,本次未覆盖或调整。
|
||||||
|
|
||||||
|
## 具体操作
|
||||||
|
|
||||||
|
1. 在两个 App 的 `ApiClient` 中增加稳定错误码到中文提示的统一映射,覆盖当前 BSM SDK 的请求、业务、令牌、基础设施和标准状态错误码。
|
||||||
|
2. 保留服务端返回的中文业务提示;无法识别的纯英文消息统一显示“操作失败,请稍后重试”。
|
||||||
|
3. 将网络连接异常和无效 JSON 响应转换为明确中文提示。
|
||||||
|
4. 在两个登录页提交前校验纯 11 位中国大陆手机号,格式错误时在手机号输入框下显示“请输入正确的11位手机号”。按已确认方案,不增加 `+86` 自动转换或兼容。
|
||||||
|
5. 登录接口返回密码错误码 `1108` 时统一提示“手机号或密码错误”,避免泄露账号是否存在。
|
||||||
|
6. 补充 Widget 与错误映射回归测试,并同步两类 App 的错误处理需求。
|
||||||
|
|
||||||
|
## 操作后状态
|
||||||
|
|
||||||
|
- `Invalid Argument` 不再直接显示;登录页会在请求前给出手机号中文格式提示。
|
||||||
|
- 两个 App 的已知接口错误按稳定错误码展示中文。
|
||||||
|
- 未知英文接口消息不再透传到界面。
|
||||||
|
- 后端路由、请求体、响应结构和错误码保持不变,公共接口兼容。
|
||||||
|
|
||||||
|
## 代码变更
|
||||||
|
|
||||||
|
- `apps/user_app/lib/data/services/api_client.dart`
|
||||||
|
- `localizeApiErrorMessage`:新增错误码中文映射和未知英文兜底。
|
||||||
|
- `ApiClient._sendRequest`、`ApiClient._decode`:统一处理网络异常、响应解析和中文错误。
|
||||||
|
- `apps/user_app/lib/ui/features/auth/login_page.dart`
|
||||||
|
- `_LoginPageState._login`:新增手机号校验和登录密码错误专用提示。
|
||||||
|
- 手机号输入框:新增字段级错误展示与输入后清除行为。
|
||||||
|
- `apps/service_app/lib/data/services/api_client.dart`
|
||||||
|
- 与用户端保持一致的错误码中文映射和请求异常处理。
|
||||||
|
- `apps/service_app/lib/ui/features/auth/login_page.dart`
|
||||||
|
- 与用户端保持一致的手机号校验和登录错误提示。
|
||||||
|
- `apps/user_app/test/ui/login_page_test.dart`
|
||||||
|
- 新增手机号格式错误和接口错误中文化测试。
|
||||||
|
- `apps/service_app/test/ui/login_page_test.dart`
|
||||||
|
- 新增手机号格式错误和接口错误中文化测试。
|
||||||
|
- `docs/03-用户端App需求.md`、`docs/04-服务端App需求.md`
|
||||||
|
- 补充移动端接口错误中文化和登录手机号校验要求。
|
||||||
|
|
||||||
|
## 行为变化
|
||||||
|
|
||||||
|
| 场景 | 修改前 | 修改后 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 登录手机号带 `+86` | 服务端返回并展示 `Invalid Argument` | 输入框提示“请输入正确的11位手机号”,不发起请求 |
|
||||||
|
| 密码错误 | 展示 `Password Incorrect` | 展示“手机号或密码错误” |
|
||||||
|
| 已知 SDK 接口错误 | 可能展示英文原文 | 按稳定错误码展示中文 |
|
||||||
|
| 未知纯英文错误 | 直接展示英文原文 | 展示“操作失败,请稍后重试” |
|
||||||
|
| 中文业务错误 | 直接展示中文原文 | 保持中文原文 |
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- `apps/user_app` 执行 `flutter analyze`:通过,无问题。
|
||||||
|
- `apps/user_app` 执行 `flutter test`:4 个测试全部通过。
|
||||||
|
- `apps/service_app` 执行 `flutter analyze`:通过,无问题。
|
||||||
|
- `apps/service_app` 执行 `flutter test`:5 个测试全部通过。
|
||||||
|
- 执行 `git diff --check`:未发现空白符错误;仅提示仓库现有的 LF/CRLF 转换规则。
|
||||||
|
- 边界测试覆盖:带 `+86` 的手机号、参数错误码、密码错误码、未知中文业务消息和未知英文消息。
|
||||||
|
|
||||||
|
## 风险评估
|
||||||
|
|
||||||
|
- 当前手机号规则与后端保持一致,仅接受纯 11 位号码;系统自动填充带 `+86` 时仍需用户手动修改,这是已确认的产品选择。
|
||||||
|
- 未知英文消息会被安全兜底文案替代,客户端界面不再包含原始诊断信息;排障应依赖稳定错误码和服务端日志。
|
||||||
|
- 两个 App 目前各自维护同一份映射;后端 SDK 新增错误码时需要同步补充两端映射及测试。
|
||||||
|
- 本次不修改认证逻辑、令牌、服务端错误码或数据库,影响仅限客户端输入校验和错误展示。
|
||||||
Reference in New Issue
Block a user