tdm_server_rust/service/
magazine_service.rs1use crate::{
6 app::AppState,
7 common::PageBean,
8 entity::{magazine::Magazine, manga::MangaListVo},
9 error::{ApiResult, AppError},
10 repository::{magazine_repo::{MagazineRepository, MagazineRow}, manga_repo::MangaRepository},
11 utils::page::paginate,
12};
13
14pub struct MagazineService;
16
17impl MagazineService {
18 #[tracing::instrument(skip_all, level = "debug")]
20 pub async fn get_magazines(
21 state: &AppState,
22 page: i32,
23 page_size: i32,
24 magazine_id: Option<i16>,
25 magazine_name: Option<String>,
26 ) -> ApiResult<PageBean<Magazine>> {
27 let repo = MagazineRepository::new(state.db.clone());
28 let all = repo
29 .get_magazines(magazine_id, magazine_name.as_deref())
30 .await?;
31 Ok(paginate(all.into_iter().map(row_to_magazine).collect(), page, page_size))
32 }
33
34 #[tracing::instrument(skip_all, level = "debug")]
36 pub async fn get_manga_magazine_list(state: &AppState) -> ApiResult<Vec<Magazine>> {
37 let repo = MagazineRepository::new(state.db.clone());
38 let rows = repo.get_manga_magazine_list().await?;
39 Ok(rows.into_iter().map(row_to_magazine).collect())
40 }
41
42 #[tracing::instrument(skip_all, level = "debug")]
44 pub async fn get_magazine_manga(
45 state: &AppState,
46 page: i32,
47 page_size: i32,
48 id: i32,
49 ) -> ApiResult<PageBean<MangaListVo>> {
50 MangaRepository::new(state.db.clone())
51 .list_by_magazine(id)
52 .await
53 .map(|all| paginate(all, page, page_size))
54 }
55
56 #[tracing::instrument(skip_all, level = "debug")]
58 pub async fn delete_magazine(state: &AppState, id: i32) -> ApiResult<()> {
59 let repo = MagazineRepository::new(state.db.clone());
60 if repo.get_related_magazine(id).await? {
61 return Err(AppError::business("该杂志已经绑定漫画了喵!"));
62 }
63 repo.delete_magazine_by_id(id).await
64 }
65
66 #[tracing::instrument(skip_all, level = "debug")]
68 pub async fn add_magazine(state: &AppState, magazine: Magazine) -> ApiResult<()> {
69 let repo = MagazineRepository::new(state.db.clone());
70 if let Some(name) = &magazine.magazine_name {
71 if repo.test_magazine_name(name).await?.is_some() {
72 return Err(AppError::unique("杂志名"));
73 }
74 }
75 let row = magazine_to_row(&magazine);
76 repo.insert_magazine(&row).await?;
77 Ok(())
78 }
79
80 #[tracing::instrument(skip_all, level = "debug")]
82 pub async fn get_magazine_by_id(state: &AppState, id: i32) -> ApiResult<Magazine> {
83 let repo = MagazineRepository::new(state.db.clone());
84 repo.get_magazine_by_id(id)
85 .await?
86 .map(row_to_magazine)
87 .ok_or_else(|| AppError::business("杂志不存在喵"))
88 }
89
90 #[tracing::instrument(skip_all, level = "debug")]
92 pub async fn update_magazine(state: &AppState, magazine: Magazine) -> ApiResult<()> {
93 let repo = MagazineRepository::new(state.db.clone());
94 let id = magazine
95 .id
96 .ok_or_else(|| AppError::business("缺少杂志 ID"))?;
97 let mut row = repo
98 .get_magazine_by_id(id)
99 .await?
100 .ok_or_else(|| AppError::business("杂志不存在喵"))?;
101 apply_magazine_fields(&mut row, &magazine);
102 if let Some(name) = &row.magazine_name {
103 if let Some(existing) = repo.test_magazine_name(name).await? {
104 if existing.id != Some(id) {
105 return Err(AppError::unique("杂志名"));
106 }
107 }
108 }
109 repo.update_magazine(&row).await
110 }
111}
112
113fn apply_magazine_fields(row: &mut MagazineRow, magazine: &Magazine) {
115 if let Some(v) = &magazine.magazine_name {
116 row.magazine_name = Some(v.clone());
117 }
118 if let Some(v) = magazine.r#type {
119 row.type_ = Some(v);
120 }
121 if let Some(v) = &magazine.update_time {
122 row.update_time = Some(v.clone());
123 }
124 if let Some(v) = magazine.price {
125 row.price = Some(v);
126 }
127}
128
129fn magazine_to_row(magazine: &Magazine) -> MagazineRow {
131 MagazineRow {
132 id: magazine.id,
133 magazine_name: magazine.magazine_name.clone(),
134 type_: magazine.r#type,
135 update_time: magazine.update_time.clone(),
136 price: magazine.price,
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::{apply_magazine_fields, magazine_to_row};
143 use crate::entity::magazine::Magazine;
144 use crate::repository::magazine_repo::MagazineRow;
145
146 #[test]
147 fn magazine_to_row_maps_all_fields() {
148 let m = Magazine {
149 id: None,
150 magazine_name: Some("季刊".into()),
151 r#type: Some(4),
152 update_time: Some("每月1日".into()),
153 price: Some(680),
154 };
155 let row = magazine_to_row(&m);
156 assert_eq!(row.magazine_name.as_deref(), Some("季刊"));
157 assert_eq!(row.type_, Some(4));
158 assert_eq!(row.update_time.as_deref(), Some("每月1日"));
159 assert_eq!(row.price, Some(680));
160 }
161
162 #[test]
163 fn apply_magazine_fields_merges_partial_update() {
164 let mut row = MagazineRow {
165 id: Some(1),
166 magazine_name: Some("旧名".into()),
167 type_: Some(1),
168 update_time: Some("旧时间".into()),
169 price: Some(100),
170 };
171 let patch = Magazine {
172 id: Some(1),
173 magazine_name: None,
174 r#type: Some(5),
175 update_time: Some("新时间".into()),
176 price: None,
177 };
178 apply_magazine_fields(&mut row, &patch);
179 assert_eq!(row.magazine_name.as_deref(), Some("旧名"));
180 assert_eq!(row.type_, Some(5));
181 assert_eq!(row.update_time.as_deref(), Some("新时间"));
182 assert_eq!(row.price, Some(100));
183 }
184}
185
186fn row_to_magazine(row: MagazineRow) -> Magazine {
188 Magazine {
189 id: row.id,
190 magazine_name: row.magazine_name,
191 r#type: row.type_,
192 update_time: row.update_time,
193 price: row.price,
194 }
195}