1use crate::utils::query_deserialize::{de_i32, de_opt_string, de_page, de_page_size};
7use crate::{
8 common::AppJson,
9 app::AppState,
10 common::{page_bean::PageBean, result::ResultBody},
11 entity::episode::{
12 EpisodeDetailVo, EpisodeEditDto, EpisodeListVo, EpisodeSimpleListVo, MemberStatistics,
13 NewestEpisodeVo, PublishLinkRequest, Statistics, UploadPageVo,
14 },
15 error::ApiResult,
16 middleware::AuthMember,
17 service::episode_service::EpisodeService,
18};
19use axum::{
20 extract::{Extension, Path, Query, State},
21 routing::{get, put},
22 Router,
23};
24use chrono::{DateTime, Utc};
25use serde::Deserialize;
26
27#[derive(Debug, Deserialize)]
29#[serde(rename_all = "camelCase")]
30pub struct EpisodePageQuery {
31 #[serde(default, deserialize_with = "de_page")]
33 pub page: i32,
34 #[serde(default, deserialize_with = "de_page_size")]
36 pub page_size: i32,
37 #[serde(deserialize_with = "de_i32")]
39 pub id: i32,
40}
41
42#[derive(Debug, Deserialize)]
44#[serde(rename_all = "camelCase")]
45pub struct UploadPageQuery {
46 #[serde(default, deserialize_with = "de_page")]
48 pub page: i32,
49 #[serde(default, deserialize_with = "de_page_size")]
51 pub page_size: i32,
52 #[serde(default, deserialize_with = "de_opt_string")]
54 pub manga_tran_name: Option<String>,
55 #[serde(default, deserialize_with = "de_opt_string")]
57 pub username: Option<String>,
58}
59
60#[derive(Debug, Deserialize)]
62pub struct StatisticsAnyQuery {
63 pub start: String,
65 pub end: String,
67}
68
69#[derive(Debug, Deserialize)]
71pub struct MemberStatisticsQuery {
72 pub start: DateTime<Utc>,
74 pub end: DateTime<Utc>,
76}
77
78pub fn routes() -> Router<AppState> {
80 Router::new()
81 .route(
82 "/",
83 get(page_episode)
84 .post(add_episode)
85 .put(update_manga_episode),
86 )
87 .route("/manga/:mangaId", get(list_episode_by_manga_id))
88 .route("/publishLinks", put(update_publish_links))
89 .route("/publishLink", put(update_publish_link))
90 .route("/:id", get(get_manga_episode_by_id).delete(delete_episode))
91 .route("/newest/:id", get(get_newest_manga_episode_by_id))
92 .route("/download", get(get_uploaded_submit))
93 .route("/statisticsAny", get(statistics_any))
94 .route("/statistics", get(statistics))
95 .route("/memberStatistics", get(member_statistics))
96 .route("/rollback/:episodeId/:workflowType", put(rollback_episode))
97}
98
99#[tracing::instrument(skip_all, level = "info")]
101pub async fn list_episode_by_manga_id(
102 State(state): State<AppState>,
103 Path(manga_id): Path<i32>,
104) -> ApiResult<ResultBody<Vec<EpisodeSimpleListVo>>> {
105 let data = EpisodeService::select_list_by_manga_id(&state, manga_id).await?;
106 Ok(ResultBody::success_data(data))
107}
108
109#[tracing::instrument(skip_all, level = "info")]
111pub async fn update_publish_links(
112 State(state): State<AppState>,
113 AppJson(body): AppJson<Vec<PublishLinkRequest>>,
114) -> ApiResult<ResultBody<()>> {
115 EpisodeService::update_manga_episodes(&state, body).await?;
116 Ok(ResultBody::success())
117}
118
119#[tracing::instrument(skip_all, level = "info")]
121pub async fn update_publish_link(
122 State(state): State<AppState>,
123 AppJson(body): AppJson<PublishLinkRequest>,
124) -> ApiResult<ResultBody<()>> {
125 EpisodeService::update_publish_link(&state, body.id, body.publish_link).await?;
126 Ok(ResultBody::success())
127}
128
129#[tracing::instrument(skip_all, level = "info")]
131pub async fn page_episode(
132 State(state): State<AppState>,
133 Query(q): Query<EpisodePageQuery>,
134) -> ApiResult<ResultBody<PageBean<EpisodeListVo>>> {
135 let data = EpisodeService::page_episode(&state, q.page, q.page_size, q.id).await?;
136 Ok(ResultBody::success_data(data))
137}
138
139#[tracing::instrument(skip_all, level = "info")]
141pub async fn delete_episode(
142 State(state): State<AppState>,
143 Path(id): Path<i32>,
144) -> ApiResult<ResultBody<()>> {
145 EpisodeService::delete_episode(&state, id).await?;
146 Ok(ResultBody::success())
147}
148
149#[tracing::instrument(skip_all, level = "info")]
151pub async fn add_episode(
152 State(state): State<AppState>,
153 AppJson(body): AppJson<EpisodeEditDto>,
154) -> ApiResult<ResultBody<()>> {
155 EpisodeService::add_episodes(&state, body).await?;
156 Ok(ResultBody::success())
157}
158
159#[tracing::instrument(skip_all, level = "info")]
161pub async fn get_manga_episode_by_id(
162 State(state): State<AppState>,
163 Path(id): Path<i32>,
164) -> ApiResult<ResultBody<EpisodeDetailVo>> {
165 let data = EpisodeService::get_manga_episode_by_id(&state, id).await?;
166 Ok(ResultBody::success_data(data))
167}
168
169#[tracing::instrument(skip_all, level = "info")]
171pub async fn get_newest_manga_episode_by_id(
172 State(state): State<AppState>,
173 Path(id): Path<i32>,
174) -> ApiResult<ResultBody<NewestEpisodeVo>> {
175 let data = EpisodeService::get_newest_manga_episode_by_id(&state, id).await?;
176 Ok(ResultBody::success_data(data))
177}
178
179#[tracing::instrument(skip_all, level = "info")]
181pub async fn update_manga_episode(
182 State(state): State<AppState>,
183 AppJson(body): AppJson<EpisodeEditDto>,
184) -> ApiResult<ResultBody<()>> {
185 EpisodeService::update_manga_episode(&state, body).await?;
186 Ok(ResultBody::success())
187}
188
189#[tracing::instrument(skip_all, level = "info")]
191pub async fn get_uploaded_submit(
192 State(state): State<AppState>,
193 Query(q): Query<UploadPageQuery>,
194) -> ApiResult<ResultBody<PageBean<UploadPageVo>>> {
195 let data = EpisodeService::get_uploaded_submit(
196 &state,
197 q.page,
198 q.page_size,
199 q.manga_tran_name,
200 q.username,
201 )
202 .await?;
203 Ok(ResultBody::success_data(data))
204}
205
206#[tracing::instrument(skip_all, level = "info")]
208pub async fn statistics_any(
209 State(state): State<AppState>,
210 Query(q): Query<StatisticsAnyQuery>,
211) -> ApiResult<ResultBody<Statistics>> {
212 let data = EpisodeService::get_statistics(&state, &q.start, &q.end).await?;
213 Ok(ResultBody::success_data(data))
214}
215
216#[tracing::instrument(skip_all, level = "info")]
218pub async fn statistics(State(state): State<AppState>) -> ApiResult<ResultBody<Vec<Statistics>>> {
219 let data = EpisodeService::get_statistics_list(&state).await?;
220 Ok(ResultBody::success_data(data))
221}
222
223#[tracing::instrument(skip_all, level = "info")]
225pub async fn member_statistics(
226 State(state): State<AppState>,
227 Query(q): Query<MemberStatisticsQuery>,
228) -> ApiResult<ResultBody<Vec<MemberStatistics>>> {
229 let data = EpisodeService::get_member_statistics(&state, q.start, q.end).await?;
230 Ok(ResultBody::success_data(data))
231}
232
233#[tracing::instrument(skip_all, level = "info")]
235pub async fn rollback_episode(
236 State(state): State<AppState>,
237 Extension(AuthMember(_member)): Extension<AuthMember>,
238 Path((episode_id, workflow_type)): Path<(i32, String)>,
239) -> ApiResult<ResultBody<()>> {
240 EpisodeService::rollback_episode(&state, episode_id, &workflow_type).await?;
241 Ok(ResultBody::success())
242}