
- 修改COSManagerAdapter以支持新的TCCos组件,确保与腾讯云COS的兼容性。 - 在CreateFeedFeature中新增图片上传相关状态和Action,优化图片选择与上传逻辑。 - 更新CreateFeedView以整合图片上传功能,提升用户体验。 - 在多个视图中添加键盘状态管理,改善用户交互体验。 - 新增COS相关的测试文件,确保功能的正确性和稳定性。
218 lines
6.4 KiB
Swift
218 lines
6.4 KiB
Swift
import Foundation
|
||
import ComposableArchitecture
|
||
|
||
// MARK: - Token 服务协议
|
||
|
||
/// Token 服务协议
|
||
public protocol COSTokenServiceProtocol: Sendable {
|
||
/// 获取有效的Token
|
||
func getValidToken() async throws -> TcTokenData
|
||
|
||
/// 刷新Token
|
||
func refreshToken() async throws -> TcTokenData
|
||
|
||
/// 清除缓存的Token
|
||
func clearCachedToken()
|
||
|
||
/// 获取Token状态信息
|
||
func getTokenStatus() async -> String
|
||
}
|
||
|
||
// MARK: - Token 服务实现
|
||
|
||
/// Token 服务实现
|
||
public struct COSTokenService: COSTokenServiceProtocol {
|
||
private let apiService: any APIServiceProtocol & Sendable
|
||
private let tokenCache: TokenCacheProtocol
|
||
|
||
init(
|
||
apiService: any APIServiceProtocol & Sendable,
|
||
tokenCache: TokenCacheProtocol = TokenCache()
|
||
) {
|
||
self.apiService = apiService
|
||
self.tokenCache = tokenCache
|
||
}
|
||
|
||
/// 获取有效的Token
|
||
public func getValidToken() async throws -> TcTokenData {
|
||
// 首先检查缓存
|
||
if let cachedToken = await tokenCache.getValidCachedToken() {
|
||
debugInfoSync("🔐 使用缓存的 COS Token")
|
||
return cachedToken
|
||
}
|
||
|
||
// 缓存无效,请求新Token
|
||
debugInfoSync("🔐 开始请求腾讯云 COS Token...")
|
||
return try await requestNewToken()
|
||
}
|
||
|
||
/// 刷新Token
|
||
public func refreshToken() async throws -> TcTokenData {
|
||
// 清除缓存
|
||
await tokenCache.clearCachedToken()
|
||
debugInfoSync("🔄 清除缓存,开始刷新 Token")
|
||
|
||
// 请求新Token
|
||
return try await requestNewToken()
|
||
}
|
||
|
||
/// 清除缓存的Token
|
||
public func clearCachedToken() {
|
||
Task {
|
||
await tokenCache.clearCachedToken()
|
||
debugInfoSync("🗑️ 清除缓存的 COS Token")
|
||
}
|
||
}
|
||
|
||
/// 获取Token状态信息
|
||
public func getTokenStatus() async -> String {
|
||
if let cachedToken = await tokenCache.getCachedToken() {
|
||
let isExpired = !cachedToken.isValid
|
||
return "Token 状态: \(isExpired ? "已过期" : "有效"), 过期时间: \(cachedToken.expirationDate)"
|
||
} else {
|
||
return "Token 状态: 未缓存"
|
||
}
|
||
}
|
||
|
||
// MARK: - 私有方法
|
||
|
||
/// 请求新Token
|
||
private func requestNewToken() async throws -> TcTokenData {
|
||
do {
|
||
let request = TcTokenRequest()
|
||
let response: TcTokenResponse = try await apiService.request(request)
|
||
|
||
guard response.code == 200, let tokenData = response.data else {
|
||
throw COSError.tokenInvalid
|
||
}
|
||
|
||
// 缓存Token
|
||
await tokenCache.cacheToken(tokenData)
|
||
|
||
debugInfoSync("✅ COS Token 获取成功")
|
||
debugInfoSync(" - 存储桶: \(tokenData.bucket)")
|
||
debugInfoSync(" - 地域: \(tokenData.region)")
|
||
debugInfoSync(" - 过期时间: \(tokenData.expirationDate)")
|
||
debugInfoSync(" - 剩余时间: \(tokenData.remainingTime)秒")
|
||
|
||
return tokenData
|
||
|
||
} catch {
|
||
debugErrorSync("❌ COS Token 请求异常: \(error.localizedDescription)")
|
||
throw COSError.networkError(error.localizedDescription)
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - Token 缓存协议
|
||
|
||
/// Token 缓存协议
|
||
public protocol TokenCacheProtocol: Sendable {
|
||
/// 获取缓存的Token
|
||
func getCachedToken() async -> TcTokenData?
|
||
|
||
/// 获取有效的缓存Token
|
||
func getValidCachedToken() async -> TcTokenData?
|
||
|
||
/// 缓存Token
|
||
func cacheToken(_ tokenData: TcTokenData) async
|
||
|
||
/// 清除缓存的Token
|
||
func clearCachedToken() async
|
||
}
|
||
|
||
// MARK: - Token 缓存实现
|
||
|
||
/// Token 缓存实现
|
||
public actor TokenCache: TokenCacheProtocol {
|
||
private var cachedToken: TcTokenData?
|
||
private var tokenExpirationDate: Date?
|
||
|
||
public init() {}
|
||
|
||
/// 获取缓存的Token
|
||
public func getCachedToken() async -> TcTokenData? {
|
||
return cachedToken
|
||
}
|
||
|
||
/// 获取有效的缓存Token
|
||
public func getValidCachedToken() async -> TcTokenData? {
|
||
guard let cached = cachedToken,
|
||
let expiration = tokenExpirationDate,
|
||
Date() < expiration else {
|
||
return nil
|
||
}
|
||
return cached
|
||
}
|
||
|
||
/// 缓存Token
|
||
public func cacheToken(_ tokenData: TcTokenData) async {
|
||
cachedToken = tokenData
|
||
tokenExpirationDate = tokenData.expirationDate
|
||
|
||
debugInfoSync("💾 COS Token 已缓存,过期时间: \(tokenExpirationDate?.description ?? "未知")")
|
||
}
|
||
|
||
/// 清除缓存的Token
|
||
public func clearCachedToken() async {
|
||
cachedToken = nil
|
||
tokenExpirationDate = nil
|
||
debugInfoSync("🗑️ 清除缓存的 COS Token")
|
||
}
|
||
}
|
||
|
||
// MARK: - TCA 依赖扩展
|
||
|
||
extension DependencyValues {
|
||
/// COS Token 服务依赖
|
||
public var cosTokenService: COSTokenServiceProtocol {
|
||
get { self[COSTokenServiceKey.self] }
|
||
set { self[COSTokenServiceKey.self] = newValue }
|
||
}
|
||
}
|
||
|
||
/// COS Token 服务依赖键
|
||
private enum COSTokenServiceKey: DependencyKey {
|
||
static let liveValue: COSTokenServiceProtocol = COSTokenService(
|
||
apiService: LiveAPIService()
|
||
)
|
||
|
||
static let testValue: COSTokenServiceProtocol = MockCOSTokenService()
|
||
}
|
||
|
||
// MARK: - Mock 服务(用于测试)
|
||
|
||
/// Mock Token 服务(用于测试)
|
||
public struct MockCOSTokenService: COSTokenServiceProtocol {
|
||
public var getValidTokenResult: Result<TcTokenData, Error> = .failure(COSError.tokenInvalid)
|
||
public var refreshTokenResult: Result<TcTokenData, Error> = .failure(COSError.tokenInvalid)
|
||
public var tokenStatusResult: String = "Mock Token Status"
|
||
|
||
public init() {}
|
||
|
||
public func getValidToken() async throws -> TcTokenData {
|
||
switch getValidTokenResult {
|
||
case .success(let token):
|
||
return token
|
||
case .failure(let error):
|
||
throw error
|
||
}
|
||
}
|
||
|
||
public func refreshToken() async throws -> TcTokenData {
|
||
switch refreshTokenResult {
|
||
case .success(let token):
|
||
return token
|
||
case .failure(let error):
|
||
throw error
|
||
}
|
||
}
|
||
|
||
public func clearCachedToken() {
|
||
// Mock 实现,无需实际操作
|
||
}
|
||
|
||
public func getTokenStatus() async -> String {
|
||
return tokenStatusResult
|
||
}
|
||
} |