diff --git a/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/AddVirtualRemoteKey.kt b/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/AddVirtualRemoteKey.kt index 26cb835..9e5d46d 100644 --- a/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/AddVirtualRemoteKey.kt +++ b/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/AddVirtualRemoteKey.kt @@ -25,8 +25,11 @@ class AddVirtualRemoteKey( val buttonId = "CUSTOM_" + UUID.randomUUID().toString().take(8).uppercase() val assignedSlot = if (remote.transmissionMode == TransmissionMode.STATIC_SLOTS) { // Find next available slot - val bindings = (repository.getKeyBindings(remoteId) as? DomainResult.Success)?.value.orEmpty() - val usedSlots = bindings.mapNotNull { it.assignedSlot?.toIntOrNull() }.toSet() + val quota = when (val result = repository.slotQuota(profileId, remote.deviceName)) { + is DomainResult.Success -> result.value + is DomainResult.Failure -> return result + } + val usedSlots = quota.assignedStaticSlots val nextSlot = (1..99).firstOrNull { it !in usedSlots } ?: return DomainResult.Failure(DomainError.InvalidRequest(detail = "槽位配额已满,无法分配静态槽位")) nextSlot.toString() diff --git a/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/LearnVirtualRemoteKey.kt b/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/LearnVirtualRemoteKey.kt index fb453a3..5b6f3e2 100644 --- a/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/LearnVirtualRemoteKey.kt +++ b/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/LearnVirtualRemoteKey.kt @@ -26,9 +26,13 @@ class LearnVirtualRemoteKey( ?: return DomainResult.Failure(DomainError.InvalidRequest(detail = "Button not found")) val targetSlot = binding.assignedSlot ?: run { - val allBindings = (repository.getKeyBindings(remoteId) as? DomainResult.Success)?.value.orEmpty() - val usedSlots = allBindings.mapNotNull { it.assignedSlot?.toIntOrNull() }.toSet() - val nextSlot = (1..99).firstOrNull { it !in usedSlots } ?: 1 + val quota = when (val result = repository.slotQuota(profileId, remote.deviceName)) { + is DomainResult.Success -> result.value + is DomainResult.Failure -> return result + } + val nextSlot = quota.availableStaticSlots.firstOrNull() ?: return DomainResult.Failure( + DomainError.InvalidRequest(detail = "槽位配额已满,无法分配静态槽位"), + ) nextSlot.toString() } diff --git a/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/VirtualRemoteRepository.kt b/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/VirtualRemoteRepository.kt index fb9e622..53ff586 100644 --- a/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/VirtualRemoteRepository.kt +++ b/domain/src/main/kotlin/com/flagship/abox/manager/domain/infrared/VirtualRemoteRepository.kt @@ -4,6 +4,7 @@ import com.flagship.abox.manager.domain.DeviceName import com.flagship.abox.manager.domain.DomainResult import com.flagship.abox.manager.domain.ProviderProfileId import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first interface VirtualRemoteRepository { fun observeRemotes( @@ -25,6 +26,11 @@ interface VirtualRemoteRepository { deviceName: DeviceName, ): Flow> + suspend fun slotQuota( + profileId: ProviderProfileId, + deviceName: DeviceName, + ): DomainResult = observeSlotQuota(profileId, deviceName).first() + suspend fun getRemote( profileId: ProviderProfileId, remoteId: VirtualRemoteId, diff --git a/domain/src/test/kotlin/com/flagship/abox/manager/domain/infrared/VirtualRemoteUseCaseTest.kt b/domain/src/test/kotlin/com/flagship/abox/manager/domain/infrared/VirtualRemoteUseCaseTest.kt index d507536..a8bed9c 100644 --- a/domain/src/test/kotlin/com/flagship/abox/manager/domain/infrared/VirtualRemoteUseCaseTest.kt +++ b/domain/src/test/kotlin/com/flagship/abox/manager/domain/infrared/VirtualRemoteUseCaseTest.kt @@ -116,4 +116,46 @@ class VirtualRemoteUseCaseTest { val fetched = (repository.getRemote(profileId, remoteId) as DomainResult.Success).value assertEquals(null, fetched) } + + @Test + fun staticKeyAllocationUsesSlotsAcrossRemotesOnTheSameHardware() = runTest { + val firstRemote = VirtualRemote( + id = VirtualRemoteId("remote-one"), + profileId = profileId, + deviceName = deviceName, + displayName = "First", + type = RemoteDeviceType.TV, + transmissionMode = TransmissionMode.STATIC_SLOTS, + createdAtEpochMillis = 1L, + updatedAtEpochMillis = 1L, + ) + repository.createRemote( + firstRemote, + listOf( + RemoteKeyBinding( + id = "binding-one", + remoteId = firstRemote.id, + profileId = profileId, + buttonId = "POWER", + displayName = "Power", + assignedSlot = "1", + ), + ), + ) + val secondRemote = VirtualRemote( + id = VirtualRemoteId("remote-two"), + profileId = profileId, + deviceName = deviceName, + displayName = "Second", + type = RemoteDeviceType.TV, + transmissionMode = TransmissionMode.STATIC_SLOTS, + createdAtEpochMillis = 2L, + updatedAtEpochMillis = 2L, + ) + repository.createRemote(secondRemote, emptyList()) + + val result = AddVirtualRemoteKey(repository)(profileId, secondRemote.id, "Volume") + + assertEquals("2", (result as DomainResult.Success).value.assignedSlot) + } }