fix(diary): 为所有 DTO 添加 Validate derive + handler 调用 validate()
DTO 验证规则: - CreateJournalReq: title 1-200, tags ≤20 - UpdateJournalReq: title 1-200, tags ≤20 - CreateClassReq: name 1-50, school_name ≤100 - JoinClassReq: class_code = 6位 - UpdateClassReq: name 1-50, school_name ≤100 - SyncReq: changes ≤100 条 - CreateTopicReq: title 1-200, description ≤2000 - UpdateTopicReq: title 1-200, description ≤2000 - CreateCommentReq: content 1-1000 - CreateStickerPackReq: name 1-50, description ≤500 - UpdateStickerPackReq: name 1-50, description ≤500 - CreateStickerReq: name 1-30, image_url 1-500 - BindChildReq/DeleteChildDataReq: Validate derive (Uuid 已由 serde 验证) Handler 调用: validate() 放在 require_permission() 之前(先验证输入再检查权限) 审计 ID: 5a-C01, 5a-C02, 5a-C03
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
/// 日记心情枚举
|
/// 日记心情枚举
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||||
@@ -26,12 +27,14 @@ pub enum Weather {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 创建日记请求
|
/// 创建日记请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct CreateJournalReq {
|
pub struct CreateJournalReq {
|
||||||
|
#[validate(length(min = 1, max = 200, message = "标题长度 1-200 字符"))]
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub date: chrono::NaiveDate,
|
pub date: chrono::NaiveDate,
|
||||||
pub mood: Mood,
|
pub mood: Mood,
|
||||||
pub weather: Weather,
|
pub weather: Weather,
|
||||||
|
#[validate(length(max = 20, message = "标签最多 20 个"))]
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
pub is_private: bool,
|
pub is_private: bool,
|
||||||
pub class_id: Option<uuid::Uuid>,
|
pub class_id: Option<uuid::Uuid>,
|
||||||
@@ -39,11 +42,13 @@ pub struct CreateJournalReq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 更新日记请求
|
/// 更新日记请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct UpdateJournalReq {
|
pub struct UpdateJournalReq {
|
||||||
|
#[validate(length(min = 1, max = 200, message = "标题长度 1-200 字符"))]
|
||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
pub mood: Option<Mood>,
|
pub mood: Option<Mood>,
|
||||||
pub weather: Option<Weather>,
|
pub weather: Option<Weather>,
|
||||||
|
#[validate(length(max = 20, message = "标签最多 20 个"))]
|
||||||
pub tags: Option<Vec<String>>,
|
pub tags: Option<Vec<String>>,
|
||||||
pub is_private: Option<bool>,
|
pub is_private: Option<bool>,
|
||||||
pub shared_to_class: Option<bool>,
|
pub shared_to_class: Option<bool>,
|
||||||
@@ -69,24 +74,29 @@ pub struct JournalResp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 创建班级请求
|
/// 创建班级请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct CreateClassReq {
|
pub struct CreateClassReq {
|
||||||
|
#[validate(length(min = 1, max = 50, message = "班级名称长度 1-50 字符"))]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
#[validate(length(max = 100, message = "学校名称最长 100 字符"))]
|
||||||
pub school_name: Option<String>,
|
pub school_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 加入班级请求
|
/// 加入班级请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct JoinClassReq {
|
pub struct JoinClassReq {
|
||||||
|
#[validate(length(min = 6, max = 6, message = "班级码必须为 6 位"))]
|
||||||
pub class_code: String,
|
pub class_code: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 更新班级请求
|
/// 更新班级请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct UpdateClassReq {
|
pub struct UpdateClassReq {
|
||||||
/// 班级名称
|
/// 班级名称
|
||||||
|
#[validate(length(min = 1, max = 50, message = "班级名称长度 1-50 字符"))]
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
/// 学校名称
|
/// 学校名称
|
||||||
|
#[validate(length(max = 100, message = "学校名称最长 100 字符"))]
|
||||||
pub school_name: Option<String>,
|
pub school_name: Option<String>,
|
||||||
/// 乐观锁版本号
|
/// 乐观锁版本号
|
||||||
pub version: i32,
|
pub version: i32,
|
||||||
@@ -114,9 +124,10 @@ pub struct ClassResp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 同步请求
|
/// 同步请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct SyncReq {
|
pub struct SyncReq {
|
||||||
pub last_sync_time: Option<chrono::DateTime<chrono::Utc>>,
|
pub last_sync_time: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
|
#[validate(length(max = 100, message = "单次同步最多 100 条变更"))]
|
||||||
pub changes: Vec<SyncChange>,
|
pub changes: Vec<SyncChange>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,11 +169,13 @@ pub struct ClassMemberResp {
|
|||||||
// ========== 主题布置 ==========
|
// ========== 主题布置 ==========
|
||||||
|
|
||||||
/// 创建主题请求
|
/// 创建主题请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct CreateTopicReq {
|
pub struct CreateTopicReq {
|
||||||
/// 主题标题
|
/// 主题标题
|
||||||
|
#[validate(length(min = 1, max = 200, message = "主题标题长度 1-200 字符"))]
|
||||||
pub title: String,
|
pub title: String,
|
||||||
/// 主题描述/要求
|
/// 主题描述/要求
|
||||||
|
#[validate(length(max = 2000, message = "主题描述最长 2000 字符"))]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// 截止日期
|
/// 截止日期
|
||||||
pub due_date: Option<chrono::NaiveDate>,
|
pub due_date: Option<chrono::NaiveDate>,
|
||||||
@@ -183,18 +196,21 @@ pub struct TopicResp {
|
|||||||
// ========== 评语 ==========
|
// ========== 评语 ==========
|
||||||
|
|
||||||
/// 创建评语请求
|
/// 创建评语请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct CreateCommentReq {
|
pub struct CreateCommentReq {
|
||||||
/// 评语内容
|
/// 评语内容
|
||||||
|
#[validate(length(min = 1, max = 1000, message = "评语长度 1-1000 字符"))]
|
||||||
pub content: String,
|
pub content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 更新主题请求
|
/// 更新主题请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct UpdateTopicReq {
|
pub struct UpdateTopicReq {
|
||||||
/// 主题标题
|
/// 主题标题
|
||||||
|
#[validate(length(min = 1, max = 200, message = "主题标题长度 1-200 字符"))]
|
||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
/// 主题描述
|
/// 主题描述
|
||||||
|
#[validate(length(max = 2000, message = "主题描述最长 2000 字符"))]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// 截止日期
|
/// 截止日期
|
||||||
pub due_date: Option<chrono::NaiveDate>,
|
pub due_date: Option<chrono::NaiveDate>,
|
||||||
@@ -203,11 +219,13 @@ pub struct UpdateTopicReq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 创建贴纸包请求
|
/// 创建贴纸包请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct CreateStickerPackReq {
|
pub struct CreateStickerPackReq {
|
||||||
/// 贴纸包名称
|
/// 贴纸包名称
|
||||||
|
#[validate(length(min = 1, max = 50, message = "贴纸包名称长度 1-50 字符"))]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// 描述
|
/// 描述
|
||||||
|
#[validate(length(max = 500, message = "描述最长 500 字符"))]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// 缩略图 URL
|
/// 缩略图 URL
|
||||||
pub thumbnail_url: Option<String>,
|
pub thumbnail_url: Option<String>,
|
||||||
@@ -218,17 +236,20 @@ pub struct CreateStickerPackReq {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub price: i32,
|
pub price: i32,
|
||||||
/// 分类
|
/// 分类
|
||||||
|
#[validate(length(max = 30, message = "分类最长 30 字符"))]
|
||||||
pub category: Option<String>,
|
pub category: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_true() -> bool { true }
|
fn default_true() -> bool { true }
|
||||||
|
|
||||||
/// 更新贴纸包请求 — 所有字段可选,仅更新传入的字段
|
/// 更新贴纸包请求 — 所有字段可选,仅更新传入的字段
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct UpdateStickerPackReq {
|
pub struct UpdateStickerPackReq {
|
||||||
/// 贴纸包名称
|
/// 贴纸包名称
|
||||||
|
#[validate(length(min = 1, max = 50, message = "贴纸包名称长度 1-50 字符"))]
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
/// 描述
|
/// 描述
|
||||||
|
#[validate(length(max = 500, message = "描述最长 500 字符"))]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// 缩略图 URL
|
/// 缩略图 URL
|
||||||
pub thumbnail_url: Option<String>,
|
pub thumbnail_url: Option<String>,
|
||||||
@@ -237,17 +258,21 @@ pub struct UpdateStickerPackReq {
|
|||||||
/// 价格(积分)
|
/// 价格(积分)
|
||||||
pub price: Option<i32>,
|
pub price: Option<i32>,
|
||||||
/// 分类
|
/// 分类
|
||||||
|
#[validate(length(max = 30, message = "分类最长 30 字符"))]
|
||||||
pub category: Option<String>,
|
pub category: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 创建贴纸请求
|
/// 创建贴纸请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct CreateStickerReq {
|
pub struct CreateStickerReq {
|
||||||
/// 贴纸名称
|
/// 贴纸名称
|
||||||
|
#[validate(length(min = 1, max = 30, message = "贴纸名称长度 1-30 字符"))]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// 图片 URL
|
/// 图片 URL
|
||||||
|
#[validate(length(min = 1, max = 500, message = "图片 URL 长度 1-500 字符"))]
|
||||||
pub image_url: String,
|
pub image_url: String,
|
||||||
/// 分类
|
/// 分类
|
||||||
|
#[validate(length(max = 30, message = "分类最长 30 字符"))]
|
||||||
pub category: Option<String>,
|
pub category: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use axum::extract::{Extension, FromRef, Path, State};
|
use axum::extract::{Extension, FromRef, Path, State};
|
||||||
use axum::response::Json;
|
use axum::response::Json;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
use erp_core::error::AppError;
|
use erp_core::error::AppError;
|
||||||
use erp_core::rbac::require_permission;
|
use erp_core::rbac::require_permission;
|
||||||
@@ -37,6 +38,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.class.manage")?;
|
require_permission(&ctx, "diary.class.manage")?;
|
||||||
|
|
||||||
if req.name.trim().is_empty() {
|
if req.name.trim().is_empty() {
|
||||||
@@ -81,6 +83,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.journal.create")?;
|
require_permission(&ctx, "diary.journal.create")?;
|
||||||
|
|
||||||
if req.class_code.trim().is_empty() {
|
if req.class_code.trim().is_empty() {
|
||||||
@@ -248,6 +251,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.class.manage")?;
|
require_permission(&ctx, "diary.class.manage")?;
|
||||||
|
|
||||||
if let Some(ref name) = req.name {
|
if let Some(ref name) = req.name {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use axum::extract::{Extension, FromRef, Path, State};
|
use axum::extract::{Extension, FromRef, Path, State};
|
||||||
use axum::response::Json;
|
use axum::response::Json;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
use erp_core::error::AppError;
|
use erp_core::error::AppError;
|
||||||
use erp_core::rbac::require_permission;
|
use erp_core::rbac::require_permission;
|
||||||
@@ -41,6 +42,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.comment.write")?;
|
require_permission(&ctx, "diary.comment.write")?;
|
||||||
|
|
||||||
if req.content.trim().is_empty() {
|
if req.content.trim().is_empty() {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use axum::response::Json;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use utoipa::IntoParams;
|
use utoipa::IntoParams;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
use erp_core::error::AppError;
|
use erp_core::error::AppError;
|
||||||
use erp_core::rbac::require_permission;
|
use erp_core::rbac::require_permission;
|
||||||
@@ -58,6 +59,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.journal.create")?;
|
require_permission(&ctx, "diary.journal.create")?;
|
||||||
|
|
||||||
// 基础验证
|
// 基础验证
|
||||||
@@ -145,6 +147,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.journal.update")?;
|
require_permission(&ctx, "diary.journal.update")?;
|
||||||
|
|
||||||
let resp = JournalService::update(
|
let resp = JournalService::update(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use axum::response::Json;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use utoipa::{IntoParams, ToSchema};
|
use utoipa::{IntoParams, ToSchema};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
use erp_core::error::AppError;
|
use erp_core::error::AppError;
|
||||||
use erp_core::rbac::require_permission;
|
use erp_core::rbac::require_permission;
|
||||||
@@ -17,7 +18,7 @@ use crate::state::DiaryState;
|
|||||||
// ---- 请求/响应 DTO ----
|
// ---- 请求/响应 DTO ----
|
||||||
|
|
||||||
/// 绑定孩子请求
|
/// 绑定孩子请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct BindChildReq {
|
pub struct BindChildReq {
|
||||||
/// 孩子的用户 ID
|
/// 孩子的用户 ID
|
||||||
pub child_id: Uuid,
|
pub child_id: Uuid,
|
||||||
@@ -42,7 +43,7 @@ pub struct ExportQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 删除孩子数据请求
|
/// 删除孩子数据请求
|
||||||
#[derive(Debug, Deserialize, ToSchema)]
|
#[derive(Debug, Deserialize, Validate, ToSchema)]
|
||||||
pub struct DeleteChildDataReq {
|
pub struct DeleteChildDataReq {
|
||||||
/// 孩子的用户 ID
|
/// 孩子的用户 ID
|
||||||
pub child_id: Uuid,
|
pub child_id: Uuid,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use axum::response::Json;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use utoipa::IntoParams;
|
use utoipa::IntoParams;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
use erp_core::error::AppError;
|
use erp_core::error::AppError;
|
||||||
use erp_core::rbac::require_permission;
|
use erp_core::rbac::require_permission;
|
||||||
@@ -110,6 +111,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.class.manage")?;
|
require_permission(&ctx, "diary.class.manage")?;
|
||||||
|
|
||||||
if req.name.trim().is_empty() {
|
if req.name.trim().is_empty() {
|
||||||
@@ -155,6 +157,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.class.manage")?;
|
require_permission(&ctx, "diary.class.manage")?;
|
||||||
|
|
||||||
if let Some(ref name) = req.name {
|
if let Some(ref name) = req.name {
|
||||||
@@ -235,6 +238,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.class.manage")?;
|
require_permission(&ctx, "diary.class.manage")?;
|
||||||
|
|
||||||
if req.name.trim().is_empty() {
|
if req.name.trim().is_empty() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use axum::extract::{Extension, FromRef, State};
|
use axum::extract::{Extension, FromRef, State};
|
||||||
use axum::response::Json;
|
use axum::response::Json;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
use erp_core::error::AppError;
|
use erp_core::error::AppError;
|
||||||
use erp_core::rbac::require_permission;
|
use erp_core::rbac::require_permission;
|
||||||
@@ -38,6 +39,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.journal.read")?;
|
require_permission(&ctx, "diary.journal.read")?;
|
||||||
|
|
||||||
let resp = SyncService::sync(
|
let resp = SyncService::sync(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use axum::extract::{Extension, FromRef, Path, State};
|
use axum::extract::{Extension, FromRef, Path, State};
|
||||||
use axum::response::Json;
|
use axum::response::Json;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
use erp_core::error::AppError;
|
use erp_core::error::AppError;
|
||||||
use erp_core::rbac::require_permission;
|
use erp_core::rbac::require_permission;
|
||||||
@@ -40,6 +41,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.topic.assign")?;
|
require_permission(&ctx, "diary.topic.assign")?;
|
||||||
|
|
||||||
if req.title.trim().is_empty() {
|
if req.title.trim().is_empty() {
|
||||||
@@ -118,6 +120,7 @@ where
|
|||||||
DiaryState: FromRef<S>,
|
DiaryState: FromRef<S>,
|
||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
|
req.validate().map_err(|e| AppError::Validation(e.to_string()))?;
|
||||||
require_permission(&ctx, "diary.topic.assign")?;
|
require_permission(&ctx, "diary.topic.assign")?;
|
||||||
|
|
||||||
if let Some(ref title) = req.title {
|
if let Some(ref title) = req.title {
|
||||||
|
|||||||
Reference in New Issue
Block a user