加入失败日志

This commit is contained in:
zeng 2026-05-09 13:27:20 +08:00
parent 9dd9e9ebde
commit e2f63b8a16

View File

@ -27,6 +27,15 @@ class APUploadIAPListVC: NSViewController {
}
private var screenshotPaths = [String: String]()
private var uploadFailureSummaries: [String: UploadFailureSummary] = [:]
private var uploadFailureOrder: [String] = []
private struct UploadFailureSummary {
let productId: String
let productName: String
let productType: String
var reasons: [String]
}
override func viewDidLoad() {
super.viewDidLoad()
@ -88,6 +97,8 @@ class APUploadIAPListVC: NSViewController {
enterBtn.isEnabled = false
APHUD.show(message: "上传中", view: self.view)
uploadFailureSummaries.removeAll()
uploadFailureOrder.removeAll()
let uploadIAPs: ((AppStoreConnectKey) -> Void) = { [weak self] ascKey in
//
self?.updateInAppPurchse(iaps: list, appId: appid, ascKey: ascKey)
@ -169,12 +180,45 @@ extension APUploadIAPListVC {
}
}
self.outputUploadFailureSummary(ascAPI: ascAPI)
self.enterBtn.isEnabled = true
APHUD.hide()
ascAPI.addMessage("完成全部内购商品,可稍后在苹果后台查看!✅✅✅")
}
}
func recordUploadFailure(product: IAPProduct, reason: String) {
let key = product.productId
let typeName = product.inAppPurchaseType.CNValue()
if uploadFailureSummaries[key] == nil {
uploadFailureSummaries[key] = UploadFailureSummary(
productId: product.productId,
productName: product.name,
productType: typeName,
reasons: []
)
uploadFailureOrder.append(key)
}
guard var summary = uploadFailureSummaries[key] else { return }
if summary.reasons.contains(reason) == false {
summary.reasons.append(reason)
}
uploadFailureSummaries[key] = summary
}
func outputUploadFailureSummary(ascAPI: APASCAPI) {
guard uploadFailureOrder.isNotEmpty else { return }
ascAPI.addMessage("本次上传失败的支付项如下:")
for key in uploadFailureOrder {
guard let summary = uploadFailureSummaries[key] else { continue }
let reasonText = summary.reasons.joined(separator: "")
ascAPI.addMessage(
"支付项失败:\(summary.productId)\(summary.productName)\(summary.productType)\(reasonText)")
}
}
// MARK: -
///
@ -243,6 +287,7 @@ extension APUploadIAPListVC {
guard let iap = await ascAPI.createInAppPurchases(appId: appId, product: product) else {
//
ascAPI.addMessage("内购商品:\(product.productId) ,创建失败!❌ ")
recordUploadFailure(product: product, reason: "内购项创建失败")
return
}
@ -269,6 +314,7 @@ extension APUploadIAPListVC {
func updateIAPPricePoint(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
guard let schedule = product.priceSchedules else {
ascAPI.addMessage("无价格计划表:\(product.productId) ,请确认!❌ ")
recordUploadFailure(product: product, reason: "价格计划表不存在")
return
}
@ -311,6 +357,9 @@ extension APUploadIAPListVC {
index: included.count))
} else {
ascAPI.addMessage("自定价格的内购价格点:\(territory)\(customerPrice) ,未找到此档位!❌ ")
recordUploadFailure(
product: product,
reason: "自定价格未找到价格点:\(territory) / \(customerPrice)")
}
}
@ -325,10 +374,16 @@ extension APUploadIAPListVC {
} else {
//
ascAPI.addMessage("内购价格点:\(baseTerritory)\(baseCustomerPrice) ,更新价格失败!❌ ")
recordUploadFailure(
product: product,
reason: "价格档位配置失败:\(baseTerritory) / \(baseCustomerPrice)")
}
} else {
//
ascAPI.addMessage("基准国家的内购价格点:\(baseTerritory)\(baseCustomerPrice) ,未找到此档位!❌ ")
recordUploadFailure(
product: product,
reason: "基准国家价格点未找到:\(baseTerritory) / \(baseCustomerPrice)")
}
}
@ -345,6 +400,9 @@ extension APUploadIAPListVC {
} else {
//
ascAPI.addMessage("内购本地化版本:\(localization.locale) ,更新语言失败!❌ ")
recordUploadFailure(
product: product,
reason: "本地化创建失败:\(localization.locale)")
}
}
@ -354,6 +412,7 @@ extension APUploadIAPListVC {
let imgName = product.reviewScreenshot
guard let imgPath = screenshotPaths[imgName] else {
ascAPI.addMessage("内购商品:\(product.productId) 无送审截图或未上传截图~")
recordUploadFailure(product: product, reason: "未上传送审截图")
return
}
@ -361,6 +420,7 @@ extension APUploadIAPListVC {
let uploadFileName = imaUrl.lastPathComponent.isEmpty ? imgName : imaUrl.lastPathComponent
guard let fileMD5 = URL.init(fileURLWithPath: imgPath).fileMD5() else {
ascAPI.addMessage("内购商品截图文件错误:\(imgPath) ,无法生成 md5 值~")
recordUploadFailure(product: product, reason: "截图文件无法生成 MD5")
return
}
@ -371,6 +431,7 @@ extension APUploadIAPListVC {
let status = await ascAPI.deleteInAppPurchasesScreenshot(iapShotId: ost.id)
if status != 204 {
ascAPI.addMessage("内购商品截图创建失败:\(imgName) ,无法删除旧截图~")
recordUploadFailure(product: product, reason: "送审截图删除旧图失败")
}
}
@ -379,6 +440,7 @@ extension APUploadIAPListVC {
let imaSize = imaUrl.fileSizeInt()
guard imaSize > 0 else {
ascAPI.addMessage("内购商品截图文件错误:\(imgPath) ,文件大小为 0~")
recordUploadFailure(product: product, reason: "截图文件大小为 0")
return
}
guard
@ -387,6 +449,7 @@ extension APUploadIAPListVC {
else {
//
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!❌ ")
recordUploadFailure(product: product, reason: "送审截图创建失败")
return
}
@ -397,6 +460,7 @@ extension APUploadIAPListVC {
let baseURL = URL(string: url)
else {
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!苹果参数异常~ ❌ ")
recordUploadFailure(product: product, reason: "送审截图上传参数异常")
return
}
@ -411,12 +475,14 @@ extension APUploadIAPListVC {
guard let response = try? await URLSession.shared.upload(for: request, fromFile: imaUrl)
else {
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!上传图片异常~ ❌ ")
recordUploadFailure(product: product, reason: "送审截图上传异常")
return
}
guard let responseCode = (response.1 as? HTTPURLResponse)?.statusCode, responseCode == 200
else {
ascAPI.addMessage(
"内购商品:\(product.productId) ,创建送审截图失败!上传图片异常 \(response.1.description)~ ❌ ")
recordUploadFailure(product: product, reason: "送审截图上传响应异常")
return
}
@ -428,6 +494,7 @@ extension APUploadIAPListVC {
ascAPI.addMessage("内购商品:\(product.productId) ,送审截图上传成功!✅ ")
} else {
ascAPI.addMessage("内购商品:\(product.productId) ,送审截图可能上传失败! ")
recordUploadFailure(product: product, reason: "送审截图确认失败")
}
}
@ -456,9 +523,11 @@ extension APUploadIAPListVC {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新成功!✅ ")
} else {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新失败!❌ ")
recordUploadFailure(product: product, reason: "销售国家/地区更新失败")
}
} else {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),无法设置!获取国家标识码失败!❌ ")
recordUploadFailure(product: product, reason: "销售国家/地区列表获取失败")
}
return
}
@ -481,6 +550,7 @@ extension APUploadIAPListVC {
ascAPI.addMessage("内购商品的销售国家/地区:\(customerTerritory) ,更新成功!✅ ")
} else {
ascAPI.addMessage("内购商品的销售国家/地区:\(customerTerritory) ,更新失败!❌ ")
recordUploadFailure(product: product, reason: "销售国家/地区更新失败")
}
}
@ -514,18 +584,32 @@ extension APUploadIAPListVC {
subGroups.append(group)
for localization in product.localizations {
let _ = await ascAPI.createSubscriptionGroupLocalizations(
if await ascAPI.createSubscriptionGroupLocalizations(
iapGroupId: group.id, name: localization.name, locale: localization.locale,
customAppName: nil)
customAppName: nil) == nil
{
recordUploadFailure(
product: product,
reason: "订阅组本地化创建失败:\(localization.locale)")
}
}
} else if currentSubGroup == nil {
ascAPI.addMessage("订阅商品:\(product.productId) ,创建订阅组失败!❌ ")
recordUploadFailure(product: product, reason: "订阅组创建失败")
return
}
//
if let group = currentSubGroup {
if let localization = product.groupLocalization {
let _ = await ascAPI.createSubscriptionGroupLocalizations(
if await ascAPI.createSubscriptionGroupLocalizations(
iapGroupId: group.id, name: localization.name, locale: localization.locale,
customAppName: nil)
customAppName: nil) == nil
{
recordUploadFailure(
product: product,
reason: "订阅组本地化更新失败:\(localization.locale)")
}
}
}
@ -549,6 +633,7 @@ extension APUploadIAPListVC {
guard let iap = await ascAPI.updateSubscription(iapId: sub.id, product: product) else {
//
ascAPI.addMessage("订阅商品已经存在:\(product.productId) ,更新信息失败!❌ ")
recordUploadFailure(product: product, reason: "订阅项更新失败")
return
}
@ -571,6 +656,9 @@ extension APUploadIAPListVC {
} else {
//
ascAPI.addMessage("订阅商品本地化版本:\(localization.locale) ,更新语言失败!❌ ")
recordUploadFailure(
product: product,
reason: "订阅本地化更新失败:\(localization.locale)")
}
} else {
//
@ -603,6 +691,7 @@ extension APUploadIAPListVC {
else {
//
ascAPI.addMessage("订阅商品:\(product.productId) ,创建失败!❌ ")
recordUploadFailure(product: product, reason: "订阅项创建失败")
return
}
@ -675,6 +764,7 @@ extension APUploadIAPListVC {
}
} else {
ascAPI.addMessage("订阅商品:\(product.productId) ,提交审核失败!请检查商品是否为准备提交状态,或是否需要随 App 版本提交。❌ ")
recordUploadFailure(product: product, reason: "订阅提交审核失败")
}
}
@ -682,6 +772,7 @@ extension APUploadIAPListVC {
func updateSubscriptionPricePoint(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
guard let schedule = product.priceSchedules else {
ascAPI.addMessage("无价格计划表:\(product.productId) ,请确认!❌ ")
recordUploadFailure(product: product, reason: "价格计划表不存在")
return
}
@ -722,9 +813,15 @@ extension APUploadIAPListVC {
ascAPI.addMessage("自定价格的订阅商品的价格点:\(territory)\(customerPrice) ,更新价格成功!✅ ")
} else {
ascAPI.addMessage("自定价格的订阅商品的价格点:\(territory)\(customerPrice) ,更新价格失败!❌ ")
recordUploadFailure(
product: product,
reason: "自定价格更新失败:\(territory) / \(customerPrice)")
}
} else {
ascAPI.addMessage("自定价格的订阅商品价格点:\(territory)\(customerPrice) ,未找到此档位!❌ ")
recordUploadFailure(
product: product,
reason: "自定价格未找到价格点:\(territory) / \(customerPrice)")
}
}
@ -748,11 +845,17 @@ extension APUploadIAPListVC {
} else {
//
ascAPI.addMessage("全球均衡价格的订阅商品的价格点:\(territory)\(customerPrice) ,更新价格失败!❌ ")
recordUploadFailure(
product: product,
reason: "全球均衡价格更新失败:\(territory) / \(customerPrice)")
}
}
} else {
//
ascAPI.addMessage("基准国家的订阅商品价格点:\(baseTerritory)\(baseCustomerPrice) ,未找到此档位!❌ ")
recordUploadFailure(
product: product,
reason: "基准国家价格点未找到:\(baseTerritory) / \(baseCustomerPrice)")
}
}
@ -769,6 +872,9 @@ extension APUploadIAPListVC {
} else {
//
ascAPI.addMessage("订阅商品本地化版本:\(localization.locale) ,更新语言失败!❌ ")
recordUploadFailure(
product: product,
reason: "订阅本地化创建失败:\(localization.locale)")
}
}
@ -778,6 +884,7 @@ extension APUploadIAPListVC {
guard (await ascAPI.updateSubscription(iapId: iapId, product: product)) != nil else {
ascAPI.addMessage("订阅商品:\(product.productId) ,刷新基础元数据失败!❌ ")
recordUploadFailure(product: product, reason: "订阅元数据刷新失败")
return
}
@ -801,6 +908,9 @@ extension APUploadIAPListVC {
ascAPI.addMessage("订阅商品:\(product.productId) ,元数据状态刷新成功!✅ ")
} else {
ascAPI.addMessage("订阅商品:\(product.productId) ,刷新本地化元数据失败!❌ ")
recordUploadFailure(
product: product,
reason: "订阅本地化元数据刷新失败:\(localization.locale)")
}
}
@ -810,6 +920,7 @@ extension APUploadIAPListVC {
let imgName = product.reviewScreenshot
guard let imgPath = screenshotPaths[imgName] else {
ascAPI.addMessage("订阅商品:\(product.productId) 无送审截图或未上传截图~")
recordUploadFailure(product: product, reason: "未上传送审截图")
return
}
@ -817,6 +928,7 @@ extension APUploadIAPListVC {
let uploadFileName = imaUrl.lastPathComponent.isEmpty ? imgName : imaUrl.lastPathComponent
guard let fileMD5 = URL.init(fileURLWithPath: imgPath).fileMD5() else {
ascAPI.addMessage("订阅商品截图文件错误:\(imgPath) ,无法生成 md5 值~")
recordUploadFailure(product: product, reason: "截图文件无法生成 MD5")
return
}
@ -827,6 +939,7 @@ extension APUploadIAPListVC {
let status = await ascAPI.deleteSubscriptionScreenshot(iapShotId: ost.id)
if status != 204 {
ascAPI.addMessage("订阅商品截图创建失败:\(imgName) ,无法删除旧截图~")
recordUploadFailure(product: product, reason: "送审截图删除旧图失败")
}
}
@ -835,6 +948,7 @@ extension APUploadIAPListVC {
let imaSize = imaUrl.fileSizeInt()
guard imaSize > 0 else {
ascAPI.addMessage("订阅商品截图文件错误:\(imgPath) ,文件大小为 0~")
recordUploadFailure(product: product, reason: "截图文件大小为 0")
return
}
guard
@ -843,6 +957,7 @@ extension APUploadIAPListVC {
else {
//
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!❌ ")
recordUploadFailure(product: product, reason: "送审截图创建失败")
return
}
@ -853,6 +968,7 @@ extension APUploadIAPListVC {
let baseURL = URL(string: url)
else {
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!苹果参数异常~ ❌ ")
recordUploadFailure(product: product, reason: "送审截图上传参数异常")
return
}
@ -867,12 +983,14 @@ extension APUploadIAPListVC {
guard let response = try? await URLSession.shared.upload(for: request, fromFile: imaUrl)
else {
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!上传图片异常~ ❌ ")
recordUploadFailure(product: product, reason: "送审截图上传异常")
return
}
guard let responseCode = (response.1 as? HTTPURLResponse)?.statusCode, responseCode == 200
else {
ascAPI.addMessage(
"订阅商品:\(product.productId) ,创建送审截图失败!上传图片异常 \(response.1.description)~ ❌ ")
recordUploadFailure(product: product, reason: "送审截图上传响应异常")
return
}
@ -883,6 +1001,7 @@ extension APUploadIAPListVC {
ascAPI.addMessage("订阅商品:\(product.productId) ,送审截图上传成功!✅ ")
} else {
ascAPI.addMessage("订阅商品:\(product.productId) ,送审截图可能上传失败! ")
recordUploadFailure(product: product, reason: "送审截图确认失败")
}
}
@ -913,9 +1032,11 @@ extension APUploadIAPListVC {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新成功!✅ ")
} else {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新失败!❌ ")
recordUploadFailure(product: product, reason: "销售国家/地区更新失败")
}
} else {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),无法设置!获取国家标识码失败!❌ ")
recordUploadFailure(product: product, reason: "销售国家/地区列表获取失败")
}
return
}
@ -938,6 +1059,7 @@ extension APUploadIAPListVC {
ascAPI.addMessage("订阅商品的销售国家/地区:\(customerTerritory) ,更新成功!✅ ")
} else {
ascAPI.addMessage("订阅商品的销售国家/地区:\(customerTerritory) ,更新失败!❌ ")
recordUploadFailure(product: product, reason: "销售国家/地区更新失败")
}
}
}