Files
k-tv/k-tv-backend/domain/src/services/channel.rs

125 lines
4.0 KiB
Rust

use std::sync::Arc;
use uuid::Uuid;
use crate::entities::{Channel, ChannelConfigSnapshot, ScheduleConfig};
use crate::errors::{DomainError, DomainResult};
use crate::repositories::ChannelRepository;
use crate::value_objects::{ChannelId, UserId};
/// Service for managing channels (CRUD + ownership enforcement).
pub struct ChannelService {
channel_repo: Arc<dyn ChannelRepository>,
}
impl ChannelService {
pub fn new(channel_repo: Arc<dyn ChannelRepository>) -> Self {
Self { channel_repo }
}
pub async fn create(
&self,
owner_id: UserId,
name: &str,
timezone: &str,
) -> DomainResult<Channel> {
let channel = Channel::new(owner_id, name, timezone);
self.channel_repo.save(&channel).await?;
Ok(channel)
}
pub async fn find_by_id(&self, id: ChannelId) -> DomainResult<Channel> {
self.channel_repo
.find_by_id(id)
.await?
.ok_or(DomainError::ChannelNotFound(id))
}
pub async fn find_all(&self) -> DomainResult<Vec<Channel>> {
self.channel_repo.find_all().await
}
pub async fn find_by_owner(&self, owner_id: UserId) -> DomainResult<Vec<Channel>> {
self.channel_repo.find_by_owner(owner_id).await
}
pub async fn update(&self, channel: Channel) -> DomainResult<Channel> {
// Auto-snapshot the existing config before overwriting
if let Some(existing) = self.channel_repo.find_by_id(channel.id).await? {
self.channel_repo
.save_config_snapshot(channel.id, &existing.schedule_config, None)
.await?;
}
self.channel_repo.save(&channel).await?;
Ok(channel)
}
pub async fn list_config_snapshots(
&self,
channel_id: ChannelId,
) -> DomainResult<Vec<ChannelConfigSnapshot>> {
self.channel_repo.list_config_snapshots(channel_id).await
}
pub async fn get_config_snapshot(
&self,
channel_id: ChannelId,
snapshot_id: Uuid,
) -> DomainResult<Option<ChannelConfigSnapshot>> {
self.channel_repo.get_config_snapshot(channel_id, snapshot_id).await
}
pub async fn patch_config_snapshot_label(
&self,
channel_id: ChannelId,
snapshot_id: Uuid,
label: Option<String>,
) -> DomainResult<Option<ChannelConfigSnapshot>> {
self.channel_repo.patch_config_snapshot_label(channel_id, snapshot_id, label).await
}
/// Restore a snapshot: auto-snapshot current config, then apply the snapshot's config.
pub async fn restore_config_snapshot(
&self,
channel_id: ChannelId,
snapshot_id: Uuid,
) -> DomainResult<Channel> {
let snapshot = self
.channel_repo
.get_config_snapshot(channel_id, snapshot_id)
.await?
.ok_or(DomainError::ChannelNotFound(channel_id))?;
let mut channel = self
.channel_repo
.find_by_id(channel_id)
.await?
.ok_or(DomainError::ChannelNotFound(channel_id))?;
// Snapshot current config before overwriting
self.channel_repo
.save_config_snapshot(channel_id, &channel.schedule_config, None)
.await?;
channel.schedule_config = snapshot.config;
channel.updated_at = chrono::Utc::now();
self.channel_repo.save(&channel).await?;
Ok(channel)
}
pub async fn save_config_snapshot(
&self,
channel_id: ChannelId,
config: &ScheduleConfig,
label: Option<String>,
) -> DomainResult<ChannelConfigSnapshot> {
self.channel_repo.save_config_snapshot(channel_id, config, label).await
}
/// Delete a channel, enforcing that `requester_id` is the owner.
pub async fn delete(&self, id: ChannelId, requester_id: UserId) -> DomainResult<()> {
let channel = self.find_by_id(id).await?;
if channel.owner_id != requester_id {
return Err(DomainError::forbidden("You don't own this channel"));
}
self.channel_repo.delete(id).await
}
}