use ::session::AuthedUser; use api::http::{Json, Path, Query, State}; use api::rest::{AppResponseData, NoData}; use uuid::Uuid; use crate::errors::ApiError; use crate::services::messages::{ BulkDeleteCmd, ForwardMessageCmd, ListMessageQuery, MessageDto, MessageEditDto, MessagePage, MessagePath, PinCmd, SendMessageCmd, UpdateMessageCmd, }; use crate::services::service::ChatService; #[utoipa::path( post, path = "/chat/rooms/{room_id}/messages", tag = "chat", description = "向房间发送消息。", params(("room_id" = Uuid, Path, description = "room id")), request_body = SendMessageCmd, responses( (status = 200, description = "message sent", body = AppResponseData), (status = 400, description = "bad request", body = AppResponseData), (status = 403, description = "forbidden", body = AppResponseData), (status = 409, description = "resource already exists", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn send_message( State(service): State, AuthedUser(uid): AuthedUser, Path(room_id): Path, Json(cmd): Json, ) -> Result, ApiError> { let dto = service.send_message(uid, room_id, cmd).await?; Ok(AppResponseData::ok(dto)) } #[utoipa::path( get, path = "/chat/rooms/{room_id}/messages", tag = "chat", description = "按房间序号过滤并分页读取消息;items 按 seq 升序,附带 has_more / oldest_seq / newest_seq 供游标翻页。", params(("room_id" = Uuid, Path, description = "room id"), ListMessageQuery), responses( (status = 200, description = "message page", body = AppResponseData), (status = 404, description = "not found", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn list_messages( State(service): State, AuthedUser(uid): AuthedUser, Path(room_id): Path, Query(q): Query, ) -> Result, ApiError> { let page = service.list_messages(uid, room_id, q).await?; Ok(AppResponseData::ok(page)) } #[utoipa::path( get, path = "/chat/rooms/{room_id}/messages/{message_id}", tag = "chat", description = "读取指定房间中单条未删除消息的详情。", params(MessagePath), responses( (status = 200, description = "message detail", body = AppResponseData), (status = 404, description = "not found", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn get_message( State(service): State, AuthedUser(uid): AuthedUser, Path(path): Path, ) -> Result, ApiError> { let dto = service .get_message(uid, path.room_id, path.message_id) .await?; Ok(AppResponseData::ok(dto)) } #[utoipa::path( patch, path = "/chat/rooms/{room_id}/messages/{message_id}", tag = "chat", description = "编辑自己或空间管理员有权修改的消息,并保存历史版本。", params(MessagePath), request_body = UpdateMessageCmd, responses( (status = 200, description = "message updated", body = AppResponseData), (status = 403, description = "forbidden", body = AppResponseData), (status = 400, description = "request failed validation", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn update_message( State(service): State, AuthedUser(uid): AuthedUser, Path(path): Path, Json(cmd): Json, ) -> Result, ApiError> { let dto = service .update_message(uid, path.room_id, path.message_id, cmd) .await?; Ok(AppResponseData::ok(dto)) } #[utoipa::path( delete, path = "/chat/rooms/{room_id}/messages/{message_id}", tag = "chat", description = "软删除指定消息并广播删除事件;作者或空间管理员可操作。", params(MessagePath), responses( (status = 200, description = "message deleted", body = AppResponseData), (status = 403, description = "forbidden", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn delete_message( State(service): State, AuthedUser(uid): AuthedUser, Path(path): Path, ) -> Result, ApiError> { service .delete_message(uid, path.room_id, path.message_id) .await?; Ok(AppResponseData::empty()) } #[utoipa::path( post, path = "/chat/rooms/{room_id}/messages/bulk-delete", tag = "chat", description = "批量软删除同一房间内最多 100 条有权删除的消息。", params(("room_id" = Uuid, Path, description = "room id")), request_body = BulkDeleteCmd, responses( (status = 200, description = "messages deleted", body = AppResponseData), (status = 403, description = "forbidden", body = AppResponseData), (status = 409, description = "resource already exists", body = AppResponseData), (status = 400, description = "request failed validation", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn bulk_delete_messages( State(service): State, AuthedUser(uid): AuthedUser, Path(room_id): Path, Json(cmd): Json, ) -> Result, ApiError> { service.bulk_delete_messages(uid, room_id, cmd).await?; Ok(AppResponseData::empty()) } #[utoipa::path( put, path = "/chat/rooms/{room_id}/messages/{message_id}/pin", tag = "chat", description = "置顶房间消息或取消置顶,并同步房间实时事件。", params(MessagePath), request_body = PinCmd, responses( (status = 200, description = "pin updated", body = AppResponseData), (status = 404, description = "not found", body = AppResponseData), (status = 400, description = "request failed validation", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn set_message_pin( State(service): State, AuthedUser(uid): AuthedUser, Path(path): Path, Json(cmd): Json, ) -> Result, ApiError> { let dto = service .set_message_pin(uid, path.room_id, path.message_id, cmd) .await?; Ok(AppResponseData::ok(dto)) } #[utoipa::path( get, path = "/chat/rooms/{room_id}/pins", tag = "chat", description = "返回房间内当前置顶且未删除的消息列表。", params(("room_id" = Uuid, Path, description = "room id")), responses( (status = 200, description = "pinned messages", body = AppResponseData>), (status = 404, description = "not found", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn list_pins( State(service): State, AuthedUser(uid): AuthedUser, Path(room_id): Path, ) -> Result>, ApiError> { let pins = service.list_pins(uid, room_id).await?; Ok(AppResponseData::ok(pins)) } #[utoipa::path( post, path = "/chat/rooms/{room_id}/messages/{message_id}/forward", tag = "chat", description = "将源房间消息转发到目标房间,并按发送消息权限执行校验。", params(MessagePath), request_body = ForwardMessageCmd, responses( (status = 200, description = "message forwarded", body = AppResponseData), (status = 404, description = "not found", body = AppResponseData), (status = 409, description = "resource already exists", body = AppResponseData), (status = 400, description = "request failed validation", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn forward_message( State(service): State, AuthedUser(uid): AuthedUser, Path(path): Path, Json(cmd): Json, ) -> Result, ApiError> { let dto = service .forward_message(uid, path.room_id, path.message_id, cmd) .await?; Ok(AppResponseData::ok(dto)) } #[utoipa::path( get, path = "/chat/rooms/{room_id}/messages/{message_id}/history", tag = "chat", description = "读取指定消息历次编辑保存的内容快照。", params(MessagePath), responses( (status = 200, description = "edit history", body = AppResponseData>), (status = 404, description = "not found", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn list_message_history( State(service): State, AuthedUser(uid): AuthedUser, Path(path): Path, ) -> Result>, ApiError> { let history = service .list_message_history(uid, path.room_id, path.message_id) .await?; Ok(AppResponseData::ok(history)) }