feat: add utoipa path annotations to all API handlers and wire OpenAPI spec

- Add #[utoipa::path] annotations to all 70+ handler functions across
  auth, config, workflow, and message modules
- Add IntoParams/ToSchema derives to Pagination, PaginatedResponse, ApiResponse
  in erp-core, and MessageQuery/TemplateQuery in erp-message
- Collect all module paths into OpenAPI spec via AuthApiDoc, ConfigApiDoc,
  WorkflowApiDoc, MessageApiDoc structs in erp-server main.rs
- Update openapi_spec handler to merge all module specs
- The /docs/openapi.json endpoint now returns complete API documentation
  with all endpoints, request/response schemas, and security requirements
This commit is contained in:
iven
2026-04-15 01:23:27 +08:00
parent ee65b6e3c9
commit e44d6063be
21 changed files with 1165 additions and 22 deletions

View File

@@ -75,7 +75,7 @@ fn default_priority() -> String {
}
/// 消息列表查询参数
#[derive(Debug, Deserialize, ToSchema)]
#[derive(Debug, Deserialize, ToSchema, utoipa::IntoParams)]
pub struct MessageQuery {
pub page: Option<u64>,
pub page_size: Option<u64>,

View File

@@ -12,6 +12,18 @@ use crate::dto::{MessageQuery, MessageResp, SendMessageReq, UnreadCountResp};
use crate::message_state::MessageState;
use crate::service::message_service::MessageService;
#[utoipa::path(
get,
path = "/api/v1/messages",
params(MessageQuery),
responses(
(status = 200, description = "成功", body = ApiResponse<PaginatedResponse<MessageResp>>),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "消息管理"
)]
/// 查询消息列表。
pub async fn list_messages<S>(
State(_state): State<MessageState>,
@@ -40,6 +52,17 @@ where
})))
}
#[utoipa::path(
get,
path = "/api/v1/messages/unread-count",
responses(
(status = 200, description = "成功", body = ApiResponse<UnreadCountResp>),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "消息管理"
)]
/// 获取未读消息数量。
pub async fn unread_count<S>(
State(_state): State<MessageState>,
@@ -55,6 +78,18 @@ where
Ok(Json(ApiResponse::ok(result)))
}
#[utoipa::path(
post,
path = "/api/v1/messages",
request_body = SendMessageReq,
responses(
(status = 200, description = "成功", body = ApiResponse<MessageResp>),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "消息管理"
)]
/// 发送消息。
pub async fn send_message<S>(
State(_state): State<MessageState>,
@@ -82,6 +117,18 @@ where
Ok(Json(ApiResponse::ok(resp)))
}
#[utoipa::path(
put,
path = "/api/v1/messages/{id}/read",
params(("id" = Uuid, Path, description = "消息ID")),
responses(
(status = 200, description = "成功"),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "消息管理"
)]
/// 标记消息已读。
pub async fn mark_read<S>(
State(_state): State<MessageState>,
@@ -96,6 +143,17 @@ where
Ok(Json(ApiResponse::ok(())))
}
#[utoipa::path(
put,
path = "/api/v1/messages/read-all",
responses(
(status = 200, description = "成功"),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "消息管理"
)]
/// 标记所有消息已读。
pub async fn mark_all_read<S>(
State(_state): State<MessageState>,
@@ -109,6 +167,18 @@ where
Ok(Json(ApiResponse::ok(())))
}
#[utoipa::path(
delete,
path = "/api/v1/messages/{id}",
params(("id" = Uuid, Path, description = "消息ID")),
responses(
(status = 200, description = "成功"),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "消息管理"
)]
/// 删除消息。
pub async fn delete_message<S>(
State(_state): State<MessageState>,

View File

@@ -9,6 +9,18 @@ use crate::dto::UpdateSubscriptionReq;
use crate::message_state::MessageState;
use crate::service::subscription_service::SubscriptionService;
#[utoipa::path(
put,
path = "/api/v1/message-subscriptions",
request_body = UpdateSubscriptionReq,
responses(
(status = 200, description = "成功", body = ApiResponse<crate::dto::MessageSubscriptionResp>),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "消息订阅"
)]
/// 更新消息订阅偏好。
pub async fn update_subscription<S>(
State(_state): State<MessageState>,

View File

@@ -12,12 +12,24 @@ use crate::dto::{CreateTemplateReq, MessageTemplateResp};
use crate::message_state::MessageState;
use crate::service::template_service::TemplateService;
#[derive(Debug, Deserialize)]
#[derive(Debug, Deserialize, utoipa::IntoParams)]
pub struct TemplateQuery {
pub page: Option<u64>,
pub page_size: Option<u64>,
}
#[utoipa::path(
get,
path = "/api/v1/message-templates",
params(TemplateQuery),
responses(
(status = 200, description = "成功", body = ApiResponse<PaginatedResponse<MessageTemplateResp>>),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "消息模板"
)]
/// 查询消息模板列表。
pub async fn list_templates<S>(
State(_state): State<MessageState>,
@@ -46,6 +58,18 @@ where
})))
}
#[utoipa::path(
post,
path = "/api/v1/message-templates",
request_body = CreateTemplateReq,
responses(
(status = 200, description = "成功", body = ApiResponse<MessageTemplateResp>),
(status = 401, description = "未授权"),
(status = 403, description = "权限不足"),
),
security(("bearer_auth" = [])),
tag = "消息模板"
)]
/// 创建消息模板。
pub async fn create_template<S>(
State(_state): State<MessageState>,