修复素材上传 OSS 路径并完善调试与表单交互。
对齐 Android material/{scenicId}/0/ 目录结构以修复视频元数据写入失败,补充 API 请求体调试日志、素材拍摄麦克风权限与上传控件点击穿透修复。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -340,12 +340,30 @@ final class APIClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// 在 Debug 环境打印请求信息。
|
||||
/// 在 Debug 环境打印请求信息(含 GET 查询参数与 POST 请求体,对齐 Android Ktor `LogLevel.BODY`)。
|
||||
private func logRequest(_ request: URLRequest) {
|
||||
#if DEBUG
|
||||
let method = request.httpMethod ?? "REQUEST"
|
||||
let url = request.url?.absoluteString ?? "<invalid url>"
|
||||
print("[API][Request] \(method) \(url)")
|
||||
var lines = ["[API][Request] \(method) \(url)"]
|
||||
|
||||
if let queryItems = URLComponents(url: request.url ?? URL(fileURLWithPath: "/"), resolvingAgainstBaseURL: false)?.queryItems,
|
||||
!queryItems.isEmpty {
|
||||
let params = queryItems
|
||||
.map { item in
|
||||
let value = item.value ?? ""
|
||||
return "\(item.name)=\(value)"
|
||||
}
|
||||
.joined(separator: "&")
|
||||
lines.append("params: \(params)")
|
||||
}
|
||||
|
||||
let contentType = request.value(forHTTPHeaderField: "Content-Type")
|
||||
if let body = Self.debugRequestBody(from: request.httpBody, contentType: contentType) {
|
||||
lines.append("body:\n\(body)")
|
||||
}
|
||||
|
||||
print(lines.joined(separator: "\n"))
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -370,6 +388,28 @@ final class APIClient {
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// 将请求体格式化为便于调试阅读的字符串。
|
||||
private static func debugRequestBody(from data: Data?, contentType: String?) -> String? {
|
||||
guard let data, !data.isEmpty else { return nil }
|
||||
|
||||
if let contentType, contentType.lowercased().contains("multipart/form-data") {
|
||||
return "<multipart/form-data: \(data.count) bytes>"
|
||||
}
|
||||
|
||||
if
|
||||
let object = try? JSONSerialization.jsonObject(with: data),
|
||||
let prettyData = try? JSONSerialization.data(withJSONObject: object, options: [.prettyPrinted, .sortedKeys]),
|
||||
let prettyJSON = String(data: prettyData, encoding: .utf8) {
|
||||
return prettyJSON
|
||||
}
|
||||
|
||||
if let text = String(data: data, encoding: .utf8), !text.isEmpty {
|
||||
return text.count > 2_000 ? String(text.prefix(2_000)) + "..." : text
|
||||
}
|
||||
|
||||
return "<non-utf8 body: \(data.count) bytes>"
|
||||
}
|
||||
|
||||
/// 将响应体格式化为便于调试阅读的字符串。
|
||||
private static func debugResponseBody(from data: Data) -> String {
|
||||
guard !data.isEmpty else { return "<empty response>" }
|
||||
|
||||
@ -314,7 +314,8 @@ enum OSSUploadPolicy {
|
||||
case "sample":
|
||||
return "sample/\(currentDate)/\(scenicId)/\(uploadId)_\(safeName)"
|
||||
case "material":
|
||||
return "material/\(currentDate)/\(scenicId)/\(uploadId)_\(safeName)"
|
||||
// Android 素材任务固定 folderId = 0,后端会按此层级解析对象信息并写入视频元数据。
|
||||
return "material/\(scenicId)/0/\(uploadId)_\(safeName)"
|
||||
case "punch_point":
|
||||
let userId = AppStore.shared.session.userId.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let ownerId = userId.isEmpty ? String(scenicId) : userId
|
||||
|
||||
Reference in New Issue
Block a user