Skip to main content

tdm_server_rust/entity/
member.rs

1//! 组员相关实体 (Member Entities)
2//!
3//! 定义组员管理的核心数据结构:
4//! - 组员信息 ([`Member`])、列表 VO ([`MemberListVo`])、详情 VO ([`MemberInfoVo`])
5//! - 登录/注册/密码重置请求体
6//! - 岗位 ([`Post`]) 与常驻漫画 ([`StationedManga`])
7//! - 话数接稿/交稿记录 ([`MemberEpisode`], [`MemberEpisodeVo`])
8
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11
12/// 岗位 (Post)
13///
14/// 表示组员的职责角色,如翻译、校对、嵌字等。
15/// 对应 Java `Post` 类。
16///
17/// ## 序列化
18///
19/// JSON 格式: `{"post": 1}`,其中 `1` 对应 `PostEnum` 常量值。
20#[derive(Debug, Clone, Serialize, Deserialize, Default)]
21pub struct Post {
22    /// 岗位 ID
23    pub post: i32,
24}
25
26/// 组员 (Member)
27///
28/// 核心组员实体,映射 `t_member` 表。
29/// 同时用于数据库读写和部分 API 响应。
30///
31/// ## 字段说明
32///
33/// 部分字段(如 `password`、`station_id`)在序列化时会根据上下文跳过,
34/// 避免敏感信息泄露或减少冗余。
35///
36/// ## 与 Java 的映射
37///
38/// 对应 Java `Member` 类,字段名采用 camelCase。
39/// `#[serde(alias = "Id")]` 兼容 Java 端大写 `Id` 字段。
40#[derive(Debug, Clone, Serialize, Deserialize, Default)]
41#[serde(rename_all = "camelCase")]
42pub struct Member {
43    /// 组员 ID
44    #[serde(alias = "Id")]
45    pub id: i32,
46    /// 用户名
47    pub username: Option<String>,
48    /// 密码
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub password: Option<String>,
51    /// 职阶
52    pub intern: i16,
53    /// 邮箱
54    pub email: Option<String>,
55    /// 注册时间
56    pub registration_time: Option<DateTime<Utc>>,
57    /// 最近交稿时间
58    pub last_submit_time: Option<DateTime<Utc>>,
59    /// 岗位列表
60    #[serde(default)]
61    pub posts: Vec<Post>,
62    /// 岗位 ID 列表
63    #[serde(default)]
64    pub post_ids: Vec<i32>,
65    /// 常驻计数
66    #[serde(default)]
67    pub station_count: Option<i32>,
68    /// 常驻记录 ID
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub station_id: Option<i32>,
71    /// 常驻岗位
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub post: Option<i32>,
74    /// 常驻状态
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub status: Option<i16>,
77    /// 漫画 ID(常驻审批列表)
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub manga_id: Option<i32>,
80    /// 漫画译名(常驻审批列表)
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub manga_name: Option<String>,
83}
84
85/// 组员列表 VO(对齐 Java MemberListVO)
86#[derive(Debug, Clone, Serialize, Deserialize)]
87#[serde(rename_all = "camelCase")]
88pub struct MemberListVo {
89    /// 用户名
90    pub username: Option<String>,
91    /// 组员 ID
92    pub id: i32,
93    /// 岗位列表
94    pub posts: Vec<Post>,
95    /// 职阶
96    pub intern: i16,
97    /// 最近交稿时间
98    pub last_submit_time: Option<DateTime<Utc>>,
99    /// 注册时间
100    pub registration_time: Option<DateTime<Utc>>,
101    /// 邮箱
102    pub email: Option<String>,
103    /// 常驻计数
104    pub station_count: Option<i32>,
105}
106
107/// 组员详情 VO(对齐 Java MemberInfoVO)
108#[derive(Debug, Clone, Serialize, Deserialize)]
109#[serde(rename_all = "camelCase")]
110pub struct MemberInfoVo {
111    /// 组员 ID
112    pub id: i32,
113    /// 用户名
114    pub username: Option<String>,
115    /// 岗位列表
116    pub posts: Vec<Post>,
117    /// 岗位 ID 列表
118    pub post_ids: Vec<i32>,
119    /// 职阶
120    pub intern: i16,
121    /// 最近交稿时间
122    pub last_submit_time: Option<DateTime<Utc>>,
123    /// 注册时间
124    pub registration_time: Option<DateTime<Utc>>,
125    /// 邮箱
126    pub email: Option<String>,
127}
128
129/// 组员缓存
130#[derive(Debug, Clone, Serialize, Deserialize)]
131#[serde(rename_all = "camelCase")]
132pub struct MemberCache {
133    /// 组员 ID
134    #[serde(alias = "Id")]
135    pub id: i32,
136    /// 用户名
137    pub username: Option<String>,
138    /// 职阶
139    pub intern: i16,
140    /// 邮箱
141    pub email: Option<String>,
142    /// 岗位 ID 列表
143    pub posts: Vec<i32>,
144}
145
146/// 登录请求体 (Login Request)
147///
148/// 对应 `POST /api/login` 的 JSON body:
149///
150/// ```json
151/// {"username": "gum979", "password": "123456"}
152/// ```
153#[derive(Debug, Deserialize)]
154pub struct MemberLoginRequest {
155    /// 用户名或 QQ 邮箱
156    pub username: String,
157    /// 密码
158    pub password: String,
159}
160
161/// 注册请求
162#[derive(Debug, Deserialize)]
163pub struct Reg {
164    /// ID
165    pub id: Option<i32>,
166    /// 用户名
167    pub username: String,
168    /// 邮箱
169    pub email: String,
170    /// 密码
171    pub password: String,
172    /// 邀请码
173    pub invitationcode: i32,
174}
175
176/// 邀请码
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct InvitationCode {
179    /// ID
180    pub id: Option<i32>,
181    /// 邀请码
182    pub code: i32,
183}
184
185/// 重置密码
186#[derive(Debug, Deserialize)]
187pub struct ResetPassword {
188    /// 组员 ID
189    pub id: i32,
190    /// 旧密码
191    pub old_pwd: String,
192    /// 新密码
193    pub new_pwd: String,
194}
195
196/// 组员接稿/交稿记录 (Member-Episode Assignment)
197///
198/// 记录组员对某一话的岗位分配及时间线:
199/// - `my_setup_time` — 接稿(认领)时间
200/// - `my_update_time` — 交稿(完成)时间
201///
202/// `my_name` 为岗位标识名,如 `"translator"`、`"proofreader"` 等。
203#[derive(Debug, Clone, Deserialize, Serialize)]
204#[serde(rename_all = "camelCase")]
205pub struct MemberEpisode {
206    /// 组员 ID
207    pub member_id: i32,
208    /// 话数 ID
209    pub episode_id: i32,
210    /// 岗位名 translator/proofreader 等
211    pub my_name: String,
212    /// 接稿时间
213    #[serde(skip_serializing_if = "Option::is_none")]
214    pub my_setup_time: Option<DateTime<Utc>>,
215    /// 交稿时间
216    #[serde(skip_serializing_if = "Option::is_none")]
217    pub my_update_time: Option<DateTime<Utc>>,
218}
219
220/// 新增组员请求
221#[derive(Debug, Deserialize)]
222#[serde(rename_all = "camelCase")]
223pub struct MemberAddRequest {
224    /// 用户名
225    pub username: String,
226    /// 密码
227    pub password: String,
228    /// 邮箱
229    pub email: String,
230    /// 是否在实习期,1是,0否
231    pub intern: i16,
232    /// 岗位 ID 列表
233    pub post_ids: Vec<i32>,
234}
235
236/// 更新组员请求(对齐 Java MemberUpdateRequest)
237#[derive(Debug, Deserialize)]
238#[serde(rename_all = "camelCase")]
239pub struct MemberUpdateRequest {
240    /// 组员 ID
241    pub id: i32,
242    /// 用户名
243    pub username: Option<String>,
244    /// 密码
245    pub password: Option<String>,
246    /// 邮箱
247    pub email: Option<String>,
248    /// 职阶
249    pub intern: Option<i16>,
250    /// 岗位 ID 列表
251    pub post_ids: Option<Vec<i32>>,
252    /// 岗位字符串列表
253    pub string_posts: Option<Vec<String>>,
254}
255
256/// 组员自助更新(对齐 Java MemberSelfUpdateRequest)
257#[derive(Debug, Deserialize)]
258#[serde(rename_all = "camelCase")]
259pub struct MemberSelfUpdateRequest {
260    /// 组员 ID
261    pub id: i32,
262    /// 用户名
263    pub username: Option<String>,
264    /// 邮箱
265    pub email: Option<String>,
266}
267
268/// 组员话数视图 (Member Episode View Object)
269///
270/// 用于展示组员参与的所有话数及在各岗位上的进度。
271/// 包含每个岗位的负责人、接稿时间、交稿时间和文件信息。
272///
273/// 对应 Java `MemberEpisodeVO`。
274#[derive(Debug, Clone, Serialize, Deserialize, Default)]
275#[serde(rename_all = "camelCase")]
276pub struct MemberEpisodeVo {
277    /// 话数 ID
278    pub id: i32,
279    /// 漫画 ID
280    pub manga_id: i32,
281    /// 漫画译名
282    pub manga_name: Option<String>,
283    /// 话数标签
284    pub manga_episode: Option<String>,
285    /// 话数名
286    pub manga_episode_name: Option<String>,
287    /// 设立时间
288    pub setup_time: Option<DateTime<Utc>>,
289    /// 更新时间
290    pub update_time: Option<DateTime<Utc>>,
291    /// 图源 ID
292    pub provider_id: Option<i32>,
293    /// 图源组员名
294    pub provider_name: Option<String>,
295    /// 图源接稿时间
296    pub provider_setup_time: Option<DateTime<Utc>>,
297    /// 图源交稿时间
298    pub provider_update_time: Option<DateTime<Utc>>,
299    /// 翻译 ID
300    pub translator_id: Option<i32>,
301    /// 翻译组员名
302    pub translator_name: Option<String>,
303    /// 翻译接稿时间
304    pub translator_setup_time: Option<DateTime<Utc>>,
305    /// 翻译交稿时间
306    pub translator_update_time: Option<DateTime<Utc>>,
307    /// 校对 ID
308    pub proofreader_id: Option<i32>,
309    /// 校对组员名
310    pub proofreader_name: Option<String>,
311    /// 校对接稿时间
312    pub proofreader_setup_time: Option<DateTime<Utc>>,
313    /// 校对交稿时间
314    pub proofreader_update_time: Option<DateTime<Utc>>,
315    /// 嵌字 ID
316    pub letterer_id: Option<i32>,
317    /// 嵌字组员名
318    pub letterer_name: Option<String>,
319    /// 嵌字接稿时间
320    pub letterer_setup_time: Option<DateTime<Utc>>,
321    /// 嵌字交稿时间
322    pub letterer_update_time: Option<DateTime<Utc>>,
323    /// 时轴 ID
324    pub timer_id: Option<i32>,
325    /// 时轴组员名
326    pub timer_name: Option<String>,
327    /// 时轴接稿时间
328    pub timer_setup_time: Option<DateTime<Utc>>,
329    /// 时轴交稿时间
330    pub timer_update_time: Option<DateTime<Utc>>,
331    /// 审稿 ID
332    pub reviewer_id: Option<i32>,
333    /// 审稿组员名
334    pub reviewer_name: Option<String>,
335    /// 审稿接稿时间
336    pub reviewer_setup_time: Option<DateTime<Utc>>,
337    /// 审稿交稿时间
338    pub reviewer_update_time: Option<DateTime<Utc>>,
339    /// 图源 OSS ID
340    pub provider_file_oss_id: Option<i32>,
341    /// 图源文件名
342    pub provider_filename: Option<String>,
343    /// 翻译稿 OSS ID
344    pub translator_file_oss_id: Option<i32>,
345    /// 翻译稿文件名
346    pub translator_filename: Option<String>,
347    /// 校对稿 OSS ID
348    pub proofreader_file_oss_id: Option<i32>,
349    /// 校对稿文件名
350    pub proofreader_filename: Option<String>,
351    /// 嵌字稿 OSS ID
352    pub letterer_file_oss_id: Option<i32>,
353    /// 嵌字稿文件名
354    pub letterer_filename: Option<String>,
355    /// 时轴 OSS ID
356    pub timer_file_oss_id: Option<i32>,
357    /// 时轴文件名
358    pub timer_filename: Option<String>,
359    /// 翻译文件
360    pub translator_file: Option<String>,
361    /// 校对文件
362    pub proofreader_file: Option<String>,
363    /// 时轴文件
364    pub timer_file: Option<String>,
365}
366
367/// 常驻漫画(对齐 Java StationedManga extends Manga)
368#[derive(Debug, Clone, Serialize, Deserialize)]
369#[serde(rename_all = "camelCase")]
370pub struct StationedManga {
371    /// 漫画信息(扁平序列化,对齐 Java Manga 字段)
372    #[serde(flatten)]
373    pub manga: crate::entity::manga::MangaResponse,
374    /// 常驻岗位 ID 列表
375    pub posts: Vec<i32>,
376}
377
378impl From<Member> for MemberListVo {
379    fn from(m: Member) -> Self {
380        Self {
381            username: m.username,
382            id: m.id,
383            posts: m.posts,
384            intern: m.intern,
385            last_submit_time: m.last_submit_time,
386            registration_time: m.registration_time,
387            email: m.email,
388            station_count: m.station_count,
389        }
390    }
391}
392
393impl From<Member> for MemberInfoVo {
394    fn from(m: Member) -> Self {
395        Self {
396            id: m.id,
397            username: m.username,
398            posts: m.posts,
399            post_ids: m.post_ids,
400            intern: m.intern,
401            last_submit_time: m.last_submit_time,
402            registration_time: m.registration_time,
403            email: m.email,
404        }
405    }
406}