From 45530616eec3f6e7a96e82f799cce4cb0c50c56c Mon Sep 17 00:00:00 2001 From: iven Date: Tue, 2 Jun 2026 23:54:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(diary):=20=E6=B7=BB=E5=8A=A0=E8=B4=B4?= =?UTF-8?q?=E7=BA=B8=E5=8C=85=20UpdateStickerPackReq=20DTO=20+=20update=20?= =?UTF-8?q?service/handler=20=E2=80=94=20Task=2013?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/erp-diary/src/dto.rs | 17 +++++ .../erp-diary/src/handler/sticker_handler.rs | 50 ++++++++++++++- crates/erp-diary/src/lib.rs | 3 +- .../erp-diary/src/service/sticker_service.rs | 63 ++++++++++++++++++- 4 files changed, 130 insertions(+), 3 deletions(-) diff --git a/crates/erp-diary/src/dto.rs b/crates/erp-diary/src/dto.rs index 244c2a9..9131076 100644 --- a/crates/erp-diary/src/dto.rs +++ b/crates/erp-diary/src/dto.rs @@ -223,6 +223,23 @@ pub struct CreateStickerPackReq { fn default_true() -> bool { true } +/// 更新贴纸包请求 — 所有字段可选,仅更新传入的字段 +#[derive(Debug, Deserialize, ToSchema)] +pub struct UpdateStickerPackReq { + /// 贴纸包名称 + pub name: Option, + /// 描述 + pub description: Option, + /// 缩略图 URL + pub thumbnail_url: Option, + /// 是否免费 + pub is_free: Option, + /// 价格(积分) + pub price: Option, + /// 分类 + pub category: Option, +} + /// 创建贴纸请求 #[derive(Debug, Deserialize, ToSchema)] pub struct CreateStickerReq { diff --git a/crates/erp-diary/src/handler/sticker_handler.rs b/crates/erp-diary/src/handler/sticker_handler.rs index 1af44a7..e53c562 100644 --- a/crates/erp-diary/src/handler/sticker_handler.rs +++ b/crates/erp-diary/src/handler/sticker_handler.rs @@ -10,7 +10,7 @@ use erp_core::error::AppError; use erp_core::rbac::require_permission; use erp_core::types::{ApiResponse, TenantContext}; -use crate::dto::{CreateStickerPackReq, CreateStickerReq, StickerPackResp, StickerResp, TemplateResp}; +use crate::dto::{CreateStickerPackReq, CreateStickerReq, StickerPackResp, StickerResp, TemplateResp, UpdateStickerPackReq}; use crate::service::sticker_service::StickerService; use crate::state::DiaryState; @@ -127,6 +127,54 @@ where Ok(Json(ApiResponse::ok(resp))) } +#[utoipa::path( + put, + path = "/api/v1/diary/sticker-packs/{pack_id}", + params(("pack_id" = Uuid, Path, description = "贴纸包ID")), + request_body = UpdateStickerPackReq, + responses( + (status = 200, description = "更新成功", body = ApiResponse), + (status = 400, description = "验证失败"), + (status = 401, description = "未授权"), + (status = 403, description = "权限不足"), + (status = 404, description = "贴纸包不存在"), + ), + security(("bearer_auth" = [])), + tag = "贴纸管理" +)] +/// PUT /api/v1/diary/sticker-packs/:pack_id +/// +/// 更新贴纸包(部分更新)。需要 `diary.class.manage` 权限。 +pub async fn update_sticker_pack( + State(state): State, + Extension(ctx): Extension, + Path(pack_id): Path, + Json(req): Json, +) -> Result>, AppError> +where + DiaryState: FromRef, + S: Clone + Send + Sync + 'static, +{ + require_permission(&ctx, "diary.class.manage")?; + + if let Some(ref name) = req.name { + if name.trim().is_empty() { + return Err(AppError::Validation("贴纸包名称不能为空".to_string())); + } + } + + let resp = StickerService::update_sticker_pack( + ctx.tenant_id, + pack_id, + ctx.user_id, + &req, + &state.db, + ) + .await?; + + Ok(Json(ApiResponse::ok(resp))) +} + #[utoipa::path( delete, path = "/api/v1/diary/sticker-packs/{pack_id}", diff --git a/crates/erp-diary/src/lib.rs b/crates/erp-diary/src/lib.rs index 26b08d6..e56e034 100644 --- a/crates/erp-diary/src/lib.rs +++ b/crates/erp-diary/src/lib.rs @@ -199,7 +199,8 @@ impl DiaryModule { ) .route( "/diary/sticker-packs/{pack_id}", - axum::routing::delete(sticker_handler::delete_sticker_pack), + axum::routing::put(sticker_handler::update_sticker_pack) + .delete(sticker_handler::delete_sticker_pack), ) .route( "/diary/sticker-packs/{pack_id}/stickers", diff --git a/crates/erp-diary/src/service/sticker_service.rs b/crates/erp-diary/src/service/sticker_service.rs index 352524d..7f4a8ab 100644 --- a/crates/erp-diary/src/service/sticker_service.rs +++ b/crates/erp-diary/src/service/sticker_service.rs @@ -6,7 +6,7 @@ use sea_orm::{ }; use uuid::Uuid; -use crate::dto::{CreateStickerPackReq, CreateStickerReq, StickerPackResp, StickerResp, TemplateResp}; +use crate::dto::{CreateStickerPackReq, CreateStickerReq, StickerPackResp, StickerResp, TemplateResp, UpdateStickerPackReq}; use crate::entity::{sticker, sticker_pack, template}; use crate::error::{DiaryError, DiaryResult}; @@ -192,6 +192,67 @@ impl StickerService { }) } + /// 更新贴纸包(部分更新,仅修改传入的字段) + pub async fn update_sticker_pack( + tenant_id: Uuid, + pack_id: Uuid, + user_id: Uuid, + req: &UpdateStickerPackReq, + db: &DatabaseConnection, + ) -> DiaryResult { + let model = sticker_pack::Entity::find() + .filter(sticker_pack::Column::Id.eq(pack_id)) + .filter(sticker_pack::Column::TenantId.eq(tenant_id)) + .filter(sticker_pack::Column::DeletedAt.is_null()) + .one(db) + .await? + .ok_or_else(|| DiaryError::NotFound(format!("贴纸包 {} 不存在", pack_id)))?; + + let now = chrono::Utc::now(); + let mut active: sticker_pack::ActiveModel = model.into(); + + if let Some(ref name) = req.name { + active.name = Set(name.clone()); + } + if let Some(ref description) = req.description { + active.description = Set(Some(description.clone())); + } + if let Some(ref thumbnail_url) = req.thumbnail_url { + active.thumbnail_url = Set(Some(thumbnail_url.clone())); + } + if let Some(is_free) = req.is_free { + active.is_free = Set(is_free); + } + if let Some(price) = req.price { + active.price = Set(price); + } + if let Some(ref category) = req.category { + active.category = Set(Some(category.clone())); + } + + active.updated_at = Set(now); + active.updated_by = Set(user_id); + + let updated = active.update(db).await?; + + // 查询贴纸数量 + let sticker_count = sticker::Entity::find() + .filter(sticker::Column::PackId.eq(updated.id)) + .filter(sticker::Column::DeletedAt.is_null()) + .count(db) + .await? as i32; + + Ok(StickerPackResp { + id: updated.id, + name: updated.name, + description: updated.description, + cover_image_url: updated.thumbnail_url, + sticker_count, + is_free: updated.is_free, + category: updated.category, + }) + } + /// 删除贴纸包(软删除) pub async fn delete_sticker_pack( tenant_id: Uuid,