tdm_server_rust/service/
editor_service.rs1use crate::app::AppState;
10use crate::cache::on_episode_mutated;
11use crate::entity::editor::{
12 EditorPageInfo, EditorUnitInfo, EditorWorkspace, SaveEditorUnitsRequest,
13};
14use crate::error::{ApiResult, AppError};
15use crate::repository::editor_page_repo::EditorPageRepository;
16use crate::repository::editor_unit_repo::EditorUnitRepository;
17use crate::repository::episode_repo::EpisodeRepository;
18use crate::repository::manga_repo::MangaRepository;
19
20pub struct EditorService;
22
23impl EditorService {
24 #[tracing::instrument(skip_all, level = "debug")]
26 pub async fn get_workspace(state: &AppState, episode_id: i32) -> ApiResult<EditorWorkspace> {
27 let episode_repo = EpisodeRepository::new(state.db.clone());
28 let manga_id = episode_repo
29 .get_manga_id_by_episode_id(episode_id)
30 .await?
31 .ok_or_else(|| AppError::business(format!("漫画单话 {episode_id} 不存在喵")))?;
32 let manga_name = MangaRepository::new(state.db.clone())
33 .get_manga_by_id(manga_id)
34 .await?
35 .and_then(|manga| manga.manga_tran_name);
36 let has_provider_source = episode_repo
37 .get_provider_oss_id(episode_id)
38 .await?
39 .is_some();
40 let pages = EditorPageRepository::new(state.db.clone())
41 .list_by_episode(episode_id)
42 .await?
43 .into_iter()
44 .map(EditorPageInfo::from)
45 .collect();
46 Ok(EditorWorkspace {
47 episode_id,
48 manga_id,
49 manga_name,
50 has_provider_source,
51 pages,
52 })
53 }
54
55 #[tracing::instrument(skip_all, level = "debug")]
57 pub async fn list_pages(state: &AppState, episode_id: i32) -> ApiResult<Vec<EditorPageInfo>> {
58 let pages = EditorPageRepository::new(state.db.clone())
59 .list_by_episode(episode_id)
60 .await?
61 .into_iter()
62 .map(EditorPageInfo::from)
63 .collect();
64 Ok(pages)
65 }
66
67 #[tracing::instrument(skip_all, level = "debug")]
69 pub async fn list_units(state: &AppState, page_id: i64) -> ApiResult<Vec<EditorUnitInfo>> {
70 let page_repo = EditorPageRepository::new(state.db.clone());
71 let episode_id = page_repo
72 .episode_of_page(page_id)
73 .await?
74 .ok_or_else(|| AppError::business(format!("页面 {page_id} 不存在喵")))?;
75 let units = EditorUnitRepository::new(state.db.clone())
76 .list_by_page(episode_id, page_id)
77 .await?
78 .into_iter()
79 .map(EditorUnitInfo::from)
80 .collect();
81 Ok(units)
82 }
83
84 #[tracing::instrument(skip_all, level = "debug")]
86 pub async fn save_units(
87 state: &AppState,
88 req: SaveEditorUnitsRequest,
89 editor_member_id: i32,
90 ) -> ApiResult<usize> {
91 let page_repo = EditorPageRepository::new(state.db.clone());
92 let episode_id = page_repo
93 .episode_of_page(req.page_id)
94 .await?
95 .ok_or_else(|| AppError::business(format!("页面 {} 不存在喵", req.page_id)))?;
96 let saved = EditorUnitRepository::new(state.db.clone())
97 .replace_page_units(episode_id, req.page_id, editor_member_id, &req.units)
98 .await?;
99 Ok(saved)
100 }
101
102 #[tracing::instrument(skip_all, level = "debug")]
104 pub async fn set_page_translation_completion(
105 state: &AppState,
106 page_id: i64,
107 completed: bool,
108 ) -> ApiResult<()> {
109 EditorPageRepository::new(state.db.clone())
110 .set_translation_completed(page_id, completed)
111 .await
112 }
113
114 #[tracing::instrument(skip_all, level = "debug")]
118 pub async fn complete_episode_workflow(
119 state: &AppState,
120 episode_id: i32,
121 role: &str,
122 member_id: i32,
123 ) -> ApiResult<()> {
124 let episode_repo = EpisodeRepository::new(state.db.clone());
125 let manga_id = episode_repo
126 .get_manga_id_by_episode_id(episode_id)
127 .await?
128 .ok_or_else(|| AppError::business(format!("漫画单话 {episode_id} 不存在喵")))?;
129 episode_repo
130 .touch_detail_submit_time(episode_id, role)
131 .await?;
132 on_episode_mutated(state, manga_id, &[member_id]).await;
134 Ok(())
135 }
136}