708 lines
15 KiB
Markdown
708 lines
15 KiB
Markdown
# Client Proxy Framework 使用指南
|
||
|
||
## 1. 框架概述
|
||
|
||
`client_proxy_framework` 是一个通用代理 API 框架,用于 Flutter 移动应用与后端 API 的通信。框架已封装好请求/响应的加密解密、字段映射等逻辑,换皮应用只需配置少量参数即可使用。
|
||
|
||
### 核心特性
|
||
|
||
- **自动加解密**:请求体和响应体自动进行 AES 加密/解密
|
||
- **字段映射**:框架自动将原始字段名转换为 V2 字段名,响应自动转换回来
|
||
- **强类型实体**:返回强类型实体类,开发者直接使用映射后的字段
|
||
- **统一响应**:`EntityResponse<T>` 提供强类型返回值
|
||
|
||
---
|
||
|
||
## 2. 快速开始
|
||
|
||
### 2.1 添加依赖
|
||
|
||
在 `pubspec.yaml` 中添加:
|
||
|
||
```yaml
|
||
dependencies:
|
||
client_proxy_framework:
|
||
path: ../client_proxy_framework
|
||
```
|
||
|
||
### 2.2 实现配置
|
||
|
||
创建一个类继承 `AppConfig`:
|
||
|
||
```dart
|
||
import 'package:client_proxy_framework/client_proxy_framework.dart';
|
||
|
||
class MyAppConfig extends AppConfig {
|
||
@override
|
||
String get appId => 'YourAppId';
|
||
|
||
@override
|
||
String get packageName => 'com.yourapp.package';
|
||
|
||
@override
|
||
String get aesKey => 'your-16-char-key';
|
||
|
||
@override
|
||
String get preBaseUrl => 'https://pre-api.example.com';
|
||
|
||
@override
|
||
String get prodBaseUrl => 'https://api.example.com';
|
||
|
||
@override
|
||
String get proxyPath => '/quester/defender/summoner';
|
||
}
|
||
```
|
||
|
||
### 2.3 初始化
|
||
|
||
在 `main.dart` 中初始化:
|
||
|
||
```dart
|
||
void main() {
|
||
ApiClient.init(MyAppConfig());
|
||
runApp(const MyApp());
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 设计理念
|
||
|
||
### 3.1 请求流程
|
||
|
||
```
|
||
调用层 (原始字段)
|
||
↓
|
||
ProxyClient.request()
|
||
↓
|
||
字段映射 (原始 → V2)
|
||
↓
|
||
AES 加密
|
||
↓
|
||
发送请求
|
||
```
|
||
|
||
### 3.2 响应流程
|
||
|
||
```
|
||
接收响应
|
||
↓
|
||
AES 解密
|
||
↓
|
||
字段映射 (V2 → 原始)
|
||
↓
|
||
转换为实体类
|
||
↓
|
||
调用层 (实体类)
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 配置项详解
|
||
|
||
### AppConfig 配置
|
||
|
||
| 配置项 | 必填 | 说明 |
|
||
|--------|------|------|
|
||
| `appId` | 是 | 应用标识,对应代理请求的 `hero_class` |
|
||
| `packageName` | 是 | 应用包名,如 `com.example.app` |
|
||
| `aesKey` | 是 | AES 加密密钥,长度需为 16 字符 |
|
||
| `preBaseUrl` | 是 | 预发环境域名 |
|
||
| `prodBaseUrl` | 是 | 生产环境域名 |
|
||
| `proxyPath` | 是 | 代理入口路径 |
|
||
| `debugBaseUrlOverride` | 否 | 调试时本地代理地址 |
|
||
| `fieldMapping` | 否 | 字段映射表,默认使用 `petsHeroAIFieldMapping` |
|
||
|
||
---
|
||
|
||
## 5. API 服务
|
||
|
||
所有 API 方法使用**原始字段名**,返回**强类型实体**。
|
||
|
||
### 5.1 UserApi - 用户相关
|
||
|
||
#### 5.1.1 fastLogin - 设备快速登录
|
||
|
||
```dart
|
||
final res = await UserApi.fastLogin(
|
||
deviceId: '设备ID',
|
||
sign: 'MD5(deviceId)大写',
|
||
referer: '归因来源', // 可选
|
||
ch: '渠道号', // 可选
|
||
type: '类型', // 可选
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final loginInfo = res.data!;
|
||
final token = loginInfo.userToken;
|
||
final credits = loginInfo.credits;
|
||
final userId = loginInfo.userId;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `FastLoginResponse`
|
||
- `userToken`: 用户 Token
|
||
- `userId`: 用户 ID
|
||
- `credits`: 积分
|
||
- `avatar`: 头像
|
||
- `userName`: 用户名
|
||
- `countryCode`: 国家码
|
||
- `isVip`: 是否 VIP
|
||
- 等等...
|
||
|
||
#### 5.1.2 getCommonInfo - 获取用户通用信息
|
||
|
||
```dart
|
||
final res = await UserApi.getCommonInfo(
|
||
app: '应用ID',
|
||
userId: '用户ID', // 可选
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final info = res.data!;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `CommonInfoResponse`
|
||
|
||
#### 5.1.3 getAccount - 获取账户信息
|
||
|
||
```dart
|
||
final res = await UserApi.getAccount(
|
||
app: '应用ID',
|
||
userId: '用户ID', // 可选
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final account = res.data!;
|
||
final credits = account.credits;
|
||
final isVip = account.isVip;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `AccountResponse`
|
||
- `credits`: 积分
|
||
- `avatar`: 头像
|
||
- `userName`: 用户名
|
||
- `isVip`: 是否 VIP
|
||
- `freeTimes`: 免费次数
|
||
|
||
---
|
||
|
||
### 5.2 PaymentApi - 支付相关
|
||
|
||
#### 5.2.1 getGooglePayActivities - 获取 Android 商品列表
|
||
|
||
```dart
|
||
final res = await PaymentApi.getGooglePayActivities(
|
||
app: '应用ID', // 可选
|
||
country: '国家', // 可选
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final products = res.data!.productList;
|
||
for (final product in products ?? []) {
|
||
final id = product.productId;
|
||
final price = product.actualAmount;
|
||
final bonus = product.bonus;
|
||
}
|
||
}
|
||
```
|
||
|
||
**返回实体**: `PaymentProductsResponse`
|
||
- `productList`: 商品列表
|
||
- `productId`: 商品 ID
|
||
- `activityId`: 活动 ID
|
||
- `actualAmount`: 实际金额
|
||
- `originAmount`: 原价
|
||
- `bonus`: 赠送积分
|
||
- `title`: 标题
|
||
|
||
#### 5.2.2 getPaymentMethods - 获取支付方式列表
|
||
|
||
```dart
|
||
final res = await PaymentApi.getPaymentMethods(
|
||
activityId: '活动ID',
|
||
country: '国家', // 可选
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final methods = res.data!.paymentMethods;
|
||
for (final method in methods ?? []) {
|
||
final pm = method.paymentMethod;
|
||
final name = method.name;
|
||
final isRecommend = method.recommend;
|
||
}
|
||
}
|
||
```
|
||
|
||
**返回实体**: `PaymentMethodsResponse`
|
||
- `paymentMethods`: 支付方式列表
|
||
- `paymentMethod`: 支付方式
|
||
- `subPaymentMethod`: 子支付方式
|
||
- `name`: 显示名称
|
||
- `icon`: 图标
|
||
- `recommend`: 是否推荐
|
||
|
||
#### 5.2.3 createPayment - 创建支付订单
|
||
|
||
```dart
|
||
final res = await PaymentApi.createPayment(
|
||
app: '应用ID',
|
||
userId: '用户ID',
|
||
activityId: '活动ID',
|
||
paymentMethod: '支付方式',
|
||
paymentType: '支付子类型', // 可选
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final order = res.data!;
|
||
final orderId = order.orderId;
|
||
final payUrl = order.payUrl;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `CreatePaymentResponse`
|
||
- `orderId`: 订单 ID
|
||
- `payUrl`: 支付链接
|
||
- `status`: 状态
|
||
|
||
#### 5.2.4 getOrderDetail - 获取订单详情
|
||
|
||
```dart
|
||
final res = await PaymentApi.getOrderDetail(
|
||
userId: '用户ID',
|
||
orderId: '订单ID',
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final order = res.data!;
|
||
final status = order.status;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `OrderDetailResponse`
|
||
- `orderId`: 订单 ID
|
||
- `status`: 状态
|
||
- `amount`: 金额
|
||
|
||
#### 5.2.5 googlepay - Google Pay 回调
|
||
|
||
```dart
|
||
final res = await PaymentApi.googlepay(
|
||
signature: '购买签名',
|
||
purchaseData: '购买数据',
|
||
orderId: '订单ID',
|
||
userId: '用户ID',
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final result = res.data!;
|
||
final status = result.status;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `GooglePayCallbackResponse`
|
||
- `orderId`: 订单 ID
|
||
- `status`: 状态
|
||
- `creditsAdded`: 是否已加积分
|
||
|
||
---
|
||
|
||
### 5.3 ImageApi - 图片/视频相关
|
||
|
||
#### 5.3.1 getCategoryList - 获取分类列表
|
||
|
||
```dart
|
||
final res = await ImageApi.getCategoryList();
|
||
|
||
if (res.isSuccess) {
|
||
final categories = res.data!.categories;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `CategoryListResponse`
|
||
- `categories`: 分类列表
|
||
- `id`: ID
|
||
- `name`: 名称
|
||
- `icon`: 图标
|
||
|
||
#### 5.3.2 getImg2VideoTasks - 获取任务列表
|
||
|
||
```dart
|
||
final res = await ImageApi.getImg2VideoTasks(
|
||
categoryId: 123, // 可选
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final tasks = res.data!.tasks;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `TasksResponse`
|
||
|
||
#### 5.3.3 getProgress - 查询任务进度
|
||
|
||
```dart
|
||
final res = await ImageApi.getProgress(
|
||
app: '应用ID',
|
||
taskId: '任务ID',
|
||
userId: '用户ID', // 可选
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final progress = res.data!;
|
||
final status = progress.status;
|
||
final resultUrl = progress.resultUrl;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `ProgressResponse`
|
||
- `taskId`: 任务 ID
|
||
- `status`: 状态
|
||
- `progress`: 进度 (0-100)
|
||
- `resultUrl`: 结果 URL
|
||
|
||
#### 5.3.4 createTask - 创建任务
|
||
|
||
```dart
|
||
final res = await ImageApi.createTask(
|
||
userId: '用户ID',
|
||
prompt: '提示词', // 可选
|
||
resolution: '1024x1024', // 可选
|
||
imgUrl: '图片URL', // 可选
|
||
allowance: false, // 可选
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final taskId = res.data!.taskId;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `CreateTaskResponse`
|
||
- `taskId`: 任务 ID
|
||
- `status`: 状态
|
||
|
||
#### 5.3.5 getMyTasks - 获取我的任务列表
|
||
|
||
```dart
|
||
final res = await ImageApi.getMyTasks(
|
||
app: '应用ID',
|
||
page: '1', // 可选
|
||
pageSize: '10', // 可选
|
||
cursor: '游标', // 可选
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final tasks = res.data!.tasks;
|
||
final total = res.data!.total;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `MyTasksResponse`
|
||
- `tasks`: 任务列表
|
||
- `total`: 总数
|
||
- `cursor`: 游标
|
||
|
||
#### 5.3.6 getCreditsPageInfo - 获取积分页面信息
|
||
|
||
```dart
|
||
final res = await ImageApi.getCreditsPageInfo(
|
||
app: '应用ID',
|
||
userId: '用户ID', // 可选
|
||
ch: '渠道', // 可选
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final info = res.data!;
|
||
final credits = info.credits;
|
||
final freeTimes = info.freeTimes;
|
||
final isVip = info.isVip;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `CreditsPageInfoResponse`
|
||
- `credits`: 积分
|
||
- `freeTimes`: 免费次数
|
||
- `isVip`: 是否 VIP
|
||
- `vipExpireTime`: VIP 过期时间
|
||
|
||
---
|
||
|
||
### 5.4 FeedbackApi - 举报/反馈相关
|
||
|
||
#### 5.4.1 getUploadPresignedUrl - 获取上传预签名 URL
|
||
|
||
```dart
|
||
final res = await FeedbackApi.getUploadPresignedUrl(
|
||
fileName: 'image.jpg',
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final result = res.data!;
|
||
final uploadUrl = result.uploadUrl;
|
||
final filePath = result.filePath;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `FeedbackUploadPresignedUrlResponse`
|
||
- `uploadUrl`: 上传 URL
|
||
- `filePath`: 文件路径
|
||
|
||
#### 5.4.2 submit - 提交反馈
|
||
|
||
```dart
|
||
final res = await FeedbackApi.submit(
|
||
fileUrls: ['https://...'],
|
||
content: '反馈内容',
|
||
contentType: 'text/plain',
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final success = res.data!.success;
|
||
}
|
||
```
|
||
|
||
**返回实体**: `SubmitFeedbackResponse`
|
||
- `success`: 是否成功
|
||
- `feedbackId`: 反馈 ID
|
||
|
||
---
|
||
|
||
## 6. 响应处理
|
||
|
||
### 6.1 EntityResponse
|
||
|
||
所有返回实体类的方法都使用 `EntityResponse<T>`:
|
||
|
||
```dart
|
||
final res = await UserApi.fastLogin(...);
|
||
|
||
// 检查成功
|
||
if (res.isSuccess) {
|
||
// 访问实体
|
||
final data = res.data!;
|
||
final token = data.userToken;
|
||
} else {
|
||
// 处理错误
|
||
print('Error: ${res.msg}');
|
||
}
|
||
```
|
||
|
||
### EntityResponse 属性
|
||
|
||
| 属性 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `code` | int | 响应码,0 表示成功 |
|
||
| `msg` | String | 响应消息 |
|
||
| `data` | T? | 实体数据 |
|
||
| `isSuccess` | bool | 便捷属性,code == 0 时为 true |
|
||
|
||
---
|
||
|
||
## 7. 设置用户 Token
|
||
|
||
登录成功后,调用以下方法设置用户 Token:
|
||
|
||
```dart
|
||
// 登录成功后
|
||
final res = await UserApi.fastLogin(...);
|
||
if (res.isSuccess) {
|
||
ApiClient.instance.setUserToken(res.data!.userToken);
|
||
}
|
||
|
||
// 登出时
|
||
ApiClient.instance.setUserToken(null);
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 调试模式
|
||
|
||
框架会自动根据 `kDebugMode` 选择环境:
|
||
|
||
- **调试模式**:使用 `preBaseUrl`(或 `debugBaseUrlOverride`)
|
||
- **发布模式**:使用 `prodBaseUrl`
|
||
|
||
---
|
||
|
||
## 9. 字段映射
|
||
|
||
框架自动处理字段映射,调用层使用原始字段名。
|
||
|
||
### 请求字段映射(原始 → V2)
|
||
|
||
| 原始字段 | V2 字段 |
|
||
|----------|---------|
|
||
| app | sentinel |
|
||
| userId | asset |
|
||
| deviceId | origin |
|
||
| sign | resolution |
|
||
| referer | digest |
|
||
| activityId | warrior |
|
||
| country | vambrace |
|
||
| paymentMethod | resource |
|
||
| paymentType | ceremony |
|
||
| orderId | federation |
|
||
| signature | sample |
|
||
| purchaseData | merchant |
|
||
| taskId | tree |
|
||
| prompt | ledger |
|
||
| resolution | guild |
|
||
| srcImgUrls | commission |
|
||
| fileName1 | gateway |
|
||
| contentType | pauldron |
|
||
| expectedSize | stronghold |
|
||
|
||
### 响应字段映射(V2 → 原始)
|
||
|
||
| V2 字段 | 原始字段 |
|
||
|---------|----------|
|
||
| helm | code/响应码 |
|
||
| rampart | msg |
|
||
| sidekick | data |
|
||
| summon | productList |
|
||
| renew | paymentMethods |
|
||
| reveal | credits |
|
||
| reevaluate | userToken |
|
||
|
||
---
|
||
|
||
## 10. 代码示例
|
||
|
||
### 完整登录流程
|
||
|
||
```dart
|
||
import 'package:client_proxy_framework/client_proxy_framework.dart';
|
||
|
||
class AuthService {
|
||
static Future<bool> login() async {
|
||
final deviceId = await getDeviceId();
|
||
final sign = md5(deviceId).toUpperCase();
|
||
|
||
final res = await UserApi.fastLogin(
|
||
deviceId: deviceId,
|
||
sign: sign,
|
||
referer: 'organic',
|
||
);
|
||
|
||
if (res.isSuccess) {
|
||
final loginInfo = res.data!;
|
||
ApiClient.instance.setUserToken(loginInfo.userToken!);
|
||
UserState.setUserId(loginInfo.userId!);
|
||
UserState.setCredits(loginInfo.credits ?? 0);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 完整支付流程
|
||
|
||
```dart
|
||
class PaymentService {
|
||
// 1. 获取商品列表
|
||
static Future<List<PaymentProductItem>> getProducts() async {
|
||
final res = await PaymentApi.getGooglePayActivities();
|
||
return res.data?.productList ?? [];
|
||
}
|
||
|
||
// 2. 获取支付方式
|
||
static Future<List<PaymentMethodItem>> getPaymentMethods(String activityId) async {
|
||
final res = await PaymentApi.getPaymentMethods(activityId: activityId);
|
||
return res.data?.paymentMethods ?? [];
|
||
}
|
||
|
||
// 3. 创建订单
|
||
static Future<String?> createOrder({
|
||
required String userId,
|
||
required String activityId,
|
||
required String paymentMethod,
|
||
}) async {
|
||
final res = await PaymentApi.createPayment(
|
||
app: ApiClient.instance.config.appId,
|
||
userId: userId,
|
||
activityId: activityId,
|
||
paymentMethod: paymentMethod,
|
||
);
|
||
if (res.isSuccess) {
|
||
return res.data?.orderId;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// 4. Google Pay 回调
|
||
static Future<bool> verifyGooglePay({
|
||
required String signature,
|
||
required String purchaseData,
|
||
required String orderId,
|
||
required String userId,
|
||
}) async {
|
||
final res = await PaymentApi.googlepay(
|
||
signature: signature,
|
||
purchaseData: purchaseData,
|
||
orderId: orderId,
|
||
userId: userId,
|
||
);
|
||
return res.isSuccess;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 11. 常见问题
|
||
|
||
### Q: 如何修改字段映射?
|
||
|
||
A: 覆盖 `AppConfig.fieldMapping`:
|
||
|
||
```dart
|
||
@override
|
||
FieldMapping get fieldMapping => const FieldMapping({
|
||
'deviceId': 'origin',
|
||
'userId': 'asset',
|
||
// ... 其他字段
|
||
});
|
||
```
|
||
|
||
### Q: 响应 data 为空怎么办?
|
||
|
||
A: 检查以下几点:
|
||
1. `ApiClient.init()` 是否已调用
|
||
2. 网络是否正常
|
||
3. `appId`、`aesKey` 等配置是否正确
|
||
|
||
---
|
||
|
||
## 12. 文件结构
|
||
|
||
```
|
||
lib/
|
||
├── client_proxy_framework.dart # 入口文件
|
||
└── src/
|
||
├── api/
|
||
│ ├── api_client.dart # 全局客户端
|
||
│ ├── api_crypto.dart # 加解密工具
|
||
│ ├── api_response.dart # 响应对象
|
||
│ └── proxy_client.dart # 代理请求客户端(含实体转换)
|
||
├── config/
|
||
│ ├── app_config.dart # 应用配置抽象类
|
||
│ ├── default_field_mapping.dart # 默认字段映射
|
||
│ └── field_mapping.dart # 字段映射类
|
||
├── entities/
|
||
│ ├── entity.dart # 实体基类
|
||
│ ├── user_entities.dart # 用户相关实体
|
||
│ ├── payment_entities.dart # 支付相关实体
|
||
│ ├── image_entities.dart # 图片/视频实体
|
||
│ └── feedback_entities.dart # 反馈实体
|
||
├── log/
|
||
│ └── app_logger.dart # 日志工具
|
||
└── services/
|
||
├── user_api.dart # 用户 API
|
||
├── payment_api.dart # 支付 API
|
||
├── image_api.dart # 图片/视频 API
|
||
└── feedback_api.dart # 反馈 API
|
||
```
|