use crate::error::RoomError; use crate::service::RoomService; use crate::ws_context::WsUserContext; use chrono::Utc; use models::rooms::{room, room_category}; use queue::ProjectRoomEvent; use sea_orm::prelude::Expr; use sea_orm::*; use uuid::Uuid; impl RoomService { pub async fn room_category_list( &self, project_name: String, ctx: &WsUserContext, ) -> Result, RoomError> { let user_id = ctx.user_id; let project = self.utils_find_project_by_name(project_name).await?; self.check_project_access(project.id, user_id).await?; let models = room_category::Entity::find() .filter(room_category::Column::Project.eq(project.id)) .order_by_asc(room_category::Column::Position) .all(&self.db) .await?; Ok(models .into_iter() .map(super::RoomCategoryResponse::from) .collect()) } pub async fn room_category_create( &self, project_name: String, request: super::RoomCategoryCreateRequest, ctx: &WsUserContext, ) -> Result { let user_id = ctx.user_id; let project = self.utils_find_project_by_name(project_name).await?; self.require_project_admin(project.id, user_id).await?; Self::validate_name(&request.name, super::MAX_CATEGORY_NAME_LEN)?; let position = if let Some(position) = request.position { position } else { let max_position: Option> = room_category::Entity::find() .filter(room_category::Column::Project.eq(project.id)) .select_only() .column_as(room_category::Column::Position.max(), "max_position") .into_tuple::>() .one(&self.db) .await?; max_position.flatten().unwrap_or(0) + 1 }; let model = room_category::ActiveModel { id: Set(Uuid::now_v7()), project: Set(project.id), name: Set(request.name), position: Set(position), created_by: Set(user_id), created_at: Set(Utc::now()), } .insert(&self.db) .await?; let event = ProjectRoomEvent { event_type: super::RoomEventType::CategoryCreated.as_str().into(), project_id: project.id, room_id: None, category_id: Some(model.id), message_id: None, seq: None, timestamp: Utc::now(), }; let _ = self .queue .publish_project_room_event(project.id, event) .await; Ok(super::RoomCategoryResponse::from(model)) } pub async fn room_category_update( &self, category_id: Uuid, request: super::RoomCategoryUpdateRequest, ctx: &WsUserContext, ) -> Result { let user_id = ctx.user_id; let model = room_category::Entity::find_by_id(category_id) .one(&self.db) .await? .ok_or_else(|| RoomError::NotFound("Room category not found".to_string()))?; self.require_project_admin(model.project, user_id).await?; let mut active: room_category::ActiveModel = model.into(); if let Some(name) = request.name { active.name = Set(name); } if let Some(position) = request.position { active.position = Set(position); } let updated = active.update(&self.db).await?; let event = ProjectRoomEvent { event_type: super::RoomEventType::CategoryUpdated.as_str().into(), project_id: updated.project, room_id: None, category_id: Some(updated.id), message_id: None, seq: None, timestamp: Utc::now(), }; let _ = self .queue .publish_project_room_event(updated.project, event) .await; Ok(super::RoomCategoryResponse::from(updated)) } pub async fn room_category_delete( &self, category_id: Uuid, ctx: &WsUserContext, ) -> Result<(), RoomError> { let user_id = ctx.user_id; let model = room_category::Entity::find_by_id(category_id) .one(&self.db) .await? .ok_or_else(|| RoomError::NotFound("Room category not found".to_string()))?; self.require_project_admin(model.project, user_id).await?; let project_id = model.project; let txn = self.db.begin().await?; room::Entity::update_many() .col_expr(room::Column::Category, Expr::value(None::)) .filter(room::Column::Category.eq(category_id)) .exec(&txn) .await?; room_category::Entity::delete_by_id(category_id) .exec(&txn) .await?; txn.commit().await?; let event = ProjectRoomEvent { event_type: super::RoomEventType::CategoryDeleted.as_str().into(), project_id, room_id: None, category_id: Some(category_id), message_id: None, seq: None, timestamp: Utc::now(), }; let _ = self .queue .publish_project_room_event(project_id, event) .await; Ok(()) } }