Skip to main content

tdm_server_rust/web/
member_controller.rs

1//! 组员管理接口 (Member Controller)
2//!
3//! 组员 CRUD、岗位管理、密码重置、常驻申请。
4//! 对应 Java MemberController。
5
6use crate::utils::query_deserialize::{
7    de_opt_i16, de_opt_i32, de_opt_string, de_page, de_page_size,
8};
9use crate::{
10    common::AppJson,
11    app::AppState,
12    common::{page_bean::PageBean, result::ResultBody},
13    entity::member::{
14        MemberAddRequest, MemberCache, MemberEpisode, MemberEpisodeVo, MemberInfoVo, MemberListVo,
15        MemberSelfUpdateRequest, MemberUpdateRequest, ResetPassword, StationedManga,
16    },
17    error::{ApiResult, AppError},
18    middleware::AuthMember,
19    service::member_service::MemberService,
20};
21use axum::{
22    extract::{Extension, Path, Query, State},
23    routing::{get, patch, put},
24    Router,
25};
26use serde::Deserialize;
27
28/// 组员分页查询参数
29#[derive(Debug, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct MemberPageQuery {
32    /// 页码
33    #[serde(default, deserialize_with = "de_page")]
34    pub page: i32,
35    /// 每页条数
36    #[serde(default, deserialize_with = "de_page_size")]
37    pub page_size: i32,
38    /// 用户名
39    #[serde(default, deserialize_with = "de_opt_string")]
40    pub username: Option<String>,
41    /// 岗位
42    #[serde(default, deserialize_with = "de_opt_i16")]
43    pub post: Option<i16>,
44    /// 职阶
45    #[serde(default, deserialize_with = "de_opt_i16")]
46    pub intern: Option<i16>,
47    /// 邮箱
48    #[serde(default, deserialize_with = "de_opt_string")]
49    pub email: Option<String>,
50}
51
52/// 组员话数分页参数
53#[derive(Debug, Deserialize)]
54#[serde(rename_all = "camelCase")]
55pub struct MemberEpisodePageQuery {
56    /// 页码
57    #[serde(default, deserialize_with = "de_page")]
58    pub page: i32,
59    /// 每页条数
60    #[serde(default, deserialize_with = "de_page_size")]
61    pub page_size: i32,
62    /// 组员 ID
63    #[serde(default, deserialize_with = "de_opt_i32")]
64    pub id: Option<i32>,
65}
66
67/// 组员路由(挂载于 `/api/members`)
68pub fn routes() -> Router<AppState> {
69    Router::new()
70        .route("/", get(page_member).post(new_member).put(update_member))
71        .route("/all", get(get_member_list))
72        .route("/self", put(update_member_self))
73        .route("/updatepwd", patch(update_member_password))
74        .route("/memberepisode", get(page_episode))
75        .route("/takeEpisode", patch(take_episode))
76        .route("/submitEpisode", patch(submit_episode))
77        .route("/stationedMangas/:id", get(find_stationed_mangas))
78        .route("/:id", get(get_member_by_id).delete(delete_member))
79}
80
81/// 分页查询组员
82#[tracing::instrument(skip_all, level = "info")]
83pub async fn page_member(
84    State(state): State<AppState>,
85    Query(q): Query<MemberPageQuery>,
86) -> ApiResult<ResultBody<PageBean<MemberListVo>>> {
87    let data = MemberService::page(
88        &state,
89        q.page,
90        q.page_size,
91        q.username,
92        q.post,
93        q.intern,
94        q.email,
95    )
96    .await?;
97    Ok(ResultBody::success_data(data))
98}
99
100/// 全量组员列表
101#[tracing::instrument(skip_all, level = "info")]
102pub async fn get_member_list(
103    State(state): State<AppState>,
104) -> ApiResult<ResultBody<Vec<MemberCache>>> {
105    let data = MemberService::get_member_list(&state).await?;
106    Ok(ResultBody::success_data(data))
107}
108
109/// 批量删除组员
110#[tracing::instrument(skip_all, level = "info")]
111pub async fn delete_member(
112    State(state): State<AppState>,
113    Path(id): Path<String>,
114) -> ApiResult<ResultBody<()>> {
115    let id_list: Vec<i32> = id
116        .split(',')
117        .filter_map(|s| s.trim().parse().ok())
118        .collect();
119    MemberService::delete(&state, id_list).await?;
120    Ok(ResultBody::success())
121}
122
123/// 新增组员
124#[tracing::instrument(skip_all, level = "info")]
125pub async fn new_member(
126    State(state): State<AppState>,
127    AppJson(body): AppJson<MemberAddRequest>,
128) -> ApiResult<ResultBody<()>> {
129    MemberService::new_member(&state, body).await?;
130    Ok(ResultBody::success())
131}
132
133/// 按 ID 查询组员
134#[tracing::instrument(skip_all, level = "info")]
135pub async fn get_member_by_id(
136    State(state): State<AppState>,
137    Path(id): Path<String>,
138) -> ApiResult<ResultBody<MemberInfoVo>> {
139    let id: i32 = id
140        .parse()
141        .map_err(|_| crate::error::AppError::business("无效的组员 ID 喵"))?;
142    let m = MemberService::get_member_by_id(&state, id).await?;
143    Ok(ResultBody::success_data(MemberInfoVo::from(m)))
144}
145
146/// 更新组员
147#[tracing::instrument(skip_all, level = "info")]
148pub async fn update_member(
149    State(state): State<AppState>,
150    AppJson(body): AppJson<MemberUpdateRequest>,
151) -> ApiResult<ResultBody<()>> {
152    MemberService::update_member(&state, body).await?;
153    Ok(ResultBody::success())
154}
155
156/// 组员自助更新
157#[tracing::instrument(skip_all, level = "info")]
158pub async fn update_member_self(
159    State(state): State<AppState>,
160    AppJson(body): AppJson<MemberSelfUpdateRequest>,
161) -> ApiResult<ResultBody<()>> {
162    MemberService::update_member_self(&state, body).await?;
163    Ok(ResultBody::success())
164}
165
166/// 查询常驻漫画
167#[tracing::instrument(skip_all, level = "info")]
168pub async fn find_stationed_mangas(
169    State(state): State<AppState>,
170    Path(id): Path<String>,
171) -> ApiResult<ResultBody<Vec<StationedManga>>> {
172    let id: i32 = id
173        .parse()
174        .map_err(|_| AppError::business("无效的组员 ID 喵"))?;
175    let data = MemberService::find_stationed_mangas(&state, id).await?;
176    Ok(ResultBody::success_data(data))
177}
178
179/// 修改密码
180#[tracing::instrument(skip_all, level = "info")]
181pub async fn update_member_password(
182    State(state): State<AppState>,
183    AppJson(body): AppJson<ResetPassword>,
184) -> ApiResult<ResultBody<()>> {
185    MemberService::update_member_password(&state, body).await?;
186    Ok(ResultBody::success())
187}
188
189/// 分页查询组员话数
190#[tracing::instrument(skip_all, level = "info")]
191pub async fn page_episode(
192    State(state): State<AppState>,
193    Query(q): Query<MemberEpisodePageQuery>,
194) -> ApiResult<ResultBody<PageBean<MemberEpisodeVo>>> {
195    let id = q.id.ok_or_else(|| AppError::business("无效的组员 ID 喵"))?;
196    let page = MemberService::page_episode(&state, q.page, q.page_size, id).await?;
197    Ok(ResultBody::success_data(page))
198}
199
200/// 接稿
201#[tracing::instrument(skip_all, level = "info")]
202pub async fn take_episode(
203    State(state): State<AppState>,
204    Extension(AuthMember(_member)): Extension<AuthMember>,
205    AppJson(body): AppJson<MemberEpisode>,
206) -> ApiResult<ResultBody<()>> {
207    MemberService::take_episode(&state, body).await?;
208    Ok(ResultBody::success())
209}
210
211/// 交稿
212#[tracing::instrument(skip_all, level = "info")]
213pub async fn submit_episode(
214    State(state): State<AppState>,
215    Extension(AuthMember(_member)): Extension<AuthMember>,
216    AppJson(body): AppJson<MemberEpisode>,
217) -> ApiResult<ResultBody<()>> {
218    MemberService::submit_episode(&state, body).await?;
219    Ok(ResultBody::success())
220}