优化:根据新的API优化框架代码

This commit is contained in:
ivan 2026-04-02 18:40:31 +08:00
parent e77f5f45b7
commit bdab80b2bd
16 changed files with 730 additions and 90 deletions

View File

@ -77,7 +77,7 @@ void main() {
``` ```
调用层 (原始字段) 调用层 (原始字段)
↓ ↓
ProxyClient.request() ProxyClient.request()(自动加 `pkg`;默认加 `User_token`)
↓ ↓
字段映射 (原始 → V2) 字段映射 (原始 → V2)
↓ ↓
@ -86,6 +86,10 @@ AES 加密
发送请求 发送请求
``` ```
**请求头**:`ProxyClient` 会将 [AppConfig.packageName] 写入映射后的包名字段(原始名 `pkg`);若已设置用户 token,默认还会写入 `User_token`。**`UserApi.fast_login` 等无需登录态接口**内部使用 `includeUserTokenInHeader: false`,避免把旧 token 打进 `filter_type`。其余请求也可在直接调用 `ProxyClient.request` 时传入该参数。
**请求体**:各 `*Api` 方法使用与《客户端指南》解密表一致的**原始字段名**(如 `referer`、`deviceId`、`fileUrls` / `contentType` / `content`)。
### 3.2 响应流程 ### 3.2 响应流程
``` ```
@ -131,9 +135,10 @@ AES 解密
final res = await UserApi.fastLogin( final res = await UserApi.fastLogin(
deviceId: '设备ID', deviceId: '设备ID',
sign: 'MD5(deviceId)大写', sign: 'MD5(deviceId)大写',
app: 'HAndroid', // 必填:HIOS / HAndroid
referer: '归因来源', // 可选 referer: '归因来源', // 可选
ch: '渠道号', // 可选 ch: '渠道号', // 可选
type: '类型', // 可选 type: 'fb', // 可选;未传时默认 fb
); );
if (res.isSuccess) { if (res.isSuccess) {
@ -157,9 +162,17 @@ if (res.isSuccess) {
#### 5.1.2 getCommonInfo - 获取用户通用信息 #### 5.1.2 getCommonInfo - 获取用户通用信息
```dart ```dart
final config = ApiClient.instance.config;
final backendApp = defaultTargetPlatform == TargetPlatform.iOS
? config.backendAppTypeIOS
: config.backendAppTypeAndroid;
final res = await UserApi.getCommonInfo( final res = await UserApi.getCommonInfo(
app: '应用ID', app: backendApp, // 必填:HIOS / HAndroid 等与后端约定
userId: '用户ID', // 可选 pkg: config.packageName, // 必填:包名
userId: '用户ID', // 可选
deviceId: '设备ID', // 可选
// client / ch / inviteBy / clientId 可选
); );
if (res.isSuccess) { if (res.isSuccess) {
@ -173,7 +186,7 @@ if (res.isSuccess) {
```dart ```dart
final res = await UserApi.getAccount( final res = await UserApi.getAccount(
app: '应用ID', app: 'HAndroid',
userId: '用户ID', // 可选 userId: '用户ID', // 可选
); );
@ -191,6 +204,46 @@ if (res.isSuccess) {
- `isVip`: 是否 VIP - `isVip`: 是否 VIP
- `freeTimes`: 免费次数 - `freeTimes`: 免费次数
#### 5.1.4 getAppLanguage - App 语言配置
`GET /v1/config/app-language`(需登录)
```dart
final res = await UserApi.getAppLanguage(
pkg: ApiClient.instance.config.packageName,
lang: 'en', // 可选
);
```
**返回实体**: `AppLanguageConfigResponse`(`config` 为语言配置 JSON 字符串)
#### 5.1.5 getCreditsPage - 积分分页流水
与 `ImageApi.getCreditsPageInfo` 相同,路径 `GET /v1/user/credits-page`。
```dart
final res = await UserApi.getCreditsPage(page: '1', size: '10', type: '1');
```
#### 5.1.6 getUserPayments - 用户支付记录
```dart
final res = await UserApi.getUserPayments(
app: backendApp,
userId: '用户ID',
);
```
**返回实体**: `UserPaymentsListResponse`(`items`: `UserPaymentRecord` 列表)
#### 5.1.7 getUnreadMessageCount - 未读消息数
```dart
final res = await UserApi.getUnreadMessageCount(types: '类型列表'); // types 可选
```
**返回实体**: `UnreadMessageCountResponse`
--- ---
### 5.2 PaymentApi - 支付相关 ### 5.2 PaymentApi - 支付相关
@ -199,7 +252,8 @@ if (res.isSuccess) {
```dart ```dart
final res = await PaymentApi.getGooglePayActivities( final res = await PaymentApi.getGooglePayActivities(
app: '应用ID', // 可选 app: 'HAndroid', // 可选;默认 `AppConfig.backendAppTypeAndroid`
client: '客户端', // 可选
country: '国家', // 可选 country: '国家', // 可选
); );
@ -226,7 +280,7 @@ if (res.isSuccess) {
```dart ```dart
final res = await PaymentApi.getPaymentMethods( final res = await PaymentApi.getPaymentMethods(
activityId: '活动ID', activityId: 12345, // int,与接口一致
country: '国家', // 可选 country: '国家', // 可选
); );
@ -252,7 +306,7 @@ if (res.isSuccess) {
```dart ```dart
final res = await PaymentApi.createPayment( final res = await PaymentApi.createPayment(
app: '应用ID', app: 'HAndroid', // HIOS / HAndroid
userId: '用户ID', userId: '用户ID',
activityId: '活动ID', activityId: '活动ID',
paymentMethod: '支付方式', paymentMethod: '支付方式',
@ -271,7 +325,24 @@ if (res.isSuccess) {
- `payUrl`: 支付链接 - `payUrl`: 支付链接
- `status`: 状态 - `status`: 状态
#### 5.2.4 getOrderDetail - 获取订单详情 #### 5.2.4 getPaymentDetailList - 支付订单列表
`GET /v1/payment/getPaymentDetailList`
```dart
final res = await PaymentApi.getPaymentDetailList(
app: 'HAndroid',
userId: '用户ID',
paymentMethod: 'GOOGLEPAY', // 可选
filterStatus: 'SUCCESS', // 可选
);
```
**返回实体**: `PaymentOrderListResponse`(`orders`: `PaymentOrderSummary` 列表)
#### 5.2.5 getOrderDetail - 获取订单详情
Query 使用原始字段 `id`(值为订单/支付 ID)。
```dart ```dart
final res = await PaymentApi.getOrderDetail( final res = await PaymentApi.getOrderDetail(
@ -290,7 +361,7 @@ if (res.isSuccess) {
- `status`: 状态 - `status`: 状态
- `amount`: 金额 - `amount`: 金额
#### 5.2.5 googlepay - Google Pay 回调 #### 5.2.6 googlepay - Google Pay 回调
```dart ```dart
final res = await PaymentApi.googlepay( final res = await PaymentApi.googlepay(
@ -410,26 +481,29 @@ if (res.isSuccess) {
#### 5.3.6 getCreditsPageInfo - 获取积分页面信息 #### 5.3.6 getCreditsPageInfo - 获取积分页面信息
对应 `GET /v1/user/credits-page`(与 `UserApi.getCreditsPage` 相同)。
```dart ```dart
final res = await ImageApi.getCreditsPageInfo( final res = await ImageApi.getCreditsPageInfo(
app: '应用ID', page: '1',
userId: '用户ID', // 可选 size: '10',
ch: '渠道', // 可选 type: '1',
); );
// 或:UserApi.getCreditsPage(page: '1', size: '10', type: '1');
if (res.isSuccess) { if (res.isSuccess) {
final info = res.data!; final info = res.data!;
final credits = info.credits; final total = info.total;
final freeTimes = info.freeTimes; final records = info.records;
final isVip = info.isVip; final credits = info.credits; // 若后端仍返回单屏字段则可用
} }
``` ```
**返回实体**: `CreditsPageInfoResponse` **返回实体**: `CreditsPageInfoResponse`
- `credits`: 积分 - `total` / `current` / `pages` / `size` / `records`: 分页与流水(新接口)
- `freeTimes`: 免费次数 - `credits` / `freeTimes` / `isVip` / `vipExpireTime`: 兼容旧字段
- `isVip`: 是否 VIP - `records` 项见 `CreditRecordItem`
- `vipExpireTime`: VIP 过期时间
--- ---
@ -587,6 +661,7 @@ class AuthService {
final res = await UserApi.fastLogin( final res = await UserApi.fastLogin(
deviceId: deviceId, deviceId: deviceId,
sign: sign, sign: sign,
app: 'HAndroid', // 或 AppConfig.backendAppTypeAndroid / IOS
referer: 'organic', referer: 'organic',
); );
@ -613,7 +688,7 @@ class PaymentService {
} }
// 2. 获取支付方式 // 2. 获取支付方式
static Future<List<PaymentMethodItem>> getPaymentMethods(String activityId) async { static Future<List<PaymentMethodItem>> getPaymentMethods(int activityId) async {
final res = await PaymentApi.getPaymentMethods(activityId: activityId); final res = await PaymentApi.getPaymentMethods(activityId: activityId);
return res.data?.paymentMethods ?? []; return res.data?.paymentMethods ?? [];
} }
@ -625,7 +700,7 @@ class PaymentService {
required String paymentMethod, required String paymentMethod,
}) async { }) async {
final res = await PaymentApi.createPayment( final res = await PaymentApi.createPayment(
app: ApiClient.instance.config.appId, app: ApiClient.instance.config.backendAppTypeAndroid,
userId: userId, userId: userId,
activityId: activityId, activityId: activityId,
paymentMethod: paymentMethod, paymentMethod: paymentMethod,

View File

@ -47,15 +47,13 @@
### 4.1 接口 ### 4.1 接口
```dart ```dart
// Android // Android(app 默认 backendAppTypeAndroid,可传 client)
final res = await PaymentApi.getGooglePayActivities( final res = await PaymentApi.getGooglePayActivities(
app: '应用ID', // 可选
country: '国家', // 可选 country: '国家', // 可选
); );
// iOS // iOS(app 默认 backendAppTypeIOS)
final res = await PaymentApi.getApplePayActivities( final res = await PaymentApi.getApplePayActivities(
app: '应用ID', // 可选
country: '国家', // 可选 country: '国家', // 可选
); );
``` ```
@ -85,7 +83,7 @@ class PaymentProductItem {
```dart ```dart
final res = await PaymentApi.getPaymentMethods( final res = await PaymentApi.getPaymentMethods(
activityId: '活动ID', activityId: 12345, // int
country: '国家', // 可选 country: '国家', // 可选
); );
@ -104,7 +102,7 @@ if (res.isSuccess) {
```dart ```dart
final res = await PaymentApi.createPayment( final res = await PaymentApi.createPayment(
app: '应用ID', app: 'HAndroid', // HIOS / HAndroid
userId: '用户ID', userId: '用户ID',
activityId: '活动ID', activityId: '活动ID',
paymentMethod: '支付方式', paymentMethod: '支付方式',
@ -132,7 +130,7 @@ if (res.isSuccess) {
```dart ```dart
// 1. 创建订单(直接走谷歌支付) // 1. 创建订单(直接走谷歌支付)
final createRes = await PaymentApi.createPayment( final createRes = await PaymentApi.createPayment(
app: '应用ID', app: 'HAndroid',
userId: '用户ID', userId: '用户ID',
activityId: '活动ID', activityId: '活动ID',
paymentMethod: 'GooglePay', paymentMethod: 'GooglePay',
@ -232,6 +230,7 @@ class GooglePayCallbackResponse {
| `getApplePayActivities` | GET | `PaymentProductsResponse` | | `getApplePayActivities` | GET | `PaymentProductsResponse` |
| `getPaymentMethods` | POST | `PaymentMethodsResponse` | | `getPaymentMethods` | POST | `PaymentMethodsResponse` |
| `createPayment` | POST | `CreatePaymentResponse` | | `createPayment` | POST | `CreatePaymentResponse` |
| `getPaymentDetailList` | GET | `PaymentOrderListResponse` |
| `getOrderDetail` | GET | `OrderDetailResponse` | | `getOrderDetail` | GET | `OrderDetailResponse` |
| `googlepay` | POST | `GooglePayCallbackResponse` | | `googlepay` | POST | `GooglePayCallbackResponse` |
@ -250,7 +249,7 @@ class GooglePayCallbackResponse {
| 活动 ID | activityId | - | | 活动 ID | activityId | - |
| 支付方式 | paymentMethod | - | | 支付方式 | paymentMethod | - |
| 支付子类型 | paymentType | - | | 支付子类型 | paymentType | - |
| 订单 ID | orderId | orderId | | 订单 / 支付 ID(详情 query) | `id`(Dart 仍用参数名 orderId) | orderId |
| 购买签名 | signature | - | | 购买签名 | signature | - |
| 购买数据 | purchaseData | - | | 购买数据 | purchaseData | - |
| 商品 ID | - | productId | | 商品 ID | - | productId |

View File

@ -102,12 +102,20 @@ class ProxyClient {
/// [headers]、[queryParams]、[body] 使用**原始字段名**, /// [headers]、[queryParams]、[body] 使用**原始字段名**,
/// 框架会按 [AppConfig.fieldMapping] 转为 V2 字段名后发送。 /// 框架会按 [AppConfig.fieldMapping] 转为 V2 字段名后发送。
/// 响应 data 会自动从 V2 转回原始字段名。 /// 响应 data 会自动从 V2 转回原始字段名。
///
/// **请求头(自动注入)**
/// - [AppConfig.packageName] → 原始字段名 `pkg`(经映射后的请求头键,如 `stakeholder`)
/// - 若已 [userToken] 且 [includeUserTokenInHeader] 为 true,则注入 `User_token`
///
/// 与文档一致:**设备快速登录等无需登录态接口**应传 `includeUserTokenInHeader: false`,
/// 避免历史 token 进入 `filter_type`。
Future<ApiResponse> request({ Future<ApiResponse> request({
required String path, required String path,
required String method, required String method,
Map<String, String>? headers, Map<String, String>? headers,
Map<String, String>? queryParams, Map<String, String>? queryParams,
Map<String, dynamic>? body, Map<String, dynamic>? body,
bool includeUserTokenInHeader = true,
}) async { }) async {
final pk = config.proxyKeys; final pk = config.proxyKeys;
final mapping = config.fieldMapping; final mapping = config.fieldMapping;
@ -116,7 +124,9 @@ class ProxyClient {
if (config.packageName.isNotEmpty) { if (config.packageName.isNotEmpty) {
headersMap[mapping.headerPackageNameField] = config.packageName; headersMap[mapping.headerPackageNameField] = config.packageName;
} }
if (userToken != null && userToken!.isNotEmpty) { if (includeUserTokenInHeader &&
userToken != null &&
userToken!.isNotEmpty) {
headersMap[mapping.headerUserTokenField] = userToken!; headersMap[mapping.headerUserTokenField] = userToken!;
} }
headersMap = mapping.mapRequest(headersMap); headersMap = mapping.mapRequest(headersMap);
@ -168,6 +178,8 @@ class ProxyClient {
/// ///
/// [headers]、[queryParams]、[body] 使用**原始字段名**。 /// [headers]、[queryParams]、[body] 使用**原始字段名**。
/// [entityFactory] 用于将映射后的 data 转换为实体对象。 /// [entityFactory] 用于将映射后的 data 转换为实体对象。
///
/// 参见 [request] 的 [includeUserTokenInHeader] 说明。
Future<EntityResponse<T>> requestEntity<T extends Entity>({ Future<EntityResponse<T>> requestEntity<T extends Entity>({
required String path, required String path,
required String method, required String method,
@ -175,6 +187,7 @@ class ProxyClient {
Map<String, String>? headers, Map<String, String>? headers,
Map<String, String>? queryParams, Map<String, String>? queryParams,
Map<String, dynamic>? body, Map<String, dynamic>? body,
bool includeUserTokenInHeader = true,
}) async { }) async {
final response = await request( final response = await request(
path: path, path: path,
@ -182,6 +195,7 @@ class ProxyClient {
headers: headers, headers: headers,
queryParams: queryParams, queryParams: queryParams,
body: body, body: body,
includeUserTokenInHeader: includeUserTokenInHeader,
); );
if (response.isSuccess && response.data is Map<String, dynamic>) { if (response.isSuccess && response.data is Map<String, dynamic>) {

View File

@ -28,6 +28,9 @@ const FieldMapping petsHeroAIFieldMapping = FieldMapping({
// === 支付 === // === 支付 ===
'activityId': 'warrior', 'activityId': 'warrior',
'country': 'vambrace', 'country': 'vambrace',
'client': 'filter',
'id': 'timing',
'filterStatus': 'quest',
'orderId': 'federation', 'orderId': 'federation',
'payUrl': 'convert', 'payUrl': 'convert',
'productId': 'helm', 'productId': 'helm',
@ -51,6 +54,8 @@ const FieldMapping petsHeroAIFieldMapping = FieldMapping({
'cursor': 'platoon', 'cursor': 'platoon',
'declaration': 'declaration', 'declaration': 'declaration',
'quest': 'quest', 'quest': 'quest',
'imgCount': 'indicator',
'aspectRatio': 'caption',
'ext': 'nexus', 'ext': 'nexus',
'imgUrl': 'congregation', 'imgUrl': 'congregation',
'poseId': 'profit', 'poseId': 'profit',

View File

@ -368,13 +368,67 @@ class MyTasksResponse extends Entity {
}; };
} }
/// 积分页面信息响应 /// 积分流水单条(`/v1/user/credits-page` 的 records)
class CreditRecordItem extends Entity {
CreditRecordItem({
this.subBusinessType,
this.credits,
this.createTime,
this.businessId,
this.type,
this.businessType,
});
final String? subBusinessType;
final int? credits;
final int? createTime;
final String? businessId;
final int? type;
final String? businessType;
static int? _toInt(dynamic v) {
if (v == null) return null;
if (v is int) return v;
if (v is num) return v.toInt();
if (v is String && v.isNotEmpty) return int.tryParse(v);
return null;
}
@override
factory CreditRecordItem.fromJson(Map<String, dynamic> json) {
return CreditRecordItem(
subBusinessType: json['subBusinessType'] as String?,
credits: _toInt(json['credits']),
createTime: _toInt(json['createTime']),
businessId: json['businessId'] as String?,
type: _toInt(json['type']),
businessType: json['businessType'] as String?,
);
}
@override
Map<String, dynamic> toJson() => {
'subBusinessType': subBusinessType,
'credits': credits,
'createTime': createTime,
'businessId': businessId,
'type': type,
'businessType': businessType,
};
}
/// 积分页面信息响应(兼容旧版单块字段 + `/v1/user/credits-page` 分页结构)
class CreditsPageInfoResponse extends Entity { class CreditsPageInfoResponse extends Entity {
CreditsPageInfoResponse({ CreditsPageInfoResponse({
this.credits, this.credits,
this.freeTimes, this.freeTimes,
this.vipExpireTime, this.vipExpireTime,
this.isVip, this.isVip,
this.total,
this.current,
this.pages,
this.size,
this.records,
}); });
final int? credits; final int? credits;
@ -382,13 +436,43 @@ class CreditsPageInfoResponse extends Entity {
final String? vipExpireTime; final String? vipExpireTime;
final bool? isVip; final bool? isVip;
/// 分页:总条数
final int? total;
/// 分页:当前页
final int? current;
/// 分页:总页数
final int? pages;
/// 分页:每页大小
final int? size;
final List<CreditRecordItem>? records;
static int? _toInt(dynamic v) {
if (v == null) return null;
if (v is int) return v;
if (v is num) return v.toInt();
if (v is String && v.isNotEmpty) return int.tryParse(v);
return null;
}
@override @override
factory CreditsPageInfoResponse.fromJson(Map<String, dynamic> json) { factory CreditsPageInfoResponse.fromJson(Map<String, dynamic> json) {
final recList = json['records'] as List<dynamic>?;
return CreditsPageInfoResponse( return CreditsPageInfoResponse(
credits: json['credits'] as int?, credits: _toInt(json['credits']),
freeTimes: json['freeTimes'] as int?, freeTimes: _toInt(json['freeTimes']),
vipExpireTime: json['vipExpireTime'] as String?, vipExpireTime: json['vipExpireTime'] as String?,
isVip: json['isVip'] as bool?, isVip: json['isVip'] is bool ? json['isVip'] as bool? : null,
total: _toInt(json['total']),
current: _toInt(json['current']),
pages: _toInt(json['pages']),
size: _toInt(json['size']),
records: recList
?.map((e) => CreditRecordItem.fromJson(e as Map<String, dynamic>))
.toList(),
); );
} }
@ -398,5 +482,10 @@ class CreditsPageInfoResponse extends Entity {
'freeTimes': freeTimes, 'freeTimes': freeTimes,
'vipExpireTime': vipExpireTime, 'vipExpireTime': vipExpireTime,
'isVip': isVip, 'isVip': isVip,
'total': total,
'current': current,
'pages': pages,
'size': size,
'records': records?.map((e) => e.toJson()).toList(),
}; };
} }

View File

@ -169,10 +169,11 @@ class OrderDetailResponse extends Entity {
@override @override
factory OrderDetailResponse.fromJson(Map<String, dynamic> json) { factory OrderDetailResponse.fromJson(Map<String, dynamic> json) {
final idRaw = json['orderId'] ?? json['id'];
return OrderDetailResponse( return OrderDetailResponse(
orderId: json['orderId'] as String?, orderId: idRaw == null ? null : idRaw.toString(),
status: json['status'] as String?, status: json['status'] as String?,
amount: json['amount'] as String?, amount: json['amount'] == null ? null : json['amount'].toString(),
); );
} }
@ -198,8 +199,9 @@ class GooglePayCallbackResponse extends Entity {
@override @override
factory GooglePayCallbackResponse.fromJson(Map<String, dynamic> json) { factory GooglePayCallbackResponse.fromJson(Map<String, dynamic> json) {
final idRaw = json['orderId'] ?? json['id'];
return GooglePayCallbackResponse( return GooglePayCallbackResponse(
orderId: json['orderId'] as String?, orderId: idRaw == null ? null : idRaw.toString(),
status: json['status'] as String?, status: json['status'] as String?,
creditsAdded: json['creditsAdded'] as bool?, creditsAdded: json['creditsAdded'] as bool?,
); );
@ -212,3 +214,84 @@ class GooglePayCallbackResponse extends Entity {
'creditsAdded': creditsAdded, 'creditsAdded': creditsAdded,
}; };
} }
/// `getPaymentDetailList` 返回的订单摘要项
class PaymentOrderSummary extends Entity {
PaymentOrderSummary({
this.id,
this.status,
this.currency,
this.count,
this.activityId,
this.addCredits,
this.redirectUrl,
});
final String? id;
final String? status;
final String? currency;
final String? count;
final String? activityId;
final bool? addCredits;
final String? redirectUrl;
static String? _str(dynamic v) {
if (v == null) return null;
if (v is String) return v;
return v.toString();
}
@override
factory PaymentOrderSummary.fromJson(Map<String, dynamic> json) {
return PaymentOrderSummary(
id: _str(json['id']),
status: json['status'] as String?,
currency: json['currency'] as String?,
count: _str(json['count']),
activityId: _str(json['activityId']),
addCredits: json['addCredits'] is bool ? json['addCredits'] as bool? : null,
redirectUrl: json['redirectUrl'] as String?,
);
}
@override
Map<String, dynamic> toJson() => {
'id': id,
'status': status,
'currency': currency,
'count': count,
'activityId': activityId,
'addCredits': addCredits,
'redirectUrl': redirectUrl,
};
}
/// `/v1/payment/getPaymentDetailList` data 为数组时的包装
class PaymentOrderListResponse extends Entity {
PaymentOrderListResponse({required this.orders});
final List<PaymentOrderSummary> orders;
static PaymentOrderListResponse fromDataList(List<dynamic> list) {
return PaymentOrderListResponse(
orders: [
for (final e in list)
PaymentOrderSummary.fromJson(e as Map<String, dynamic>),
],
);
}
@override
factory PaymentOrderListResponse.fromJson(Map<String, dynamic> json) {
final raw = json['orders'] as List<dynamic>?;
if (raw == null) {
return PaymentOrderListResponse(orders: const []);
}
return PaymentOrderListResponse.fromDataList(raw);
}
@override
Map<String, dynamic> toJson() => {
'orders': orders.map((e) => e.toJson()).toList(),
};
}

View File

@ -290,3 +290,167 @@ class AccountResponse extends Entity {
'freeTimes': freeTimes, 'freeTimes': freeTimes,
}; };
} }
/// `/v1/config/app-language` 响应 data
class AppLanguageConfigResponse extends Entity {
AppLanguageConfigResponse({this.config});
/// 语言配置 JSON 字符串
final String? config;
@override
factory AppLanguageConfigResponse.fromJson(Map<String, dynamic> json) {
return AppLanguageConfigResponse(
config: json['config'] as String?,
);
}
@override
Map<String, dynamic> toJson() => {'config': config};
}
/// `/v1/user/payments` 单条支付记录
class UserPaymentRecord extends Entity {
UserPaymentRecord({
this.amount,
this.payTime,
this.payMethod,
this.currency,
this.payId,
});
final int? amount;
final int? payTime;
final String? payMethod;
final String? currency;
final String? payId;
static int? _toInt(dynamic v) {
if (v == null) return null;
if (v is int) return v;
if (v is num) return v.toInt();
if (v is String && v.isNotEmpty) return int.tryParse(v);
return null;
}
@override
factory UserPaymentRecord.fromJson(Map<String, dynamic> json) {
return UserPaymentRecord(
amount: _toInt(json['amount']),
payTime: _toInt(json['payTime']),
payMethod: json['payMethod'] as String?,
currency: json['currency'] as String?,
payId: json['payId'] as String?,
);
}
@override
Map<String, dynamic> toJson() => {
'amount': amount,
'payTime': payTime,
'payMethod': payMethod,
'currency': currency,
'payId': payId,
};
}
/// `/v1/user/payments` 列表包装
class UserPaymentsListResponse extends Entity {
UserPaymentsListResponse({required this.items});
final List<UserPaymentRecord> items;
static UserPaymentsListResponse fromDataList(List<dynamic> list) {
return UserPaymentsListResponse(
items: [
for (final e in list)
UserPaymentRecord.fromJson(e as Map<String, dynamic>),
],
);
}
@override
factory UserPaymentsListResponse.fromJson(Map<String, dynamic> json) {
final raw = json['items'] as List<dynamic>?;
if (raw == null) {
return UserPaymentsListResponse(items: const []);
}
return UserPaymentsListResponse.fromDataList(raw);
}
@override
Map<String, dynamic> toJson() => {
'items': items.map((e) => e.toJson()).toList(),
};
}
/// 未读数按类型
class UnreadCountItem extends Entity {
UnreadCountItem({this.count, this.type});
final int? count;
final String? type;
static int? _toInt(dynamic v) {
if (v == null) return null;
if (v is int) return v;
if (v is num) return v.toInt();
if (v is String && v.isNotEmpty) return int.tryParse(v);
return null;
}
@override
factory UnreadCountItem.fromJson(Map<String, dynamic> json) {
return UnreadCountItem(
count: _toInt(json['count']),
type: json['type'] as String?,
);
}
@override
Map<String, dynamic> toJson() => {
'count': count,
'type': type,
};
}
/// `/v1/user/unread-message-count` 响应 data
class UnreadMessageCountResponse extends Entity {
UnreadMessageCountResponse({
this.unreadCountList,
this.totalUnreadCount,
this.lastUpdateTime,
});
final List<UnreadCountItem>? unreadCountList;
final int? totalUnreadCount;
final int? lastUpdateTime;
static int? _toInt(dynamic v) {
if (v == null) return null;
if (v is int) return v;
if (v is num) return v.toInt();
if (v is String && v.isNotEmpty) return int.tryParse(v);
return null;
}
@override
factory UnreadMessageCountResponse.fromJson(Map<String, dynamic> json) {
final raw = json['unreadCountList'] as List<dynamic>?;
return UnreadMessageCountResponse(
unreadCountList: raw
?.map((e) => UnreadCountItem.fromJson(e as Map<String, dynamic>))
.toList(),
totalUnreadCount: _toInt(json['totalUnreadCount']),
lastUpdateTime: _toInt(json['lastUpdateTime']),
);
}
@override
Map<String, dynamic> toJson() => {
'unreadCountList':
unreadCountList?.map((e) => e.toJson()).toList(),
'totalUnreadCount': totalUnreadCount,
'lastUpdateTime': lastUpdateTime,
};
}

View File

@ -201,6 +201,9 @@ abstract class FrameworkAuthService {
if (uid.isEmpty) return; if (uid.isEmpty) return;
final config = ApiClient.instance.config; final config = ApiClient.instance.config;
final backendApp = defaultTargetPlatform == TargetPlatform.iOS
? config.backendAppTypeIOS
: config.backendAppTypeAndroid;
// 上报 Adjust 归因 // 上报 Adjust 归因
final adjustReferer = await AttributionService.getAdjustReferrer(); final adjustReferer = await AttributionService.getAdjustReferrer();
@ -210,7 +213,7 @@ abstract class FrameworkAuthService {
: 'android_adjust'; : 'android_adjust';
try { try {
final rAdjust = await UserApi.referrer( final rAdjust = await UserApi.referrer(
app: config.appId, app: backendApp,
userId: uid, userId: uid,
referer: adjustReferer, referer: adjustReferer,
deviceId: deviceId, deviceId: deviceId,
@ -232,7 +235,7 @@ abstract class FrameworkAuthService {
if (playReferrer != null && playReferrer.isNotEmpty) { if (playReferrer != null && playReferrer.isNotEmpty) {
try { try {
final rGg = await UserApi.referrer( final rGg = await UserApi.referrer(
app: config.appId, app: backendApp,
userId: uid, userId: uid,
referer: playReferrer, referer: playReferrer,
deviceId: deviceId, deviceId: deviceId,
@ -252,8 +255,10 @@ abstract class FrameworkAuthService {
// 获取通用信息 // 获取通用信息
try { try {
final commonRes = await UserApi.getCommonInfo( final commonRes = await UserApi.getCommonInfo(
app: config.appId, app: backendApp,
pkg: config.packageName,
userId: uid, userId: uid,
deviceId: deviceId,
); );
if (commonRes.isSuccess && commonRes.data != null) { if (commonRes.isSuccess && commonRes.data != null) {
_callbacks?.onCommonInfoLoaded(commonRes.data!); _callbacks?.onCommonInfoLoaded(commonRes.data!);

View File

@ -3,6 +3,8 @@ import '../api/proxy_client.dart';
import '../entities/feedback_entities.dart'; import '../entities/feedback_entities.dart';
/// 举报/反馈相关 API(使用原始字段名) /// 举报/反馈相关 API(使用原始字段名)
///
/// **Body**(`submit`):`fileUrls`、`contentType`、`content`(与文档顺序一致)。
abstract final class FeedbackApi { abstract final class FeedbackApi {
static ProxyClient get _client => ApiClient.instance.proxy; static ProxyClient get _client => ApiClient.instance.proxy;
@ -31,8 +33,8 @@ abstract final class FeedbackApi {
entityFactory: SubmitFeedbackResponse.fromJson, entityFactory: SubmitFeedbackResponse.fromJson,
body: { body: {
'fileUrls': fileUrls, 'fileUrls': fileUrls,
'content': content,
'contentType': contentType, 'contentType': contentType,
'content': content,
}, },
); );
} }

View File

@ -3,6 +3,8 @@ import '../api/proxy_client.dart';
import '../entities/image_entities.dart'; import '../entities/image_entities.dart';
/// 图片/视频生成相关 API(使用原始字段名) /// 图片/视频生成相关 API(使用原始字段名)
///
/// **请求头**:需登录接口自动附带 `pkg` 与 `User_token`(参见 [ProxyClient.request])。
abstract final class ImageApi { abstract final class ImageApi {
static ProxyClient get _client => ApiClient.instance.proxy; static ProxyClient get _client => ApiClient.instance.proxy;
@ -47,13 +49,20 @@ abstract final class ImageApi {
} }
/// 创建文生图任务 /// 创建文生图任务
///
/// **Body**(原始字段):`imgCount`(默认 1)、`aspectRatio`(可选)、`prompt`(必填)。
static Future<EntityResponse<CreateTaskResponse>> createTxt2Img({ static Future<EntityResponse<CreateTaskResponse>> createTxt2Img({
required String app, required String app,
required String prompt, required String prompt,
String? ch, String? ch,
String? userId, String? userId,
int imgCount = 1,
String? aspectRatio,
/// 兼容旧调用,等价于 [aspectRatio]。
String? quest, String? quest,
}) async { }) async {
final ratio = aspectRatio ?? quest;
return _client.requestEntity( return _client.requestEntity(
path: '/v1/image/txt2img_create', path: '/v1/image/txt2img_create',
method: 'POST', method: 'POST',
@ -64,8 +73,8 @@ abstract final class ImageApi {
if (userId != null) 'userId': userId, if (userId != null) 'userId': userId,
}, },
body: { body: {
'declaration': 1, 'imgCount': imgCount,
if (quest != null) 'quest': quest, if (ratio != null && ratio.isNotEmpty) 'aspectRatio': ratio,
'prompt': prompt, 'prompt': prompt,
}, },
); );
@ -209,21 +218,27 @@ abstract final class ImageApi {
); );
} }
/// 获取积分页面信息 /// 获取积分页面信息(与 `UserApi.getCreditsPage` 相同,走 `GET /v1/user/credits-page`)
///
/// 保留此方法以兼容旧调用;[app]、[userId]、[ch] 不再参与请求(接口只吃分页参数)。
static Future<EntityResponse<CreditsPageInfoResponse>> getCreditsPageInfo({ static Future<EntityResponse<CreditsPageInfoResponse>> getCreditsPageInfo({
required String app, String page = '1',
String size = '10',
String type = '1',
String? app,
String? userId, String? userId,
String? ch, String? ch,
}) async { }) async {
return _client.requestEntity( return _client.requestEntity(
path: '/v1/image/getCreditsPageInfo', path: '/v1/user/credits-page',
method: 'GET', method: 'GET',
entityFactory: CreditsPageInfoResponse.fromJson, entityFactory: CreditsPageInfoResponse.fromJson,
queryParams: { queryParams: {
'app': app, 'page': page,
if (userId != null) 'userId': userId, 'size': size,
if (ch != null) 'ch': ch, 'type': type,
}, },
); );
} }
} }

View File

@ -3,53 +3,61 @@ import '../api/proxy_client.dart';
import '../entities/payment_entities.dart'; import '../entities/payment_entities.dart';
/// 支付相关 API(使用原始字段名) /// 支付相关 API(使用原始字段名)
///
/// **请求头**:需登录接口自动附带 `pkg` 与 `User_token`。
abstract final class PaymentApi { abstract final class PaymentApi {
static ProxyClient get _client => ApiClient.instance.proxy; static ProxyClient get _client => ApiClient.instance.proxy;
/// 获取 Google 商品列表(Android) /// 获取 Google 商品列表(Android)
///
/// 默认 `app` 为 [AppConfig.backendAppTypeAndroid];可选 [client]、[country]。
static Future<EntityResponse<PaymentProductsResponse>> static Future<EntityResponse<PaymentProductsResponse>>
getGooglePayActivities({ getGooglePayActivities({
String? app, String? app,
String? shield, String? client,
String? country, String? country,
String? pkg, String? pkg,
}) async { }) async {
final cfg = ApiClient.instance.config;
return _client.requestEntity( return _client.requestEntity(
path: '/v1/payment/getGooglePayActivities', path: '/v1/payment/getGooglePayActivities',
method: 'GET', method: 'GET',
entityFactory: PaymentProductsResponse.fromJson, entityFactory: PaymentProductsResponse.fromJson,
queryParams: { queryParams: {
'app': app ?? ApiClient.instance.config.appId, 'app': app ?? cfg.backendAppTypeAndroid,
'pkg': pkg ?? ApiClient.instance.config.packageName, 'pkg': pkg ?? cfg.packageName,
if (shield != null) 'shield': shield, if (client != null && client.isNotEmpty) 'client': client,
if (country != null) 'country': country, if (country != null && country.isNotEmpty) 'country': country,
}, },
); );
} }
/// 获取 Apple 商品列表(iOS) /// 获取 Apple 商品列表(iOS)
///
/// 默认 `app` 为 [AppConfig.backendAppTypeIOS]。
static Future<EntityResponse<PaymentProductsResponse>> getApplePayActivities({ static Future<EntityResponse<PaymentProductsResponse>> getApplePayActivities({
String? app, String? app,
String? shield, String? client,
String? country, String? country,
String? pkg, String? pkg,
}) async { }) async {
final cfg = ApiClient.instance.config;
return _client.requestEntity( return _client.requestEntity(
path: '/v1/payment/getApplePayActivities', path: '/v1/payment/getApplePayActivities',
method: 'GET', method: 'GET',
entityFactory: PaymentProductsResponse.fromJson, entityFactory: PaymentProductsResponse.fromJson,
queryParams: { queryParams: {
'app': app ?? ApiClient.instance.config.backendAppTypeIOS, 'app': app ?? cfg.backendAppTypeIOS,
'pkg': pkg ?? ApiClient.instance.config.packageName, 'pkg': pkg ?? cfg.packageName,
if (shield != null) 'shield': shield, if (client != null && client.isNotEmpty) 'client': client,
if (country != null) 'country': country, if (country != null && country.isNotEmpty) 'country': country,
}, },
); );
} }
/// 获取支付方式列表 /// 获取支付方式列表(body 中 [activityId] 为 int,与接口一致)
static Future<EntityResponse<PaymentMethodsResponse>> getPaymentMethods({ static Future<EntityResponse<PaymentMethodsResponse>> getPaymentMethods({
required String activityId, required int activityId,
String? country, String? country,
}) async { }) async {
return _client.requestEntity( return _client.requestEntity(
@ -64,6 +72,13 @@ abstract final class PaymentApi {
} }
/// 创建支付订单 /// 创建支付订单
///
/// **Query**:`app`、`userId`。
/// **Body**:必填字段 + 文档中的可选字段(以下为原始名,非空才写入):
/// `lastName`、`country`、`expireMonth`、`accountName`、`userInfoType`、
/// `automaticRenewal`、`channel`、`cvcCode`、`channelType`、`firstName`、
/// `subPaymentMethod`、`phone`、`tgOrderId`、`tgId`、`name`、`expireYear`、
/// `card`、`status`;另保留换皮用的 `lineage`、`armor`(视 [FieldMapping] 而定)。
static Future<EntityResponse<CreatePaymentResponse>> createPayment({ static Future<EntityResponse<CreatePaymentResponse>> createPayment({
required String app, required String app,
required String userId, required String userId,
@ -72,6 +87,24 @@ abstract final class PaymentApi {
String? paymentType, String? paymentType,
String? lineage, String? lineage,
String? armor, String? armor,
String? lastName,
String? country,
String? expireMonth,
String? accountName,
String? userInfoType,
bool? automaticRenewal,
String? channel,
String? cvcCode,
String? channelType,
String? firstName,
String? subPaymentMethod,
String? phone,
String? tgOrderId,
String? tgId,
String? name,
String? expireYear,
String? card,
String? status,
}) async { }) async {
return _client.requestEntity( return _client.requestEntity(
path: '/v1/payment/createPayment', path: '/v1/payment/createPayment',
@ -85,13 +118,72 @@ abstract final class PaymentApi {
'paymentMethod': paymentMethod, 'paymentMethod': paymentMethod,
if (paymentType != null && paymentType.isNotEmpty) if (paymentType != null && paymentType.isNotEmpty)
'paymentType': paymentType, 'paymentType': paymentType,
if (lineage != null) 'lineage': lineage, if (lineage != null && lineage.isNotEmpty) 'lineage': lineage,
if (armor != null) 'armor': armor, if (armor != null && armor.isNotEmpty) 'armor': armor,
if (lastName != null && lastName.isNotEmpty) 'lastName': lastName,
if (country != null && country.isNotEmpty) 'country': country,
if (expireMonth != null && expireMonth.isNotEmpty)
'expireMonth': expireMonth,
if (accountName != null && accountName.isNotEmpty)
'accountName': accountName,
if (userInfoType != null && userInfoType.isNotEmpty)
'userInfoType': userInfoType,
if (automaticRenewal != null) 'automaticRenewal': automaticRenewal,
if (channel != null && channel.isNotEmpty) 'channel': channel,
if (cvcCode != null && cvcCode.isNotEmpty) 'cvcCode': cvcCode,
if (channelType != null && channelType.isNotEmpty)
'channelType': channelType,
if (firstName != null && firstName.isNotEmpty) 'firstName': firstName,
if (subPaymentMethod != null && subPaymentMethod.isNotEmpty)
'subPaymentMethod': subPaymentMethod,
if (phone != null && phone.isNotEmpty) 'phone': phone,
if (tgOrderId != null && tgOrderId.isNotEmpty) 'tgOrderId': tgOrderId,
if (tgId != null && tgId.isNotEmpty) 'tgId': tgId,
if (name != null && name.isNotEmpty) 'name': name,
if (expireYear != null && expireYear.isNotEmpty) 'expireYear': expireYear,
if (card != null && card.isNotEmpty) 'card': card,
if (status != null && status.isNotEmpty) 'status': status,
}, },
); );
} }
/// 获取订单详情 /// 支付订单列表(data 为数组)
static Future<EntityResponse<PaymentOrderListResponse>>
getPaymentDetailList({
required String app,
String? userId,
String? paymentMethod,
String? filterStatus,
}) async {
final response = await _client.request(
path: '/v1/payment/getPaymentDetailList',
method: 'GET',
queryParams: {
'app': app,
if (userId != null && userId.isNotEmpty) 'userId': userId,
if (paymentMethod != null && paymentMethod.isNotEmpty)
'paymentMethod': paymentMethod,
if (filterStatus != null && filterStatus.isNotEmpty)
'filterStatus': filterStatus,
},
);
if (response.isSuccess && response.data is List) {
return EntityResponse<PaymentOrderListResponse>(
code: response.code,
msg: response.msg,
data: PaymentOrderListResponse.fromDataList(
response.data! as List<dynamic>,
),
);
}
return EntityResponse<PaymentOrderListResponse>(
code: response.code,
msg: response.msg,
data: null,
);
}
/// 获取订单详情(query 使用原始字段 `id` 表示订单/支付 ID)
static Future<EntityResponse<OrderDetailResponse>> getOrderDetail({ static Future<EntityResponse<OrderDetailResponse>> getOrderDetail({
required String userId, required String userId,
required String orderId, required String orderId,
@ -102,30 +194,35 @@ abstract final class PaymentApi {
entityFactory: OrderDetailResponse.fromJson, entityFactory: OrderDetailResponse.fromJson,
queryParams: { queryParams: {
'userId': userId, 'userId': userId,
'orderId': orderId, 'id': orderId,
}, },
); );
} }
/// Google 支付结果回调 /// Google 支付结果回调
///
/// **Query**:`app`、`userId`。
/// **Body**(顺序与文档一致):`signature`、`purchaseData`、`id`、`userId`。
static Future<EntityResponse<GooglePayCallbackResponse>> googlepay({ static Future<EntityResponse<GooglePayCallbackResponse>> googlepay({
required String signature, required String signature,
required String purchaseData, required String purchaseData,
required String orderId, required String orderId,
required String userId, required String userId,
String? app,
}) async { }) async {
final cfg = ApiClient.instance.config;
return _client.requestEntity( return _client.requestEntity(
path: '/v1/payment/googlepay', path: '/v1/payment/googlepay',
method: 'POST', method: 'POST',
entityFactory: GooglePayCallbackResponse.fromJson, entityFactory: GooglePayCallbackResponse.fromJson,
queryParams: { queryParams: {
'app': ApiClient.instance.config.appId, 'app': app ?? cfg.backendAppTypeAndroid,
'userId': userId, 'userId': userId,
}, },
body: { body: {
'signature': signature, 'signature': signature,
'purchaseData': purchaseData, 'purchaseData': purchaseData,
'orderId': orderId, 'id': orderId,
'userId': userId, 'userId': userId,
}, },
); );

View File

@ -1,26 +1,30 @@
import '../api/api_client.dart'; import '../api/api_client.dart';
import '../api/api_response.dart'; import '../api/api_response.dart';
import '../api/proxy_client.dart'; import '../api/proxy_client.dart';
import '../entities/image_entities.dart';
import '../entities/user_entities.dart'; import '../entities/user_entities.dart';
/// 用户相关 API(使用原始字段名) /// 用户相关 API(使用原始字段名)
///
/// **请求头**:除 [UserApi.fastLogin] 外,需登录接口均由 [ProxyClient] 自动附带
/// `pkg`(包名)与 `User_token`(已设置 token 时)。fast_login 仅带 `pkg`,不带 token。
///
/// **请求体**:与《客户端指南》一致,使用解密后的**原始字段名**(如 `referer`、`deviceId`)。
abstract final class UserApi { abstract final class UserApi {
static ProxyClient get _client => ApiClient.instance.proxy; static ProxyClient get _client => ApiClient.instance.proxy;
/// 设备快速登录 /// 设备快速登录
/// [deviceId] 设备ID (必填) ///
/// [sign] 签名,MD5(deviceId)大写32位 (必填) /// **请求头**:仅 `pkg`(无 `User_token`)。
/// [referer] 归因信息 /// **Query**:`app`、`type`(默认 `fb`)、`pkg`、`ch`(可选)。
/// [ch] 渠道 /// **Body**:`referer`、`sign`、`deviceId`。
/// [type] referrer类型 (af/fb/gg/ios_adjust/android_adjust)
/// [app] 应用类型 (iOS: HIOS / Android: HAndroid)
static Future<EntityResponse<FastLoginResponse>> fastLogin({ static Future<EntityResponse<FastLoginResponse>> fastLogin({
required String deviceId, required String deviceId,
required String sign, required String sign,
required String app,
String? referer, String? referer,
String? ch, String? ch,
String? type, String? type,
String? app,
}) async { }) async {
final config = ApiClient.instance.config; final config = ApiClient.instance.config;
return _client.requestEntity( return _client.requestEntity(
@ -28,20 +32,25 @@ abstract final class UserApi {
method: 'POST', method: 'POST',
entityFactory: FastLoginResponse.fromJson, entityFactory: FastLoginResponse.fromJson,
queryParams: { queryParams: {
if (ch != null) 'ch': ch, if (ch != null && ch.isNotEmpty) 'ch': ch,
'pkg': config.packageName, 'pkg': config.packageName,
if (type != null) 'type': type, 'type': type ?? 'fb',
if (app != null) 'app': app, 'app': app,
}, },
body: { body: {
'referer': referer ?? '', 'referer': referer ?? '',
'sign': sign, 'sign': sign,
'deviceId': deviceId, 'deviceId': deviceId,
}, },
includeUserTokenInHeader: false,
); );
} }
/// 归因上报 /// 归因上报
///
/// **请求头**:`pkg`、`User_token`。
/// **Query**:`app`、`userId`、`type`(默认 `fb`)、`pkg`。
/// **Body**:`referer`、`deviceId`。
static Future<ApiResponse> referrer({ static Future<ApiResponse> referrer({
required String app, required String app,
required String userId, required String userId,
@ -56,7 +65,7 @@ abstract final class UserApi {
queryParams: { queryParams: {
'app': app, 'app': app,
'userId': userId, 'userId': userId,
if (type != null) 'type': type, 'type': type ?? 'fb',
'pkg': pkg ?? ApiClient.instance.config.packageName, 'pkg': pkg ?? ApiClient.instance.config.packageName,
}, },
body: { body: {
@ -67,15 +76,20 @@ abstract final class UserApi {
} }
/// 获取用户通用信息 /// 获取用户通用信息
///
/// 与当前接口约定一致(原始 query 字段名,会经 [AppConfig.fieldMapping] 映射):
/// - [app] 必填:应用渠道标识(常见 iOS `HIOS` / Android `HAndroid`,与 fast_login 一致)
/// - [pkg] 必填:应用包名
/// - 其余可选:[client]、[userId]、[ch]、[inviteBy]、[deviceId]、[clientId]
static Future<EntityResponse<CommonInfoResponse>> getCommonInfo({ static Future<EntityResponse<CommonInfoResponse>> getCommonInfo({
required String app, required String app,
String? shield, required String pkg,
String? client,
String? userId, String? userId,
String? ch, String? ch,
String? item, String? inviteBy,
String? deviceId, String? deviceId,
String? gauntlet, String? clientId,
String? pkg,
}) async { }) async {
return _client.requestEntity( return _client.requestEntity(
path: '/v1/user/common_info', path: '/v1/user/common_info',
@ -83,13 +97,29 @@ abstract final class UserApi {
entityFactory: CommonInfoResponse.fromJson, entityFactory: CommonInfoResponse.fromJson,
queryParams: { queryParams: {
'app': app, 'app': app,
if (shield != null) 'shield': shield, 'pkg': pkg,
if (userId != null) 'userId': userId, if (client != null && client.isNotEmpty) 'client': client,
if (ch != null) 'ch': ch, if (userId != null && userId.isNotEmpty) 'userId': userId,
if (item != null) 'item': item, if (ch != null && ch.isNotEmpty) 'ch': ch,
if (deviceId != null) 'deviceId': deviceId, if (inviteBy != null && inviteBy.isNotEmpty) 'inviteBy': inviteBy,
if (gauntlet != null) 'gauntlet': gauntlet, if (deviceId != null && deviceId.isNotEmpty) 'deviceId': deviceId,
if (pkg != null) 'pkg': pkg, if (clientId != null && clientId.isNotEmpty) 'clientId': clientId,
},
);
}
/// 获取 App 语言配置(需登录态)
static Future<EntityResponse<AppLanguageConfigResponse>> getAppLanguage({
required String pkg,
String? lang,
}) async {
return _client.requestEntity(
path: '/v1/config/app-language',
method: 'GET',
entityFactory: AppLanguageConfigResponse.fromJson,
queryParams: {
'pkg': pkg,
if (lang != null && lang.isNotEmpty) 'lang': lang,
}, },
); );
} }
@ -110,6 +140,68 @@ abstract final class UserApi {
); );
} }
/// 积分分页流水(`GET /v1/user/credits-page`)
static Future<EntityResponse<CreditsPageInfoResponse>> getCreditsPage({
String page = '1',
String size = '10',
String type = '1',
}) async {
return _client.requestEntity(
path: '/v1/user/credits-page',
method: 'GET',
entityFactory: CreditsPageInfoResponse.fromJson,
queryParams: {
'page': page,
'size': size,
'type': type,
},
);
}
/// 用户支付记录列表(`GET /v1/user/payments`,data 为数组)
static Future<EntityResponse<UserPaymentsListResponse>> getUserPayments({
required String app,
String? userId,
}) async {
final response = await _client.request(
path: '/v1/user/payments',
method: 'GET',
queryParams: {
'app': app,
if (userId != null && userId.isNotEmpty) 'userId': userId,
},
);
if (response.isSuccess && response.data is List) {
return EntityResponse<UserPaymentsListResponse>(
code: response.code,
msg: response.msg,
data: UserPaymentsListResponse.fromDataList(
response.data! as List<dynamic>,
),
);
}
return EntityResponse<UserPaymentsListResponse>(
code: response.code,
msg: response.msg,
data: null,
);
}
/// 未读消息数(`types` 可为逗号分隔或后端约定的枚举串)
static Future<EntityResponse<UnreadMessageCountResponse>>
getUnreadMessageCount({
String? types,
}) async {
return _client.requestEntity(
path: '/v1/user/unread-message-count',
method: 'GET',
entityFactory: UnreadMessageCountResponse.fromJson,
queryParams: {
if (types != null && types.isNotEmpty) 'types': types,
},
);
}
/// 删除账号 /// 删除账号
static Future<ApiResponse> deleteAccount({ static Future<ApiResponse> deleteAccount({
required String app, required String app,