feat(health): 积分规则查重 — 同租户同事件类型不可重复创建
- 新增迁移 m20260526_000163:points_rule (tenant_id, event_type) 部分唯一索引(排除软删除行) - 后端 create_rule 添加 event_type 查重,重复时返回 400 Validation 错误 - 前端 PointsRuleList 提取后端错误消息展示给用户
This commit is contained in:
@@ -142,8 +142,9 @@ export default function PointsRuleList() {
|
|||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
fetchData();
|
fetchData();
|
||||||
} catch {
|
} catch (err: unknown) {
|
||||||
message.error(editing ? '更新失败' : '创建失败');
|
const apiMsg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message;
|
||||||
|
message.error(apiMsg || (editing ? '更新失败' : '创建失败'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,20 @@ pub async fn create_rule(
|
|||||||
operator_id: Option<Uuid>,
|
operator_id: Option<Uuid>,
|
||||||
req: CreatePointsRuleReq,
|
req: CreatePointsRuleReq,
|
||||||
) -> HealthResult<PointsRuleResp> {
|
) -> HealthResult<PointsRuleResp> {
|
||||||
|
// 查重:同一租户下不允许重复的 event_type
|
||||||
|
let existing = points_rule::Entity::find()
|
||||||
|
.filter(points_rule::Column::TenantId.eq(tenant_id))
|
||||||
|
.filter(points_rule::Column::EventType.eq(&req.event_type))
|
||||||
|
.filter(points_rule::Column::DeletedAt.is_null())
|
||||||
|
.one(&state.db)
|
||||||
|
.await?;
|
||||||
|
if existing.is_some() {
|
||||||
|
return Err(HealthError::Validation(format!(
|
||||||
|
"事件类型 '{}' 已存在规则,不可重复创建",
|
||||||
|
req.event_type
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
let active = points_rule::ActiveModel {
|
let active = points_rule::ActiveModel {
|
||||||
id: Set(Uuid::now_v7()),
|
id: Set(Uuid::now_v7()),
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ mod m20260521_000164_reorganize_menus_scheme_b;
|
|||||||
mod m20260522_000160_article_add_is_public;
|
mod m20260522_000160_article_add_is_public;
|
||||||
mod m20260522_000161_patient_points_manage_perm;
|
mod m20260522_000161_patient_points_manage_perm;
|
||||||
mod m20260522_000162_seed_patient_miniprogram_permissions;
|
mod m20260522_000162_seed_patient_miniprogram_permissions;
|
||||||
|
mod m20260526_000163_points_rule_unique_event_type;
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
@@ -345,6 +346,7 @@ impl MigratorTrait for Migrator {
|
|||||||
Box::new(m20260522_000160_article_add_is_public::Migration),
|
Box::new(m20260522_000160_article_add_is_public::Migration),
|
||||||
Box::new(m20260522_000161_patient_points_manage_perm::Migration),
|
Box::new(m20260522_000161_patient_points_manage_perm::Migration),
|
||||||
Box::new(m20260522_000162_seed_patient_miniprogram_permissions::Migration),
|
Box::new(m20260522_000162_seed_patient_miniprogram_permissions::Migration),
|
||||||
|
Box::new(m20260526_000163_points_rule_unique_event_type::Migration),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
/// 为 points_rule 添加 (tenant_id, event_type) 唯一索引(排除软删除行)
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
let db = manager.get_connection();
|
||||||
|
|
||||||
|
// 删除旧的非唯一索引
|
||||||
|
db.execute_unprepared("DROP INDEX IF EXISTS idx_points_rule_event_type")
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// 创建部分唯一索引(仅对未软删除的行生效)
|
||||||
|
db.execute_unprepared(
|
||||||
|
r#"
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_points_rule_tenant_event
|
||||||
|
ON points_rule (tenant_id, event_type)
|
||||||
|
WHERE deleted_at IS NULL
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
let db = manager.get_connection();
|
||||||
|
|
||||||
|
db.execute_unprepared("DROP INDEX IF EXISTS uq_points_rule_tenant_event")
|
||||||
|
.await?;
|
||||||
|
db.execute_unprepared(
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_points_rule_event_type ON points_rule (event_type)",
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user