Skip to main content

tdm_server_rust/repository/
author_repo.rs

1//! 作者数据访问层 (Author Repository)
2//!
3//! 封装 `authortb` 表的增删改查。
4
5use crate::entity::{author::Author, manga::Manga};
6use sqlx::{MySql, Pool, Row};
7
8/// 作者仓储
9pub struct AuthorRepository {
10    /// 连接池
11    pool: Pool<MySql>,
12}
13
14impl AuthorRepository {
15    /// 构造仓储
16    pub fn new(pool: Pool<MySql>) -> Self {
17        Self { pool }
18    }
19
20    /// 条件查询作者列表
21    #[tracing::instrument(skip_all, level = "debug")]
22    pub async fn get_authors(
23        &self,
24        author_id: Option<i16>,
25        author_name: Option<&str>,
26    ) -> crate::error::ApiResult<Vec<Author>> {
27        let mut sql = String::from("SELECT a.Id, a.authorName FROM authortb a WHERE 1=1");
28        if author_id.is_some() {
29            sql.push_str(" AND a.Id = ?");
30        }
31        if author_name.is_some() {
32            sql.push_str(" AND a.authorName = ?");
33        }
34        let mut q = sqlx::query(&sql);
35        if let Some(id) = author_id {
36            q = q.bind(id);
37        }
38        if let Some(name) = author_name {
39            q = q.bind(name);
40        }
41        let rows = q.fetch_all(&self.pool).await?;
42        Ok(rows.into_iter().map(|r| row_to_author(&r)).collect())
43    }
44
45    /// 按 ID 删除作者
46    #[tracing::instrument(skip_all, level = "debug")]
47    pub async fn delete_by_id(&self, id: i32) -> crate::error::ApiResult<()> {
48        sqlx::query("DELETE FROM authortb WHERE Id = ?")
49            .bind(id)
50            .execute(&self.pool)
51            .await?;
52        Ok(())
53    }
54
55    /// 新增作者
56    #[tracing::instrument(skip_all, level = "debug")]
57    pub async fn insert(&self, author: &Author) -> crate::error::ApiResult<i32> {
58        let r = sqlx::query("INSERT INTO authortb(authorName) VALUES (?)")
59            .bind(&author.author_name)
60            .execute(&self.pool)
61            .await?;
62        Ok(r.last_insert_id() as i32)
63    }
64
65    /// 按 ID 查询作者
66    #[tracing::instrument(skip_all, level = "debug")]
67    pub async fn get_author_by_id(&self, id: i32) -> crate::error::ApiResult<Option<Author>> {
68        let row = sqlx::query("SELECT Id, authorName FROM authortb WHERE Id = ?")
69            .bind(id)
70            .fetch_optional(&self.pool)
71            .await?;
72        Ok(row.map(|r| row_to_author(&r)))
73    }
74
75    /// 更新作者
76    #[tracing::instrument(skip_all, level = "debug")]
77    pub async fn update_author(&self, author: &Author) -> crate::error::ApiResult<()> {
78        sqlx::query("UPDATE authortb SET authorName = ? WHERE Id = ?")
79            .bind(&author.author_name)
80            .bind(author.id)
81            .execute(&self.pool)
82            .await?;
83        Ok(())
84    }
85
86    /// 按名称检测作者是否已存在
87    #[tracing::instrument(skip_all, level = "debug")]
88    pub async fn test_author_name(&self, author_name: &str) -> crate::error::ApiResult<Option<Author>> {
89        let row = sqlx::query("SELECT Id, authorName FROM authortb WHERE authorName = ?")
90            .bind(author_name)
91            .fetch_optional(&self.pool)
92            .await?;
93        Ok(row.map(|r| row_to_author(&r)))
94    }
95
96    /// 删除前检查是否绑定漫画
97    #[tracing::instrument(skip_all, level = "debug")]
98    pub async fn get_related_manga(&self, id: i32) -> crate::error::ApiResult<bool> {
99        let row = sqlx::query("SELECT authorId FROM mangaauthor WHERE authorId = ? LIMIT 1")
100            .bind(id)
101            .fetch_optional(&self.pool)
102            .await?;
103        Ok(row.is_some())
104    }
105
106    /// 获取最大作者 ID
107    #[tracing::instrument(skip_all, level = "debug")]
108    pub async fn get_max_author_id(&self) -> crate::error::ApiResult<Option<i32>> {
109        let row = sqlx::query("SELECT MAX(Id) AS maxId FROM authortb")
110            .fetch_one(&self.pool)
111            .await?;
112        Ok(row.try_get("maxId").ok())
113    }
114
115    /// 无条件查询全部作者
116    #[tracing::instrument(skip_all, level = "debug")]
117    pub async fn get_manga_author_list(&self) -> crate::error::ApiResult<Vec<Author>> {
118        let rows = sqlx::query("SELECT Id, authorName FROM authortb")
119            .fetch_all(&self.pool)
120            .await?;
121        Ok(rows.into_iter().map(|r| row_to_author(&r)).collect())
122    }
123
124    /// 查询作者关联的全部漫画
125    #[tracing::instrument(skip_all, level = "debug")]
126    pub async fn get_author_manga(&self, id: i32) -> crate::error::ApiResult<Vec<Manga>> {
127        let rows = sqlx::query(
128            "SELECT DISTINCT m.Id, m.mangaTranName, m.mangaOriName, m.category, m.mangaStatus, \
129             m.image AS imgUrl, m.updateTime \
130             FROM mangatb m \
131             LEFT JOIN mangaauthor ma ON m.Id = ma.mangaId \
132             LEFT JOIN authortb a ON ma.authorId = a.Id \
133             WHERE a.Id = ? \
134             GROUP BY m.Id, m.updateTime \
135             ORDER BY m.updateTime DESC",
136        )
137        .bind(id)
138        .fetch_all(&self.pool)
139        .await?;
140        Ok(rows.into_iter().map(|r| row_to_manga(&r)).collect())
141    }
142}
143
144/// 行映射为作者
145fn row_to_author(row: &sqlx::mysql::MySqlRow) -> Author {
146    Author {
147        id: row.try_get("Id").ok(),
148        author_name: row.try_get("authorName").ok(),
149    }
150}
151
152/// 行映射为漫画
153fn row_to_manga(row: &sqlx::mysql::MySqlRow) -> Manga {
154    use chrono::{DateTime, Utc};
155    Manga {
156        id: row.try_get("Id").ok(),
157        manga_tran_name: row.try_get("mangaTranName").ok(),
158        manga_ori_name: row.try_get("mangaOriName").ok(),
159        category: row.try_get("category").ok(),
160        manga_status: row.try_get("mangaStatus").ok(),
161        img_url: row.try_get("imgUrl").ok(),
162        link: None,
163        introduction: None,
164        update_time: row.try_get::<DateTime<Utc>, _>("updateTime").ok(),
165    }
166}