use ::session::AuthedUser; use api::http::{Json, Path, State}; use api::rest::{AppResponseData, NoData}; use uuid::Uuid; use crate::errors::ApiError; use crate::services::read_state::{MarkReadCmd, ReadStateDto, SpaceReadStates}; use crate::services::service::ChatService; #[utoipa::path( put, path = "/chat/rooms/{room_id}/read-state", tag = "chat", description = "按消息 ID、序号或房间末尾消息更新当前用户的已读游标。", params(("room_id" = Uuid, Path, description = "room id")), request_body = MarkReadCmd, responses( (status = 200, description = "read state 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 mark_read( State(service): State, AuthedUser(uid): AuthedUser, Path(room_id): Path, Json(cmd): Json, ) -> Result, ApiError> { let dto = service.mark_read(uid, room_id, cmd).await?; Ok(AppResponseData::ok(dto)) } #[utoipa::path( get, path = "/chat/rooms/{room_id}/read-state", tag = "chat", description = "读取当前用户在指定房间的已读位置与未读计数。", params(("room_id" = Uuid, Path, description = "room id")), responses( (status = 200, description = "read state", body = AppResponseData), (status = 404, description = "not found", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn get_read_state( State(service): State, AuthedUser(uid): AuthedUser, Path(room_id): Path, ) -> Result, ApiError> { let dto = service.get_read_state(uid, room_id).await?; Ok(AppResponseData::ok(dto)) } #[utoipa::path( get, path = "/chat/spaces/{space_name}/read-states", tag = "chat", description = "汇总当前用户在一个空间内可见房间的未读状态。", params(("space_name" = String, Path, description = "space name")), responses( (status = 200, description = "space read states", body = AppResponseData), (status = 404, description = "not found", body = AppResponseData), (status = 401, description = "authentication required", body = AppResponseData) ) )] pub async fn list_space_read_states( State(service): State, AuthedUser(uid): AuthedUser, Path(space_name): Path, ) -> Result, ApiError> { let page = service.list_space_read_states(uid, &space_name).await?; Ok(AppResponseData::ok(page)) } #[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)] pub struct MarkThreadReadCmd { #[serde(default)] pub message_id: Option, } #[utoipa::path( put, path = "/chat/threads/{thread_id}/read-state", tag = "chat", description = "更新当前用户在指定话题中的已读游标。", params(("thread_id" = Uuid, Path, description = "thread id")), request_body = MarkThreadReadCmd, responses( (status = 200, description = "thread marked read", 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 mark_thread_read( State(service): State, AuthedUser(uid): AuthedUser, Path(thread_id): Path, Json(q): Json, ) -> Result, ApiError> { service .mark_thread_read(uid, thread_id, q.message_id) .await?; Ok(AppResponseData::empty()) }