1use crate::utils::query_deserialize::{de_i32, de_opt_string, de_page, de_page_size};
7use crate::{
8 app::AppState,
9 common::result::{json_bytes_response, ResultBody},
10 common::AppJson,
11 entity::episode::{
12 EpisodeDetailVo, EpisodeEditDto, EpisodeType, NewestEpisodeVo, PublishLinkRequest,
13 Statistics,
14 },
15 error::ApiResult,
16 middleware::AuthMember,
17 service::episode_service::EpisodeService,
18};
19use axum::{
20 extract::{Extension, Path, Query, State},
21 response::Response,
22 routing::{get, put},
23 Router,
24};
25use chrono::{DateTime, Utc};
26use serde::Deserialize;
27
28#[derive(Debug, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct EpisodePageQuery {
32 #[serde(default, deserialize_with = "de_page")]
34 pub page: i32,
35 #[serde(default, deserialize_with = "de_page_size")]
37 pub page_size: i32,
38 #[serde(deserialize_with = "de_i32")]
40 pub id: i32,
41 #[serde(default)]
43 pub episode_type: EpisodeType,
44}
45
46#[derive(Debug, Deserialize)]
48#[serde(rename_all = "camelCase")]
49pub struct UploadPageQuery {
50 #[serde(default, deserialize_with = "de_page")]
52 pub page: i32,
53 #[serde(default, deserialize_with = "de_page_size")]
55 pub page_size: i32,
56 #[serde(default, deserialize_with = "de_opt_string")]
58 pub manga_tran_name: Option<String>,
59 #[serde(default, deserialize_with = "de_opt_string")]
61 pub username: Option<String>,
62}
63
64#[derive(Debug, Deserialize)]
66pub struct StatisticsAnyQuery {
67 pub start: String,
69 pub end: String,
71}
72
73#[derive(Debug, Deserialize)]
75pub struct MemberStatisticsQuery {
76 pub start: DateTime<Utc>,
78 pub end: DateTime<Utc>,
80}
81
82pub fn routes() -> Router<AppState> {
84 Router::new()
85 .route(
86 "/",
87 get(page_episode)
88 .post(add_episode)
89 .put(update_manga_episode),
90 )
91 .route("/manga/:mangaId", get(list_episode_by_manga_id))
92 .route("/publishLinks", put(update_publish_links))
93 .route("/publishLink", put(update_publish_link))
94 .route("/:id", get(get_manga_episode_by_id).delete(delete_episode))
95 .route("/newest/:id", get(get_newest_manga_episode_by_id))
96 .route("/download", get(get_uploaded_submit))
97 .route("/statisticsAny", get(statistics_any))
98 .route("/statistics", get(statistics))
99 .route("/memberStatistics", get(member_statistics))
100 .route("/rollback/:episodeId/:workflowType", put(rollback_episode))
101}
102
103#[tracing::instrument(skip_all, level = "info")]
105pub async fn list_episode_by_manga_id(
106 State(state): State<AppState>,
107 Path(manga_id): Path<i32>,
108) -> ApiResult<Response> {
109 let json = EpisodeService::select_list_by_manga_id_json(&state, manga_id).await?;
110 Ok(json_bytes_response(json))
111}
112
113#[tracing::instrument(skip_all, level = "info")]
115pub async fn update_publish_links(
116 State(state): State<AppState>,
117 AppJson(body): AppJson<Vec<PublishLinkRequest>>,
118) -> ApiResult<ResultBody<()>> {
119 EpisodeService::update_manga_episodes(&state, body).await?;
120 Ok(ResultBody::success())
121}
122
123#[tracing::instrument(skip_all, level = "info")]
125pub async fn update_publish_link(
126 State(state): State<AppState>,
127 AppJson(body): AppJson<PublishLinkRequest>,
128) -> ApiResult<ResultBody<()>> {
129 EpisodeService::update_publish_link(&state, body.id, body.publish_link).await?;
130 Ok(ResultBody::success())
131}
132
133#[tracing::instrument(skip_all, level = "info")]
135pub async fn page_episode(
136 State(state): State<AppState>,
137 Query(q): Query<EpisodePageQuery>,
138) -> ApiResult<Response> {
139 let json = EpisodeService::page_episode_json(
140 &state,
141 q.page,
142 q.page_size,
143 q.id,
144 q.episode_type,
145 )
146 .await?;
147 Ok(json_bytes_response(json))
148}
149
150#[tracing::instrument(skip_all, level = "info")]
152pub async fn delete_episode(
153 State(state): State<AppState>,
154 Path(id): Path<i32>,
155) -> ApiResult<ResultBody<()>> {
156 EpisodeService::delete_episode(&state, id).await?;
157 Ok(ResultBody::success())
158}
159
160#[tracing::instrument(skip_all, level = "info")]
162pub async fn add_episode(
163 State(state): State<AppState>,
164 AppJson(body): AppJson<EpisodeEditDto>,
165) -> ApiResult<ResultBody<()>> {
166 EpisodeService::add_episodes(&state, body).await?;
167 Ok(ResultBody::success())
168}
169
170#[tracing::instrument(skip_all, level = "info")]
172pub async fn get_manga_episode_by_id(
173 State(state): State<AppState>,
174 Path(id): Path<i32>,
175) -> ApiResult<ResultBody<EpisodeDetailVo>> {
176 let data = EpisodeService::get_manga_episode_by_id(&state, id).await?;
177 Ok(ResultBody::success_data(data))
178}
179
180#[tracing::instrument(skip_all, level = "info")]
182pub async fn get_newest_manga_episode_by_id(
183 State(state): State<AppState>,
184 Path(id): Path<i32>,
185) -> ApiResult<ResultBody<NewestEpisodeVo>> {
186 let data = EpisodeService::get_newest_manga_episode_by_id(&state, id).await?;
187 Ok(ResultBody::success_data(data))
188}
189
190#[tracing::instrument(skip_all, level = "info")]
192pub async fn update_manga_episode(
193 State(state): State<AppState>,
194 AppJson(body): AppJson<EpisodeEditDto>,
195) -> ApiResult<ResultBody<()>> {
196 EpisodeService::update_manga_episode(&state, body).await?;
197 Ok(ResultBody::success())
198}
199
200#[tracing::instrument(skip_all, level = "info")]
202pub async fn get_uploaded_submit(
203 State(state): State<AppState>,
204 Query(q): Query<UploadPageQuery>,
205) -> ApiResult<Response> {
206 let json = EpisodeService::get_uploaded_submit_json(
207 &state,
208 q.page,
209 q.page_size,
210 q.manga_tran_name,
211 q.username,
212 )
213 .await?;
214 Ok(json_bytes_response(json))
215}
216
217#[tracing::instrument(skip_all, level = "info")]
219pub async fn statistics_any(
220 State(state): State<AppState>,
221 Query(q): Query<StatisticsAnyQuery>,
222) -> ApiResult<ResultBody<Statistics>> {
223 let data = EpisodeService::get_statistics(&state, &q.start, &q.end).await?;
224 Ok(ResultBody::success_data(data))
225}
226
227#[tracing::instrument(skip_all, level = "info")]
229pub async fn statistics(State(state): State<AppState>) -> ApiResult<Response> {
230 let json = EpisodeService::get_statistics_list_json(&state).await?;
231 Ok(json_bytes_response(json))
232}
233
234#[tracing::instrument(skip_all, level = "info")]
236pub async fn member_statistics(
237 State(state): State<AppState>,
238 Query(q): Query<MemberStatisticsQuery>,
239) -> ApiResult<Response> {
240 let json = EpisodeService::get_member_statistics_json(&state, q.start, q.end).await?;
241 Ok(json_bytes_response(json))
242}
243
244#[tracing::instrument(skip_all, level = "info")]
246pub async fn rollback_episode(
247 State(state): State<AppState>,
248 Extension(AuthMember(member)): Extension<AuthMember>,
249 Path((episode_id, workflow_type)): Path<(i32, String)>,
250) -> ApiResult<ResultBody<()>> {
251 EpisodeService::rollback_episode(&state, episode_id, &workflow_type, member.map(|m| m.id))
252 .await?;
253 Ok(ResultBody::success())
254}