fix(proxy): 测试按区域选凭据不再误用默认账号;签到成功/已签即时落本地记录
Some checks are pending
Release / build (push) Waiting to run

This commit is contained in:
Liuxinyu176 2026-10-10 11:39:28 +08:00
parent 4ac90449e5
commit 063658a90d
3 changed files with 109 additions and 25 deletions

View File

@ -46,7 +46,7 @@ class TraeChatProxy @Inject constructor(
forcedRegion: ProviderRegion? = null,
): ProxyUpstreamResponse? =
withContext(Dispatchers.IO) {
val credential = credentialRepository.get(ServiceType.TRAE, accountId)
val credential = pickCredential(accountId, forcedRegion)
?: return@withContext null
if (credential !is Credential.TraeCredential) return@withContext null
val jwt = credential.jwt.trim().takeIf { it.isNotBlank() }
@ -81,7 +81,7 @@ class TraeChatProxy @Inject constructor(
accountId: String? = null,
forcedRegion: ProviderRegion? = null,
): ProxyUpstreamStream? = withContext(Dispatchers.IO) {
val credential = credentialRepository.get(ServiceType.TRAE, accountId)
val credential = pickCredential(accountId, forcedRegion)
?: return@withContext null
if (credential !is Credential.TraeCredential) return@withContext null
val jwt = credential.jwt.trim().takeIf { it.isNotBlank() }
@ -233,6 +233,30 @@ class TraeChatProxy @Inject constructor(
* INTL 统一走 Web 网关 core-normal.trae.ai(实测 api-us-east/a0ai 均不提供
* llm_utils_chat 路由);CN 走 IDE 网关 trae-api-cn.mchost.guru。
*/
/** 按区域选凭据:显式 accountId 优先;未指定时优先同区域账号,再兼容默认账号。 */
private suspend fun pickCredential(
accountId: String?,
region: ProviderRegion?,
): Credential.TraeCredential? {
if (!accountId.isNullOrBlank()) {
return credentialRepository.get(ServiceType.TRAE, accountId) as? Credential.TraeCredential
}
val accounts = credentialRepository.accountsFor(ServiceType.TRAE)
if (accounts.isEmpty()) {
val legacy = credentialRepository.get(ServiceType.TRAE, null) as? Credential.TraeCredential
?: return null
return if (region == null || legacy.region.equals(region.name, ignoreCase = true)) legacy else null
}
val all = accounts.mapNotNull { account ->
credentialRepository.get(ServiceType.TRAE, account.id) as? Credential.TraeCredential
}
if (all.isEmpty()) return null
if (region != null) {
all.firstOrNull { it.region.equals(region.name, ignoreCase = true) }?.let { return it }
}
return all.first()
}
private fun chatBase(region: ProviderRegion?): String =
if (region == ProviderRegion.INTL) {
"https://core-normal.trae.ai"

View File

@ -36,7 +36,7 @@ class WorkBuddyChatProxy @Inject constructor(
forcedRegion: ProviderRegion? = null,
): ProxyUpstreamResponse? =
withContext(Dispatchers.IO) {
val credential = credentialRepository.get(ServiceType.WORKBUDDY, accountId)
val credential = pickCredential(accountId, forcedRegion)
?: return@withContext null
if (credential !is Credential.WorkBuddyCredential) return@withContext null
val accessToken = credential.accessToken.trim().takeIf { it.isNotBlank() }
@ -78,7 +78,7 @@ class WorkBuddyChatProxy @Inject constructor(
accountId: String? = null,
forcedRegion: ProviderRegion? = null,
): ProxyUpstreamStream? = withContext(Dispatchers.IO) {
val credential = credentialRepository.get(ServiceType.WORKBUDDY, accountId)
val credential = pickCredential(accountId, forcedRegion)
?: return@withContext null
if (credential !is Credential.WorkBuddyCredential) return@withContext null
val accessToken = credential.accessToken.trim().takeIf { it.isNotBlank() }
@ -120,6 +120,30 @@ class WorkBuddyChatProxy @Inject constructor(
return@withContext null
}
/** 按区域选凭据:显式 accountId 优先;未指定时优先同区域账号,再兼容单账号旧档。 */
private suspend fun pickCredential(
accountId: String?,
region: ProviderRegion?,
): Credential.WorkBuddyCredential? {
if (!accountId.isNullOrBlank()) {
return credentialRepository.get(ServiceType.WORKBUDDY, accountId) as? Credential.WorkBuddyCredential
}
val accounts = credentialRepository.accountsFor(ServiceType.WORKBUDDY)
if (accounts.isEmpty()) {
val legacy = credentialRepository.get(ServiceType.WORKBUDDY, null) as? Credential.WorkBuddyCredential
?: return null
return if (region == null || legacy.region.equals(region.name, ignoreCase = true)) legacy else null
}
val all = accounts.mapNotNull { account ->
credentialRepository.get(ServiceType.WORKBUDDY, account.id) as? Credential.WorkBuddyCredential
}
if (all.isEmpty()) return null
if (region != null) {
all.firstOrNull { it.region.equals(region.name, ignoreCase = true) }?.let { return it }
}
return all.first()
}
private fun buildChatRequest(
base: String,
path: String,

View File

@ -166,17 +166,32 @@ class AutoCheckinManager @Inject constructor(
ServiceType.TRAE -> refreshBalanceUseCase.checkInTrae(account.id).fold(
onSuccess = { r ->
when (r.outcome) {
TraeRepository.CheckinOutcome.SUCCESS -> ServiceResult(
service = service,
accountLabel = label,
newlyCheckedIn = true,
checkedInToday = true,
creditsGained = r.points,
message = "",
failed = false
)
TraeRepository.CheckinOutcome.ALREADY ->
TraeRepository.CheckinOutcome.SUCCESS -> {
checkinStatusStore.record(
ServiceType.TRAE,
account.id,
CheckinStatusStore.STATUS_SUCCESS,
checkinId = java.util.UUID.randomUUID().toString(),
credits = r.points
)
ServiceResult(
service = service,
accountLabel = label,
newlyCheckedIn = true,
checkedInToday = true,
creditsGained = r.points,
message = "",
failed = false
)
}
TraeRepository.CheckinOutcome.ALREADY -> {
checkinStatusStore.record(
ServiceType.TRAE,
account.id,
CheckinStatusStore.STATUS_ALREADY
)
ServiceResult(service, label, false, true, 0, "", false)
}
// INTL 不支持签到:静默跳过,不进弹窗(避免误报「已签到」)
TraeRepository.CheckinOutcome.INTL_UNSUPPORTED -> null
TraeRepository.CheckinOutcome.DEVICE_BLOCKED -> {
@ -201,19 +216,40 @@ class AutoCheckinManager @Inject constructor(
ServiceType.WORKBUDDY -> refreshBalanceUseCase.checkInWorkBuddy(account.id).fold(
onSuccess = { s ->
when (s.outcome) {
WorkBuddyRepository.CheckinOutcome.SUCCESS -> ServiceResult(
service = service,
accountLabel = label,
newlyCheckedIn = true,
checkedInToday = true,
creditsGained = (s.todayCredit.takeIf { it > 0 } ?: s.dailyCredit),
message = "",
failed = false
)
WorkBuddyRepository.CheckinOutcome.ALREADY ->
WorkBuddyRepository.CheckinOutcome.SUCCESS -> {
checkinStatusStore.record(
ServiceType.WORKBUDDY,
account.id,
CheckinStatusStore.STATUS_SUCCESS,
checkinId = java.util.UUID.randomUUID().toString(),
credits = (s.todayCredit.takeIf { it > 0 } ?: s.dailyCredit)
)
ServiceResult(
service = service,
accountLabel = label,
newlyCheckedIn = true,
checkedInToday = true,
creditsGained = (s.todayCredit.takeIf { it > 0 } ?: s.dailyCredit),
message = "",
failed = false
)
}
WorkBuddyRepository.CheckinOutcome.ALREADY -> {
checkinStatusStore.record(
ServiceType.WORKBUDDY,
account.id,
CheckinStatusStore.STATUS_ALREADY
)
ServiceResult(service, label, false, true, 0, "", false)
WorkBuddyRepository.CheckinOutcome.QUERY ->
}
WorkBuddyRepository.CheckinOutcome.QUERY -> {
checkinStatusStore.record(
ServiceType.WORKBUDDY,
account.id,
CheckinStatusStore.STATUS_ALREADY
)
ServiceResult(service, label, false, true, 0, "", false)
}
WorkBuddyRepository.CheckinOutcome.NO_CAMPAIGN ->
// 国际站没有签到活动不是账号失败:静默跳过,不记失败日志
ServiceResult(service, label, false, false, 0, s.message, false)