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, forcedRegion: ProviderRegion? = null,
): ProxyUpstreamResponse? = ): ProxyUpstreamResponse? =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val credential = credentialRepository.get(ServiceType.TRAE, accountId) val credential = pickCredential(accountId, forcedRegion)
?: return@withContext null ?: return@withContext null
if (credential !is Credential.TraeCredential) return@withContext null if (credential !is Credential.TraeCredential) return@withContext null
val jwt = credential.jwt.trim().takeIf { it.isNotBlank() } val jwt = credential.jwt.trim().takeIf { it.isNotBlank() }
@ -81,7 +81,7 @@ class TraeChatProxy @Inject constructor(
accountId: String? = null, accountId: String? = null,
forcedRegion: ProviderRegion? = null, forcedRegion: ProviderRegion? = null,
): ProxyUpstreamStream? = withContext(Dispatchers.IO) { ): ProxyUpstreamStream? = withContext(Dispatchers.IO) {
val credential = credentialRepository.get(ServiceType.TRAE, accountId) val credential = pickCredential(accountId, forcedRegion)
?: return@withContext null ?: return@withContext null
if (credential !is Credential.TraeCredential) return@withContext null if (credential !is Credential.TraeCredential) return@withContext null
val jwt = credential.jwt.trim().takeIf { it.isNotBlank() } 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 均不提供 * INTL 统一走 Web 网关 core-normal.trae.ai(实测 api-us-east/a0ai 均不提供
* llm_utils_chat 路由);CN 走 IDE 网关 trae-api-cn.mchost.guru。 * 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 = private fun chatBase(region: ProviderRegion?): String =
if (region == ProviderRegion.INTL) { if (region == ProviderRegion.INTL) {
"https://core-normal.trae.ai" "https://core-normal.trae.ai"

View File

@ -36,7 +36,7 @@ class WorkBuddyChatProxy @Inject constructor(
forcedRegion: ProviderRegion? = null, forcedRegion: ProviderRegion? = null,
): ProxyUpstreamResponse? = ): ProxyUpstreamResponse? =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val credential = credentialRepository.get(ServiceType.WORKBUDDY, accountId) val credential = pickCredential(accountId, forcedRegion)
?: return@withContext null ?: return@withContext null
if (credential !is Credential.WorkBuddyCredential) return@withContext null if (credential !is Credential.WorkBuddyCredential) return@withContext null
val accessToken = credential.accessToken.trim().takeIf { it.isNotBlank() } val accessToken = credential.accessToken.trim().takeIf { it.isNotBlank() }
@ -78,7 +78,7 @@ class WorkBuddyChatProxy @Inject constructor(
accountId: String? = null, accountId: String? = null,
forcedRegion: ProviderRegion? = null, forcedRegion: ProviderRegion? = null,
): ProxyUpstreamStream? = withContext(Dispatchers.IO) { ): ProxyUpstreamStream? = withContext(Dispatchers.IO) {
val credential = credentialRepository.get(ServiceType.WORKBUDDY, accountId) val credential = pickCredential(accountId, forcedRegion)
?: return@withContext null ?: return@withContext null
if (credential !is Credential.WorkBuddyCredential) return@withContext null if (credential !is Credential.WorkBuddyCredential) return@withContext null
val accessToken = credential.accessToken.trim().takeIf { it.isNotBlank() } val accessToken = credential.accessToken.trim().takeIf { it.isNotBlank() }
@ -120,6 +120,30 @@ class WorkBuddyChatProxy @Inject constructor(
return@withContext null 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( private fun buildChatRequest(
base: String, base: String,
path: String, path: String,

View File

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