feat: add observable scene execution foundation
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
package com.flagship.abox.manager.domain
|
||||
|
||||
interface NotificationGateway {
|
||||
suspend fun show(
|
||||
profileId: ProviderProfileId,
|
||||
title: String,
|
||||
message: String,
|
||||
): DomainResult<Unit>
|
||||
}
|
||||
@@ -1,5 +1,19 @@
|
||||
package com.flagship.abox.manager.domain
|
||||
|
||||
@JvmInline
|
||||
value class SceneExecutionId(val value: String)
|
||||
|
||||
enum class SceneExecutionSource { USER, AUTOMATION }
|
||||
|
||||
sealed interface SceneExecutionUpdate {
|
||||
data class Started(val executionId: SceneExecutionId, val scene: Scene) : SceneExecutionUpdate
|
||||
data class StepPending(val action: SceneAction) : SceneExecutionUpdate
|
||||
data class StepRunning(val action: SceneAction) : SceneExecutionUpdate
|
||||
data class StepFinished(val result: SceneActionExecutionResult) : SceneExecutionUpdate
|
||||
data class Completed(val result: SceneExecutionResult) : SceneExecutionUpdate
|
||||
data class Rejected(val error: DomainError) : SceneExecutionUpdate
|
||||
}
|
||||
|
||||
enum class SceneActionOutcome {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.flagship.abox.manager.domain
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Executes a [Scene] sequentially. Per-action failures are recorded but do not abort subsequent
|
||||
* actions; callers receive a [SceneExecutionResult] that summarizes every step.
|
||||
@@ -9,4 +13,23 @@ package com.flagship.abox.manager.domain
|
||||
*/
|
||||
interface SceneExecutor {
|
||||
suspend fun execute(scene: Scene): DomainResult<SceneExecutionResult>
|
||||
|
||||
fun execute(
|
||||
scene: Scene,
|
||||
executionId: SceneExecutionId = SceneExecutionId(UUID.randomUUID().toString()),
|
||||
source: SceneExecutionSource = SceneExecutionSource.USER,
|
||||
): Flow<SceneExecutionUpdate> = flow {
|
||||
emit(SceneExecutionUpdate.Started(executionId, scene))
|
||||
when (val result = execute(scene)) {
|
||||
is DomainResult.Failure -> emit(SceneExecutionUpdate.Rejected(result.error))
|
||||
is DomainResult.Success -> {
|
||||
result.value.results.forEach { actionResult ->
|
||||
emit(SceneExecutionUpdate.StepPending(actionResult.action))
|
||||
emit(SceneExecutionUpdate.StepRunning(actionResult.action))
|
||||
emit(SceneExecutionUpdate.StepFinished(actionResult))
|
||||
}
|
||||
emit(SceneExecutionUpdate.Completed(result.value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.flagship.abox.manager.domain
|
||||
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class SceneExecutionFlowTest {
|
||||
@Test
|
||||
fun observableExecutionPublishesLifecycleEventsWithoutChangingLegacyResult() = runTest {
|
||||
val scene = Scene(
|
||||
id = SceneId("scene"),
|
||||
profileId = ProviderProfileId("profile"),
|
||||
name = "测试情景",
|
||||
)
|
||||
val executor = DefaultSceneExecutor(
|
||||
socketGateway = object : SocketGateway {
|
||||
override suspend fun getStatus(profileId: ProviderProfileId, deviceName: DeviceName) =
|
||||
DomainResult.Success(SocketStatus(profileId, deviceName, SocketPowerState.OFF))
|
||||
override suspend fun setPower(profileId: ProviderProfileId, deviceName: DeviceName, powerState: SocketPowerState) =
|
||||
DomainResult.Success(SocketStatus(profileId, deviceName, powerState))
|
||||
},
|
||||
infraredGateway = object : InfraredGateway {
|
||||
override suspend fun sendKey(profileId: ProviderProfileId, deviceName: DeviceName, key: String) = DomainResult.Success(Unit)
|
||||
override suspend fun learnKey(profileId: ProviderProfileId, deviceName: DeviceName, key: String) = DomainResult.Success(Unit)
|
||||
override suspend fun downloadCodes(profileId: ProviderProfileId, codes: List<InfraredCode>) = DomainResult.Success(emptyList<InfraredCodeDownloadResult>())
|
||||
},
|
||||
)
|
||||
|
||||
val updates = executor.execute(scene, SceneExecutionId("run-1")).toList()
|
||||
|
||||
assertTrue(updates.first() is SceneExecutionUpdate.Started)
|
||||
assertTrue(updates.last() is SceneExecutionUpdate.Completed)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user