first commit for e-party

This commit is contained in:
edwinQQQ
2025-07-07 14:19:07 +08:00
parent 007c10daaf
commit 5926906f3c
14 changed files with 1248 additions and 201 deletions

626
API-README.md Normal file
View File

@@ -0,0 +1,626 @@
# Yana iOS API 使用指南
## 📋 目录
- [架构概览](#架构概览)
- [快速开始](#快速开始)
- [环境配置](#环境配置)
- [请求方式](#请求方式)
- [错误处理](#错误处理)
- [安全机制](#安全机制)
- [最佳实践](#最佳实践)
- [API接口列表](#api接口列表)
- [示例代码](#示例代码)
## 🏗️ 架构概览
Yana iOS 项目采用基于 **TCA (The Composable Architecture)** 的现代化 API 架构设计:
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ SwiftUI View │───▶│ TCA Reducer │───▶│ API Service │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ App State │ │ Network Layer │
└─────────────────┘ └─────────────────┘
```
### 核心组件
- **APIService**: 网络请求核心服务
- **APIModels**: 数据模型和协议定义
- **APIEndpoints**: API 端点配置
- **APILogger**: 请求日志记录
- **BaseRequest**: 基础请求参数管理
## 🚀 快速开始
### 1. 基本使用
```swift
import SwiftUI
struct ContentView: View {
@State private var isLoading = false
@State private var result = ""
var body: some View {
VStack {
Button("发起 API 请求") {
Task {
await makeAPIRequest()
}
}
.disabled(isLoading)
if isLoading {
ProgressView("请求中...")
}
Text(result)
}
}
private func makeAPIRequest() async {
isLoading = true
do {
// 创建 API 服务实例
let apiService = LiveAPIService()
// 创建请求
let request = ConfigRequest()
// 发起请求
let response = try await apiService.request(request)
await MainActor.run {
result = "请求成功: \(response)"
isLoading = false
}
} catch {
await MainActor.run {
result = "请求失败: \(error.localizedDescription)"
isLoading = false
}
}
}
}
```
### 2. TCA 集成使用
```swift
import ComposableArchitecture
@Reducer
struct APIFeature {
@ObservableState
struct State: Equatable {
var data: ConfigResponse?
var isLoading = false
var errorMessage: String?
}
enum Action: Equatable {
case loadConfig
case configLoaded(ConfigResponse)
case loadingFailed(String)
}
@Dependency(\.apiService) var apiService
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .loadConfig:
state.isLoading = true
state.errorMessage = nil
return .run { send in
do {
let request = ConfigRequest()
let response = try await apiService.request(request)
await send(.configLoaded(response))
} catch {
await send(.loadingFailed(error.localizedDescription))
}
}
case let .configLoaded(response):
state.isLoading = false
state.data = response
return .none
case let .loadingFailed(error):
state.isLoading = false
state.errorMessage = error
return .none
}
}
}
}
```
## ⚙️ 环境配置
### 服务器环境
| 环境 | 地址 | 说明 |
|------|------|------|
| 测试环境 | `http://beta.api.molistar.xyz` | 开发测试服务器 |
| 生产环境 | `https://api.hfighting.com` | 正式服务器 |
### 配置参数
```swift
struct APIConfiguration {
static let baseURL = "http://beta.api.molistar.xyz"
static let timeout: TimeInterval = 30.0
static let maxDataSize: Int = 50 * 1024 * 1024 // 50MB
}
```
### 默认请求头
所有请求都会自动添加以下请求头:
```swift
static var defaultHeaders: [String: String] {
return [
"Content-Type": "application/json",
"Accept": "application/json",
"Accept-Encoding": "gzip, br",
"Accept-Language": Locale.current.languageCode ?? "en",
"App-Version": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0"
]
}
```
## 📡 请求方式
### 1. GET 请求
```swift
struct ConfigRequest: APIRequestProtocol {
typealias Response = ConfigResponse
let endpoint = "/client/config"
let method: HTTPMethod = .GET
let includeBaseParameters = true
let queryParameters: [String: String]? = nil
let bodyParameters: [String: Any]? = nil
let headers: [String: String]? = nil
let timeout: TimeInterval = 30.0
}
```
### 2. POST 请求
```swift
struct LoginRequest: APIRequestProtocol {
typealias Response = LoginResponse
let endpoint = "/auth/login"
let method: HTTPMethod = .POST
let includeBaseParameters = true
let queryParameters: [String: String]? = nil
let bodyParameters: [String: Any]?
let headers: [String: String]? = nil
let timeout: TimeInterval = 30.0
init(username: String, password: String) {
self.bodyParameters = [
"username": username,
"password": password
]
}
}
```
### 3. 基础参数说明
每个请求都会自动包含以下基础参数:
```swift
struct BaseRequest {
let acceptLanguage: String // 用户语言偏好
let os: String = "iOS" // 操作系统类型
let osVersion: String // 系统版本号
let netType: Int // 网络类型 (WiFi=2, 蜂窝=1)
let ispType: String // 运营商类型
let channel: String // 应用分发渠道
let model: String // 设备型号
let deviceId: String // 设备唯一标识
let appVersion: String // 应用版本
let app: String // 应用名称
let lang: String // 语言代码
let mcc: String? // 移动国家代码
let pubSign: String // 安全签名
}
```
## ❌ 错误处理
### 错误类型
```swift
enum APIError: Error, Equatable {
case invalidURL // 无效的 URL
case noData // 没有收到数据
case decodingError(String) // 数据解析失败
case networkError(String) // 网络错误
case httpError(statusCode: Int, message: String?) // HTTP 错误
case timeout // 请求超时
case resourceTooLarge // 响应数据过大
case unknown(String) // 未知错误
}
```
### 错误处理示例
```swift
do {
let response = try await apiService.request(request)
// 处理成功响应
} catch let apiError as APIError {
switch apiError {
case .networkError(let message):
print("网络错误: \(message)")
case .timeout:
print("请求超时,请检查网络连接")
case .httpError(let statusCode, let message):
print("服务器错误 \(statusCode): \(message ?? "未知错误")")
case .decodingError(let message):
print("数据解析失败: \(message)")
default:
print("其他错误: \(apiError.localizedDescription)")
}
} catch {
print("未知错误: \(error)")
}
```
## 🔐 安全机制
### 签名生成流程
1. **参数过滤**: 移除系统级参数
2. **参数排序**: 按字典 key 升序排序
3. **字符串拼接**: `"key0=value0&key1=value1"`
4. **添加密钥**: 拼接 `key=rpbs6us1m8r2j9g6u06ff2bo18orwaya`
5. **MD5加密**: 生成大写 MD5 签名
### 认证头部
```swift
// 用户认证相关头部(如果用户已登录)
if let userId = UserInfoManager.getCurrentUserId() {
headers["pub_uid"] = userId
}
if let userTicket = UserInfoManager.getCurrentUserTicket() {
headers["pub_ticket"] = userTicket
}
```
## 💡 最佳实践
### 1. 错误处理
```swift
// ✅ 推荐:完整的错误处理
do {
let response = try await apiService.request(request)
// 处理成功响应
} catch let urlError as URLError {
switch urlError.code {
case .notConnectedToInternet:
showAlert("网络不可用,请检查网络连接")
case .timedOut:
showAlert("请求超时,请重试")
default:
showAlert("网络错误: \(urlError.localizedDescription)")
}
} catch let apiError as APIError {
showAlert(apiError.localizedDescription)
} catch {
showAlert("未知错误: \(error)")
}
```
### 2. 主线程更新 UI
```swift
// ✅ 推荐:使用 MainActor 更新 UI
await MainActor.run {
self.isLoading = false
self.data = response
}
```
### 3. 取消请求
```swift
// ✅ 推荐:支持取消的请求
struct ContentView: View {
@State private var task: Task<Void, Never>?
private func makeRequest() {
task = Task {
do {
let response = try await apiService.request(request)
// 处理响应
} catch {
if !Task.isCancelled {
// 处理错误
}
}
}
}
private func cancelRequest() {
task?.cancel()
}
}
```
### 4. 重试机制
```swift
// ✅ 推荐:实现重试逻辑
func requestWithRetry<T: APIRequestProtocol>(
_ request: T,
maxRetries: Int = 3
) async throws -> T.Response {
var lastError: Error?
for attempt in 0..<maxRetries {
do {
return try await apiService.request(request)
} catch {
lastError = error
if attempt < maxRetries - 1 {
try await Task.sleep(nanoseconds: UInt64(pow(2.0, Double(attempt))) * 1_000_000_000)
}
}
}
throw lastError!
}
```
## 📋 API接口列表
### 当前可用接口
| 接口名称 | 端点 | 方法 | 说明 |
|---------|------|------|------|
| 配置获取 | `/client/config` | GET | 获取客户端配置信息 |
| 初始化配置 | `/client/init` | GET | 获取初始化配置 |
| 用户登录 | `/auth/login` | POST | 用户登录认证 |
### 接口扩展
要添加新的 API 接口,请按以下步骤操作:
1. **在 `APIEndpoints.swift` 中添加端点**:
```swift
enum APIEndpoint: String, CaseIterable {
case newEndpoint = "/new/endpoint"
// ...
}
```
2. **创建请求模型**:
```swift
struct NewRequest: APIRequestProtocol {
typealias Response = NewResponse
let endpoint = "/new/endpoint"
let method: HTTPMethod = .POST
let includeBaseParameters = true
// ... 其他属性
}
```
3. **创建响应模型**:
```swift
struct NewResponse: Codable {
let success: Bool
let data: SomeDataModel?
let message: String?
}
```
## 📝 示例代码
### 完整的登录功能示例
```swift
import SwiftUI
import ComposableArchitecture
// MARK: - Login Feature
@Reducer
struct LoginFeature {
@ObservableState
struct State: Equatable {
var username = ""
var password = ""
var isLoading = false
var isLoggedIn = false
var errorMessage: String?
}
enum Action: Equatable {
case usernameChanged(String)
case passwordChanged(String)
case loginButtonTapped
case loginResponse(Result<LoginResponse, APIError>)
}
@Dependency(\.apiService) var apiService
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case let .usernameChanged(username):
state.username = username
return .none
case let .passwordChanged(password):
state.password = password
return .none
case .loginButtonTapped:
state.isLoading = true
state.errorMessage = nil
let request = LoginRequest(
username: state.username,
password: state.password
)
return .run { send in
do {
let response = try await apiService.request(request)
await send(.loginResponse(.success(response)))
} catch let error as APIError {
await send(.loginResponse(.failure(error)))
} catch {
await send(.loginResponse(.failure(.unknown(error.localizedDescription))))
}
}
case let .loginResponse(.success(response)):
state.isLoading = false
state.isLoggedIn = response.success
if !response.success {
state.errorMessage = response.message
}
return .none
case let .loginResponse(.failure(error)):
state.isLoading = false
state.errorMessage = error.localizedDescription
return .none
}
}
}
}
// MARK: - Login View
struct LoginView: View {
let store: StoreOf<LoginFeature>
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
VStack(spacing: 20) {
TextField("用户名", text: viewStore.binding(
get: \.username,
send: LoginFeature.Action.usernameChanged
))
.textFieldStyle(RoundedBorderTextFieldStyle())
SecureField("密码", text: viewStore.binding(
get: \.password,
send: LoginFeature.Action.passwordChanged
))
.textFieldStyle(RoundedBorderTextFieldStyle())
Button("登录") {
viewStore.send(.loginButtonTapped)
}
.disabled(viewStore.isLoading || viewStore.username.isEmpty || viewStore.password.isEmpty)
if viewStore.isLoading {
ProgressView("登录中...")
}
if let errorMessage = viewStore.errorMessage {
Text(errorMessage)
.foregroundColor(.red)
}
}
.padding()
}
}
}
// MARK: - Request & Response Models
struct LoginRequest: APIRequestProtocol {
typealias Response = LoginResponse
let endpoint = "/auth/login"
let method: HTTPMethod = .POST
let includeBaseParameters = true
let queryParameters: [String: String]? = nil
let bodyParameters: [String: Any]?
let headers: [String: String]? = nil
let timeout: TimeInterval = 30.0
init(username: String, password: String) {
self.bodyParameters = [
"username": username,
"password": password
]
}
}
struct LoginResponse: Codable {
let success: Bool
let message: String?
let data: UserData?
}
struct UserData: Codable {
let userId: String
let username: String
let token: String
}
```
## 🔧 调试和日志
### 启用详细日志
API 请求和响应会自动记录到控制台,包括:
- 请求 URL 和参数
- 请求头信息
- 响应状态码和数据
- 请求耗时
- 错误信息
### 日志示例
```
🚀 API Request: GET /client/config
📋 Headers: ["Content-Type": "application/json", "Accept": "application/json"]
📊 Query Parameters: ["deviceId": "ABC123", "appVersion": "1.0.0"]
✅ API Response: 200 OK (0.45s)
📦 Response Size: 1.2KB
📄 Response Data: {"success": true, "data": {...}}
```
## 📚 相关文档
- [API 规则详解](yana/APIs/API%20rule.md)
- [集成指南](yana/APIs/Integration-Guide.md)
- [TCA 官方文档](https://github.com/pointfreeco/swift-composable-architecture)
## 🤝 贡献指南
1. 遵循现有的代码风格和架构模式
2. 为新的 API 接口添加完整的文档和示例
3. 确保所有请求都包含适当的错误处理
4. 添加单元测试覆盖新功能
5. 更新相关文档
---
**注意**: 本文档基于当前项目架构编写,如有架构变更请及时更新文档内容。