main
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<ReadStateDto>),
(status = 404, description = "not found", body = AppResponseData<NoData>),
(status = 400, description = "request failed validation", body = AppResponseData<NoData>),
(status = 401, description = "authentication required", body = AppResponseData<NoData>)
)
)]
pub async fn mark_read(
State(service): State<ChatService>,
AuthedUser(uid): AuthedUser,
Path(room_id): Path<Uuid>,
Json(cmd): Json<MarkReadCmd>,
) -> Result<AppResponseData<ReadStateDto>, 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<ReadStateDto>),
(status = 404, description = "not found", body = AppResponseData<NoData>),
(status = 401, description = "authentication required", body = AppResponseData<NoData>)
)
)]
pub async fn get_read_state(
State(service): State<ChatService>,
AuthedUser(uid): AuthedUser,
Path(room_id): Path<Uuid>,
) -> Result<AppResponseData<ReadStateDto>, 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<SpaceReadStates>),
(status = 404, description = "not found", body = AppResponseData<NoData>),
(status = 401, description = "authentication required", body = AppResponseData<NoData>)
)
)]
pub async fn list_space_read_states(
State(service): State<ChatService>,
AuthedUser(uid): AuthedUser,
Path(space_name): Path<String>,
) -> Result<AppResponseData<SpaceReadStates>, 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<Uuid>,
}
#[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<NoData>),
(status = 404, description = "not found", body = AppResponseData<NoData>),
(status = 400, description = "request failed validation", body = AppResponseData<NoData>),
(status = 401, description = "authentication required", body = AppResponseData<NoData>)
)
)]
pub async fn mark_thread_read(
State(service): State<ChatService>,
AuthedUser(uid): AuthedUser,
Path(thread_id): Path<Uuid>,
Json(q): Json<MarkThreadReadCmd>,
) -> Result<AppResponseData<NoData>, ApiError> {
service
.mark_thread_read(uid, thread_id, q.message_id)
.await?;
Ok(AppResponseData::empty())
}