easy_msr_api/
dto.rs

1//! # 数据传输对象 (DTO)
2//!
3//! 定义了API请求和响应的数据结构。
4//!
5//! 这些结构体用于序列化和反序列化JSON数据,并提供了OpenAPI文档支持。
6//! 所有结构体都实现了`Serialize`、`Deserialize`和`ToSchema` trait。
7
8use serde::{Deserialize, Serialize};
9use utoipa::{IntoParams, ToSchema, schema};
10
11/// 统一的API响应格式
12///
13/// 所有API响应都使用这个统一的格式包装。
14/// 包含状态码、消息和实际数据。
15#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
16pub struct ApiResp<T> {
17    /// 响应状态码
18    ///
19    /// 0表示成功,其他值表示错误
20    #[schema(value_type = i32, example = 0)]
21    pub code: i32,
22
23    /// 响应消息
24    ///
25    /// 成功时为空字符串,错误时包含错误描述
26    #[schema(value_type = String, example = "")]
27    pub msg: String,
28
29    /// 响应数据
30    ///
31    /// 实际的业务数据,类型由泛型参数T决定
32    pub data: T,
33}
34
35impl<T> ApiResp<T> {
36    /// 创建成功的响应
37    ///
38    /// # 参数
39    ///
40    /// * `data` - 要返回的业务数据
41    ///
42    /// # 示例
43    ///
44    /// ```rust
45    /// use easy_msr_api::web::dto::ApiResp;
46    ///
47    /// let resp = ApiResp::success("Hello, World!");
48    /// assert_eq!(resp.code, 0);
49    /// ```
50    pub fn success(data: T) -> Self {
51        Self {
52            code: 0,
53            msg: String::new(),
54            data,
55        }
56    }
57
58    /// 创建错误响应
59    ///
60    /// # 参数
61    ///
62    /// * `msg` - 错误消息
63    ///
64    /// # 示例
65    ///
66    /// ```rust
67    /// use easy_msr_api::web::dto::ApiResp;
68    ///
69    /// let resp: ApiResp<String> = ApiResp::error("参数错误".to_string());
70    /// assert_eq!(resp.code, -1);
71    /// ```
72    pub fn error(msg: String) -> Self
73    where
74        T: Default,
75    {
76        Self {
77            code: -1,
78            msg,
79            data: T::default(),
80        }
81    }
82}
83
84/// 歌曲数据
85///
86/// 包含歌曲的完整信息,包括音频文件URL、歌词URL等。
87#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
88pub struct SongData {
89    /// 歌曲唯一标识符(cid)
90    #[serde(rename = "cid")]
91    #[schema(value_type = String, example = "953953")]
92    pub id: String,
93
94    /// 歌曲名称
95    #[schema(value_type = String, example = "Little Wish")]
96    pub name: String,
97
98    /// 所属专辑cid
99    #[serde(rename = "albumCid")]
100    #[schema(value_type = String, example = "3888")]
101    pub album_id: String,
102
103    /// 音频文件URL
104    #[serde(rename = "sourceUrl")]
105    #[schema(value_type = String, example = "https://res01.hycdn.cn/xxx/xxx.wav")]
106    pub source_url: Option<String>,
107
108    /// 歌词文件URL
109    #[serde(rename = "lyricUrl")]
110    #[schema(value_type = String, example = "https://web.hycdn.cn/siren/lyric/xxx/xxx.lrc")]
111    pub lyric_url: Option<String>,
112
113    /// MV视频URL
114    #[serde(rename = "mvUrl")]
115    pub mv_url: Option<String>,
116
117    /// MV封面URL
118    #[serde(rename = "mvCoverUrl")]
119    pub mv_cover_url: Option<String>,
120
121    /// 艺术家列表
122    pub artists: Vec<String>,
123}
124
125/// 歌曲响应类型
126pub type SongResp = ApiResp<SongData>;
127
128/// 所有歌曲列表单项
129///
130/// 简化版的歌曲信息,用于列表展示。
131#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
132pub struct AllSongsItem {
133    /// 歌曲唯一标识符(cid)
134    #[serde(rename = "cid")]
135    #[schema(value_type = String, example = "953953")]
136    pub id: String,
137
138    /// 歌曲名称
139    #[schema(value_type = String, example = "Little Wish")]
140    pub name: String,
141
142    /// 所属专辑cid
143    #[serde(rename = "albumCid")]
144    #[schema(value_type = String, example = "3888")]
145    pub album_id: String,
146
147    /// 艺术家列表
148    pub artists: Vec<String>,
149}
150
151/// 所有歌曲数据
152#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
153pub struct AllSongsData {
154    /// 歌曲列表
155    pub list: Vec<AllSongsItem>,
156
157    /// 自动播放的歌曲cid
158    #[serde(rename = "autoplay")]
159    #[schema(value_type = String, example = "048794")]
160    pub auto_paly: String,
161}
162
163/// 所有歌曲响应类型
164pub type AllSongsResp = ApiResp<AllSongsData>;
165
166/// 专辑数据
167///
168/// 包含专辑的基本信息。
169#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
170pub struct AlbumData {
171    /// 专辑唯一标识符(cid)
172    #[serde(rename = "cid")]
173    #[schema(value_type = String, example = "3888")]
174    pub id: String,
175
176    /// 专辑名称
177    #[schema(value_type = String, example = "Little Wish")]
178    pub name: String,
179
180    /// 专辑简介
181    #[schema(value_type = String, example = "一触即碎的肥皂泡,也要托起小小愿望,飞越风雨,飞向太阳,绽放她的幻彩流光。")]
182    pub intro: String,
183
184    /// 所属分类
185    #[schema(value_type = String, example = "arknights")]
186    pub belong: String,
187
188    /// 封面图片URL
189    #[serde(rename = "coverUrl")]
190    #[schema(value_type = String, example = "https://web.hycdn.cn/siren/pic/xxx/xxx.jpg")]
191    pub cover_url: String,
192
193    /// 详情页封面URL
194    #[serde(rename = "coverDeUrl")]
195    #[schema(value_type = String, example = "https://web.hycdn.cn/siren/pic/xxx/xxx.jpg")]
196    pub cover_de_url: String,
197
198    /// 艺术家列表(注意:API中拼写为"artistes")
199    #[serde(rename = "artistes")]
200    pub artists: Vec<String>,
201}
202
203/// 专辑响应类型
204pub type AlbumResp = ApiResp<AlbumData>;
205
206/// 专辑详情中的歌曲项
207///
208/// 专辑详情中包含的简化歌曲信息。
209#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
210pub struct AlbumDetailSongItem {
211    /// 歌曲唯一标识符(cid)
212    #[serde(rename = "cid")]
213    #[schema(value_type = String, example = "953953")]
214    pub id: String,
215
216    /// 歌曲名称
217    #[schema(value_type = String, example = "Little Wish")]
218    pub name: String,
219
220    /// 艺术家列表
221    #[serde(rename = "artistes")]
222    pub artists: Vec<String>,
223}
224
225/// 专辑详情数据
226///
227/// 包含专辑的完整信息和歌曲列表。
228#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
229pub struct AlbumDetailData {
230    /// 专辑唯一标识符(cid)
231    #[serde(rename = "cid")]
232    #[schema(value_type = String, example = "953953")]
233    pub id: String,
234
235    /// 专辑名称
236    #[schema(value_type = String, example = "Little Wish")]
237    pub name: String,
238
239    /// 专辑简介
240    #[schema(value_type = String, example = "一触即碎的肥皂泡,也要托起小小愿望,飞越风雨,飞向太阳,绽放她的幻彩流光。")]
241    pub intro: String,
242
243    /// 所属分类
244    #[schema(value_type = String, example = "arknights")]
245    pub belong: String,
246
247    /// 封面图片URL
248    #[serde(rename = "coverUrl")]
249    #[schema(value_type = String, example = "https://web.hycdn.cn/siren/pic/xxx/xxx.jpg")]
250    pub cover_url: String,
251
252    /// 详情页封面URL
253    #[serde(rename = "coverDeUrl")]
254    #[schema(value_type = String, example = "https://web.hycdn.cn/siren/pic/xxx/xxx.jpg")]
255    pub cover_de_url: String,
256
257    /// 专辑中的歌曲列表
258    pub songs: Vec<AlbumDetailSongItem>,
259}
260
261/// 专辑详情响应类型
262pub type AlbumDetailResp = ApiResp<AlbumDetailData>;
263
264/// 所有专辑列表单项
265///
266/// 简化版的专辑信息,用于列表展示。
267#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
268pub struct AllAlbumsItem {
269    /// 专辑唯一标识符(cid)
270    #[serde(rename = "cid")]
271    #[schema(value_type = String, example = "3888")]
272    pub id: String,
273
274    /// 专辑名称
275    #[schema(value_type = String, example = "Little Wish")]
276    pub name: String,
277
278    /// 封面图片URL
279    #[serde(rename = "coverUrl")]
280    #[schema(value_type = String, example = "https://web.hycdn.cn/siren/pic/xxx/xxx.jpg")]
281    pub cover_url: String,
282
283    /// 艺术家列表
284    #[serde(rename = "artistes")]
285    pub artists: Vec<String>,
286}
287
288/// 搜索结果中的专辑项
289///
290/// 搜索结果中的专辑信息。
291#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
292pub struct SearchAlbumItem {
293    /// 专辑唯一标识符(cid)
294    #[serde(rename = "cid")]
295    #[schema(value_type = String, example = "3888")]
296    pub id: String,
297
298    /// 专辑名称
299    #[schema(value_type = String, example = "Little Wish")]
300    pub name: String,
301
302    /// 所属分类
303    #[schema(value_type = String, example = "arknights")]
304    pub belong: String,
305
306    /// 封面图片URL
307    #[serde(rename = "coverUrl")]
308    #[schema(value_type = String, example = "https://web.hycdn.cn/siren/pic/xxx/xxx.jpg")]
309    pub cover_url: String,
310
311    /// 艺术家列表
312    #[serde(rename = "artistes")]
313    pub artists: Vec<String>,
314}
315
316/// 搜索结果中的专辑数据
317#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
318pub struct SearchAlbumData {
319    /// 专辑列表
320    pub list: Vec<SearchAlbumItem>,
321
322    /// 是否已到达列表末尾
323    pub end: bool,
324}
325
326/// 搜索专辑查询参数
327#[derive(Serialize, Deserialize, Debug, IntoParams)]
328pub struct SearchAlbumQuery {
329    /// 搜索关键词
330    pub keyword: String,
331
332    /// 分页参数,从指定cid之后开始获取
333    #[serde(rename = "lastCid")]
334    pub last_cid: Option<String>,
335}
336
337/// 搜索专辑响应类型
338pub type SearchAlbumResp = ApiResp<SearchAlbumData>;
339
340/// 新闻项
341///
342/// 简化版的新闻信息,用于列表展示。
343#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
344pub struct NewsItem {
345    /// 新闻唯一标识符(cid)
346    #[serde(rename = "cid")]
347    pub id: String,
348
349    /// 新闻标题
350    pub title: String,
351
352    /// 分类ID
353    pub cate: i32,
354
355    /// 发布日期
356    #[schema(value_type = String, example = "2022-01-01")]
357    pub date: String,
358}
359
360/// 新闻数据
361#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
362pub struct NewsData {
363    /// 新闻列表
364    pub list: Vec<NewsItem>,
365
366    /// 是否已到达列表末尾
367    pub end: bool,
368}
369
370/// 搜索新闻查询参数
371#[derive(Serialize, Deserialize, Debug, IntoParams)]
372pub struct NewsQuery {
373    /// 搜索关键词
374    pub keyword: String,
375
376    /// 分页参数,从指定cid之后开始获取
377    #[serde(rename = "lastCid")]
378    pub last_cid: Option<String>,
379}
380
381/// 获取所有新闻查询参数
382#[derive(Serialize, Deserialize, Debug, IntoParams)]
383pub struct AllNewsQuery {
384    /// 分页参数,从指定cid之后开始获取
385    #[serde(rename = "lastCid")]
386    pub last_cid: Option<String>,
387}
388
389/// 搜索新闻响应类型
390pub type SearchNewsResp = ApiResp<NewsData>;
391
392/// 搜索查询参数
393#[derive(Serialize, Deserialize, Debug, IntoParams)]
394pub struct SearchQuery {
395    /// 搜索关键词
396    pub keyword: String,
397}
398
399/// 搜索结果数据
400///
401/// 包含专辑和新闻的搜索结果。
402#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
403pub struct SearchData {
404    /// 专辑搜索结果
405    pub albums: SearchAlbumData,
406
407    /// 新闻搜索结果
408    pub news: NewsData,
409}
410
411/// 综合搜索响应类型
412pub type SearchResp = ApiResp<SearchData>;
413
414/// 新闻详情数据
415///
416/// 包含新闻的完整内容。
417#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
418pub struct NewsDetailData {
419    /// 新闻唯一标识符(cid)
420    #[serde(rename = "cid")]
421    pub id: String,
422
423    /// 新闻标题
424    pub title: String,
425
426    /// 分类ID
427    pub cate: i32,
428
429    /// 作者
430    pub author: String,
431
432    /// 新闻内容
433    pub content: String,
434
435    /// 发布日期
436    pub date: String,
437}
438
439/// 新闻详情响应类型
440pub type NewsDetailResp = ApiResp<NewsDetailData>;
441
442/// 字体文件项
443///
444/// 包含不同格式的字体文件URL。
445#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
446pub struct FontItem {
447    /// TrueType字体文件URL
448    pub tt: String,
449
450    /// Embedded OpenType字体文件URL
451    pub eot: String,
452
453    /// SVG字体文件URL
454    pub svg: String,
455
456    /// Web Open Font Format字体文件URL
457    pub woff: String,
458}
459
460/// 字体数据
461///
462/// 包含所有可用的字体配置。
463#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, ToSchema)]
464pub struct FontData {
465    /// 常规无衬线字体
466    #[serde(rename = "Sans-Regular")]
467    pub sans_regular: FontItem,
468
469    /// 粗体无衬线字体
470    #[serde(rename = "Sans-Bold")]
471    pub sans_bold: FontItem,
472}
473
474/// 字体响应类型
475pub type FontResp = ApiResp<FontData>;