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);
|
||||
form.resetFields();
|
||||
fetchData();
|
||||
} catch {
|
||||
message.error(editing ? '更新失败' : '创建失败');
|
||||
} catch (err: unknown) {
|
||||
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>,
|
||||
req: CreatePointsRuleReq,
|
||||
) -> 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 active = points_rule::ActiveModel {
|
||||
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_000161_patient_points_manage_perm;
|
||||
mod m20260522_000162_seed_patient_miniprogram_permissions;
|
||||
mod m20260526_000163_points_rule_unique_event_type;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
@@ -345,6 +346,7 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20260522_000160_article_add_is_public::Migration),
|
||||
Box::new(m20260522_000161_patient_points_manage_perm::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