feat: 加入带有详细类型注释的 swagger 文档 (#257)

This commit is contained in:
ᴀᴍᴛᴏᴀᴇʀ
2025-02-18 01:55:54 +08:00
committed by GitHub
parent 1467c262a1
commit c995b3bf72
10 changed files with 293 additions and 102 deletions

View File

@@ -1,4 +1,3 @@
use std::collections::HashMap;
use std::sync::Arc;
use anyhow::{anyhow, Result};
@@ -7,71 +6,100 @@ use axum::Json;
use bili_sync_entity::*;
use bili_sync_migration::Expr;
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect};
use utoipa::OpenApi;
use crate::api::auth::OpenAPIAuth;
use crate::api::error::ApiError;
use crate::api::payload::{PageInfo, VideoDetail, VideoInfo, VideoList, VideoListModel, VideoListModelItem};
use crate::api::request::VideosRequest;
use crate::api::response::{PageInfo, VideoInfo, VideoResponse, VideoSource, VideoSourcesResponse, VideosResponse};
/// 列出所有视频列表
pub async fn get_video_list_models(
#[derive(OpenApi)]
#[openapi(
paths(get_video_sources, get_videos, get_video),
modifiers(&OpenAPIAuth),
security(
("Token" = []),
)
)]
pub struct ApiDoc;
/// 列出所有视频来源
#[utoipa::path(
get,
path = "/api/video-sources",
responses(
(status = 200, body = VideoSourcesResponse),
)
)]
pub async fn get_video_sources(
Extension(db): Extension<Arc<DatabaseConnection>>,
) -> Result<Json<VideoListModel>, ApiError> {
Ok(Json(VideoListModel {
) -> Result<Json<VideoSourcesResponse>, ApiError> {
Ok(Json(VideoSourcesResponse {
collection: collection::Entity::find()
.select_only()
.columns([collection::Column::Id, collection::Column::Name])
.into_model::<VideoListModelItem>()
.into_model::<VideoSource>()
.all(db.as_ref())
.await?,
favorite: favorite::Entity::find()
.select_only()
.columns([favorite::Column::Id, favorite::Column::Name])
.into_model::<VideoListModelItem>()
.into_model::<VideoSource>()
.all(db.as_ref())
.await?,
submission: submission::Entity::find()
.select_only()
.column(submission::Column::Id)
.column_as(submission::Column::UpperName, "name")
.into_model::<VideoListModelItem>()
.into_model::<VideoSource>()
.all(db.as_ref())
.await?,
watch_later: watch_later::Entity::find()
.select_only()
.column(watch_later::Column::Id)
.column_as(Expr::value("稍后再看"), "name")
.into_model::<VideoListModelItem>()
.into_model::<VideoSource>()
.all(db.as_ref())
.await?,
}))
}
/// 列出所有视频的基本信息支持根据视频列表筛选,支持分页
pub async fn list_videos(
/// 列出视频的基本信息支持根据视频来源筛选、名称查找和分页
#[utoipa::path(
get,
path = "/api/videos",
params(
VideosRequest,
),
responses(
(status = 200, body = VideosResponse),
)
)]
pub async fn get_videos(
Extension(db): Extension<Arc<DatabaseConnection>>,
Query(params): Query<HashMap<String, String>>,
) -> Result<Json<VideoList>, ApiError> {
Query(params): Query<VideosRequest>,
) -> Result<Json<VideosResponse>, ApiError> {
let mut query = video::Entity::find();
for (query_key, filter_column) in [
("collection", video::Column::CollectionId),
("favorite", video::Column::FavoriteId),
("submission", video::Column::SubmissionId),
("watch_later", video::Column::WatchLaterId),
for (field, column) in [
(params.collection, video::Column::CollectionId),
(params.favorite, video::Column::FavoriteId),
(params.submission, video::Column::SubmissionId),
(params.watch_later, video::Column::WatchLaterId),
] {
if let Some(value) = params.get(query_key) {
query = query.filter(filter_column.eq(value));
break;
if let Some(id) = field {
query = query.filter(column.eq(id));
}
}
if let Some(query_word) = params.get("q") {
if let Some(query_word) = params.query {
query = query.filter(video::Column::Name.contains(query_word));
}
let total_count = query.clone().count(db.as_ref()).await?;
let (page, page_size) = if let (Some(page), Some(page_size)) = (params.get("page"), params.get("page_size")) {
(page.parse::<u64>()?, page_size.parse::<u64>()?)
let (page, page_size) = if let (Some(page), Some(page_size)) = (params.page, params.page_size) {
(page, page_size)
} else {
(1, 10)
};
Ok(Json(VideoList {
Ok(Json(VideosResponse {
videos: query
.order_by_desc(video::Column::Id)
.into_partial_model::<VideoInfo>()
@@ -82,11 +110,18 @@ pub async fn list_videos(
}))
}
/// 根据 id 获取视频详细信息,包括关联的所有 page
/// 获取视频详细信息,包括关联的所有 page
#[utoipa::path(
get,
path = "/api/videos/{id}",
responses(
(status = 200, body = VideoResponse),
)
)]
pub async fn get_video(
Path(id): Path<i32>,
Extension(db): Extension<Arc<DatabaseConnection>>,
) -> Result<Json<VideoDetail>, ApiError> {
) -> Result<Json<VideoResponse>, ApiError> {
let video_info = video::Entity::find_by_id(id)
.into_partial_model::<VideoInfo>()
.one(db.as_ref())
@@ -100,7 +135,7 @@ pub async fn get_video(
.into_partial_model::<PageInfo>()
.all(db.as_ref())
.await?;
Ok(Json(VideoDetail {
Ok(Json(VideoResponse {
video: video_info,
pages,
}))