Files
ttstd_family_care/lib/features/account/data/account_api.dart
2026-08-21 17:12:17 +08:00

114 lines
3.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:cupertino_ui/cupertino_ui.dart';
import 'package:ttstd_family_care/core/network/api_exception.dart';
import 'package:ttstd_family_care/core/network/dio_client.dart';
import 'package:ttstd_family_care/features/account/domain/account_models.dart';
/// 账号与安全接口访问层。
///
/// 后端接口前缀为 `/api/v1/open/account`(与设备接口所在的 `/api/v1/client` 不同)。
/// [DioClient] 已统一解析后台返回体的 `data` 字段,业务失败时抛出 [ApiException]
/// 本层将查询类接口的异常降级为空数据,写操作则把错误提示回传给控制器。
class AccountApi {
const AccountApi();
/// 登录设备(会话)列表。
Future<List<LoginDevice>> getLoginDevices() async {
try {
final data = await DioClient.get('account/login-devices');
return _parseList(data, LoginDevice.fromJson);
} on ApiException catch (e) {
debugPrint('[AccountApi] getLoginDevices failed: ${e.message}');
return const [];
}
}
/// 将指定登录设备下线。
Future<String?> revokeLoginDevice(String id) =>
_write(() => DioClient.post(
'account/login-devices/revoke',
queryParameters: {'id': id},
));
/// 修改密码。
Future<String?> changePassword({
required String oldPassword,
required String newPassword,
}) =>
_write(() => DioClient.post(
'account/password',
data: {'oldPassword': oldPassword, 'newPassword': newPassword},
));
/// 发送更换手机号验证码到新手机号。
Future<String?> sendChangeMobileCode(String newMobile) =>
_write(() => DioClient.post(
'account/mobile/sms/code',
queryParameters: {'mobile': newMobile},
));
/// 更换绑定手机号。
Future<String?> changeMobile({
required String newMobile,
required String code,
required String password,
}) =>
_write(() => DioClient.post(
'account/mobile',
data: {
'mobile': newMobile,
'code': code,
'password': password,
},
));
/// 查询微信绑定状态。
Future<WechatBinding> getWechatBinding() async {
try {
final data = await DioClient.get('account/wechat');
if (data is Map<String, dynamic>) return WechatBinding.fromJson(data);
return WechatBinding.unbound;
} on ApiException catch (e) {
debugPrint('[AccountApi] getWechatBinding failed: ${e.message}');
return WechatBinding.unbound;
}
}
/// 绑定微信。
Future<String?> bindWechat(String authCode) => _write(() => DioClient.post(
'account/wechat/bind',
data: {'code': authCode},
));
/// 解绑微信。
Future<String?> unbindWechat() =>
_write(() => DioClient.post('account/wechat/unbind'));
/// 统一处理写操作:成功返回 null业务失败返回后台提示文案。
Future<String?> _write(Future<dynamic> Function() call) async {
try {
await call();
return null;
} on ApiException catch (e) {
return e.message;
}
}
/// 兼容后台可能返回「数组」或「分页对象(含 list/records」两种结构。
List<T> _parseList<T>(
dynamic data,
T Function(Map<String, dynamic>) fromJson,
) {
Iterable<dynamic>? raw;
if (data is List) {
raw = data;
} else if (data is Map<String, dynamic>) {
raw = (data['list'] ?? data['records']) as Iterable<dynamic>?;
}
if (raw == null) return const [];
return raw
.whereType<Map<String, dynamic>>()
.map(fromJson)
.toList(growable: false);
}
}