feat(health+dialysis): 补全 8 组权限码 + 修复 N+1 查询 + 防御性编码
权限补全: - 新增 14 个权限声明(危急值告警/阈值/随访模板/日常监测/知情同意/用药记录/药物提醒) - 更新 8 个 handler 使用正确的专属权限码 - erp-dialysis 新增 health.dialysis.stats 权限 性能优化: - article_service list_articles 标签加载从 N+1 改为批量查询 - follow_up_template_service 字段计数从 N+1 改为批量 GROUP BY 防御性编码: - alert_engine/article/critical_alert 的 unwrap() 替换为 unwrap_or/expect
This commit is contained in:
@@ -16,7 +16,7 @@ where
|
||||
DialysisState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.dialysis.list")?;
|
||||
require_permission(&ctx, "health.dialysis.stats")?;
|
||||
let dialysis_state = DialysisState::from_ref(&state);
|
||||
let stats = dialysis_stats_service::get_dialysis_statistics(&dialysis_state, ctx.tenant_id).await?;
|
||||
Ok(Json(ApiResponse::ok(stats)))
|
||||
|
||||
@@ -106,6 +106,12 @@ impl ErpModule for DialysisModule {
|
||||
description: "创建、编辑、删除透析处方".into(),
|
||||
module: "erp-dialysis".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.dialysis.stats".into(),
|
||||
name: "查看透析统计".into(),
|
||||
description: "查看透析统计数据".into(),
|
||||
module: "erp-dialysis".into(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.critical-alert.list")?;
|
||||
require_permission(&ctx, "health.critical-alerts.list")?;
|
||||
let page = query.page.unwrap_or(1);
|
||||
let page_size = query.page_size.unwrap_or(20);
|
||||
|
||||
@@ -54,7 +54,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.critical-alert.list")?;
|
||||
require_permission(&ctx, "health.critical-alerts.list")?;
|
||||
let alert = critical_alert_service::get_alert(&state, ctx.tenant_id, id).await?;
|
||||
Ok(axum::Json(ApiResponse::ok(alert)))
|
||||
}
|
||||
@@ -74,7 +74,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.critical-alert.manage")?;
|
||||
require_permission(&ctx, "health.critical-alerts.manage")?;
|
||||
critical_alert_service::acknowledge_alert(
|
||||
&state,
|
||||
ctx.tenant_id,
|
||||
|
||||
@@ -37,7 +37,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.list")?;
|
||||
require_permission(&ctx, "health.critical-value-thresholds.list")?;
|
||||
let list = critical_value_threshold_service::find_thresholds(&state.db, ctx.tenant_id).await?;
|
||||
Ok(Json(ApiResponse::ok(list)))
|
||||
}
|
||||
@@ -51,7 +51,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.manage")?;
|
||||
require_permission(&ctx, "health.critical-value-thresholds.manage")?;
|
||||
let result = critical_value_threshold_service::create_threshold(
|
||||
&state.db,
|
||||
ctx.tenant_id,
|
||||
@@ -78,7 +78,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.manage")?;
|
||||
require_permission(&ctx, "health.critical-value-thresholds.manage")?;
|
||||
let result = critical_value_threshold_service::update_threshold(
|
||||
&state.db,
|
||||
ctx.tenant_id,
|
||||
@@ -104,7 +104,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.manage")?;
|
||||
require_permission(&ctx, "health.critical-value-thresholds.manage")?;
|
||||
critical_value_threshold_service::delete_threshold(&state.db, ctx.tenant_id, id, Some(ctx.user_id))
|
||||
.await?;
|
||||
Ok(Json(ApiResponse::ok(())))
|
||||
|
||||
@@ -36,7 +36,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.list")?;
|
||||
require_permission(&ctx, "health.daily-monitoring.list")?;
|
||||
let page = params.page.unwrap_or(1);
|
||||
let page_size = params.page_size.unwrap_or(20);
|
||||
let result = daily_monitoring_service::list_daily_monitoring(
|
||||
@@ -55,7 +55,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.list")?;
|
||||
require_permission(&ctx, "health.daily-monitoring.list")?;
|
||||
let result = daily_monitoring_service::get_daily_monitoring(
|
||||
&state, ctx.tenant_id, record_id,
|
||||
)
|
||||
@@ -72,7 +72,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.manage")?;
|
||||
require_permission(&ctx, "health.daily-monitoring.manage")?;
|
||||
let mut req = req;
|
||||
req.sanitize();
|
||||
let result = daily_monitoring_service::create_daily_monitoring(
|
||||
@@ -92,7 +92,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.manage")?;
|
||||
require_permission(&ctx, "health.daily-monitoring.manage")?;
|
||||
let mut data = req.data;
|
||||
data.sanitize();
|
||||
let result = daily_monitoring_service::update_daily_monitoring(
|
||||
@@ -112,7 +112,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.manage")?;
|
||||
require_permission(&ctx, "health.daily-monitoring.manage")?;
|
||||
daily_monitoring_service::delete_daily_monitoring(
|
||||
&state, ctx.tenant_id, record_id, Some(ctx.user_id), req.version,
|
||||
)
|
||||
|
||||
@@ -37,7 +37,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.follow-up.list")?;
|
||||
require_permission(&ctx, "health.follow-up-templates.list")?;
|
||||
let page = params.page.unwrap_or(1);
|
||||
let page_size = params.page_size.unwrap_or(20);
|
||||
let result = follow_up_template_service::list_templates(
|
||||
@@ -56,7 +56,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.follow-up.list")?;
|
||||
require_permission(&ctx, "health.follow-up-templates.list")?;
|
||||
let result = follow_up_template_service::get_template(&state, ctx.tenant_id, id).await?;
|
||||
Ok(Json(ApiResponse::ok(result)))
|
||||
}
|
||||
@@ -70,7 +70,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.follow-up.manage")?;
|
||||
require_permission(&ctx, "health.follow-up-templates.manage")?;
|
||||
let mut req = req;
|
||||
req.sanitize();
|
||||
let result = follow_up_template_service::create_template(
|
||||
@@ -90,7 +90,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.follow-up.manage")?;
|
||||
require_permission(&ctx, "health.follow-up-templates.manage")?;
|
||||
let mut data = req.data;
|
||||
data.sanitize();
|
||||
let result = follow_up_template_service::update_template(
|
||||
@@ -110,7 +110,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.follow-up.manage")?;
|
||||
require_permission(&ctx, "health.follow-up-templates.manage")?;
|
||||
follow_up_template_service::delete_template(
|
||||
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version,
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.list")?;
|
||||
require_permission(&ctx, "health.medication-reminders.list")?;
|
||||
let page = params.page.unwrap_or(1);
|
||||
let page_size = params.page_size.unwrap_or(20);
|
||||
let result = medication_reminder_service::list_reminders(
|
||||
@@ -42,7 +42,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.manage")?;
|
||||
require_permission(&ctx, "health.medication-reminders.manage")?;
|
||||
req.sanitize();
|
||||
let result = medication_reminder_service::create_reminder(
|
||||
&state, ctx.tenant_id, Some(ctx.user_id), req.0,
|
||||
@@ -67,7 +67,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.manage")?;
|
||||
require_permission(&ctx, "health.medication-reminders.manage")?;
|
||||
let mut data = req.data;
|
||||
data.sanitize();
|
||||
let result = medication_reminder_service::update_reminder(
|
||||
@@ -91,7 +91,7 @@ where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.health-data.manage")?;
|
||||
require_permission(&ctx, "health.medication-reminders.manage")?;
|
||||
medication_reminder_service::delete_reminder(
|
||||
&state, ctx.tenant_id, id, Some(ctx.user_id), req.version,
|
||||
).await?;
|
||||
|
||||
@@ -526,6 +526,15 @@ impl HealthModule {
|
||||
"/health/admin/points/orders",
|
||||
axum::routing::get(points_handler::admin_list_orders),
|
||||
)
|
||||
// 积分账户 — 管理端按患者查询
|
||||
.route(
|
||||
"/health/admin/points/patients/{patient_id}/account",
|
||||
axum::routing::get(points_handler::admin_get_patient_account),
|
||||
)
|
||||
.route(
|
||||
"/health/admin/points/patients/{patient_id}/transactions",
|
||||
axum::routing::get(points_handler::admin_list_patient_transactions),
|
||||
)
|
||||
// 线下活动 — 管理端
|
||||
.route(
|
||||
"/health/admin/offline-events",
|
||||
@@ -954,6 +963,90 @@ impl ErpModule for HealthModule {
|
||||
description: "创建/编辑/启停告警规则".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.critical-alerts.list".into(),
|
||||
name: "查看危急值告警".into(),
|
||||
description: "查看危急值告警列表和详情".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.critical-alerts.manage".into(),
|
||||
name: "处理危急值告警".into(),
|
||||
description: "确认危急值告警".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.critical-value-thresholds.list".into(),
|
||||
name: "查看危急值阈值".into(),
|
||||
description: "查看危急值阈值配置".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.critical-value-thresholds.manage".into(),
|
||||
name: "管理危急值阈值".into(),
|
||||
description: "创建/编辑/删除危急值阈值配置".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.follow-up-templates.list".into(),
|
||||
name: "查看随访模板".into(),
|
||||
description: "查看随访模板列表和详情".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.follow-up-templates.manage".into(),
|
||||
name: "管理随访模板".into(),
|
||||
description: "创建/编辑/删除随访模板".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.daily-monitoring.list".into(),
|
||||
name: "查看日常监测".into(),
|
||||
description: "查看患者日常监测数据".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.daily-monitoring.manage".into(),
|
||||
name: "管理日常监测".into(),
|
||||
description: "录入/编辑/删除日常监测数据".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.consent.list".into(),
|
||||
name: "查看知情同意".into(),
|
||||
description: "查看患者知情同意记录".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.consent.manage".into(),
|
||||
name: "管理知情同意".into(),
|
||||
description: "签署/撤销知情同意".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.medication-records.list".into(),
|
||||
name: "查看用药记录".into(),
|
||||
description: "查看患者用药记录列表和详情".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.medication-records.manage".into(),
|
||||
name: "管理用药记录".into(),
|
||||
description: "创建/编辑/删除用药记录".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.medication-reminders.list".into(),
|
||||
name: "查看药物提醒".into(),
|
||||
description: "查看患者药物提醒列表".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.medication-reminders.manage".into(),
|
||||
name: "管理药物提醒".into(),
|
||||
description: "创建/编辑/删除药物提醒".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -156,8 +156,8 @@ fn evaluate_trend_in_memory(
|
||||
return false;
|
||||
}
|
||||
|
||||
let first = in_window.first().unwrap().avg_val;
|
||||
let last = in_window.last().unwrap().avg_val;
|
||||
let first = in_window.first().map(|r| r.avg_val).unwrap_or(0.0);
|
||||
let last = in_window.last().map(|r| r.avg_val).unwrap_or(0.0);
|
||||
let actual_delta = last - first;
|
||||
|
||||
match direction {
|
||||
|
||||
@@ -77,9 +77,14 @@ pub async fn list_articles(
|
||||
.await?;
|
||||
|
||||
let total_pages = total.div_ceil(limit.max(1));
|
||||
|
||||
// 批量加载所有文章的标签,避免 N+1 查询
|
||||
let article_ids: Vec<Uuid> = models.iter().map(|m| m.id).collect();
|
||||
let tags_map = batch_load_article_tags(state, &article_ids).await?;
|
||||
|
||||
let mut data = Vec::with_capacity(models.len());
|
||||
for m in models {
|
||||
let tags = load_article_tags(state, m.id).await?;
|
||||
let tags = tags_map.get(&m.id).cloned().unwrap_or_default();
|
||||
data.push(ArticleListItem {
|
||||
id: m.id,
|
||||
title: m.title,
|
||||
@@ -285,7 +290,7 @@ pub async fn increment_view_count(
|
||||
) -> HealthResult<()> {
|
||||
let model = find_article(state, tenant_id, id).await?;
|
||||
let mut active: article::ActiveModel = model.into();
|
||||
active.view_count = Set(active.view_count.unwrap() + 1);
|
||||
active.view_count = Set(active.view_count.take().unwrap_or(0) + 1);
|
||||
active.updated_at = Set(Utc::now());
|
||||
active.update(&state.db).await?;
|
||||
Ok(())
|
||||
@@ -480,6 +485,58 @@ async fn load_article_tags(state: &HealthState, article_id: Uuid) -> HealthResul
|
||||
Ok(tags.into_iter().map(|t| t.name).collect())
|
||||
}
|
||||
|
||||
/// 批量加载多篇文章的标签,避免 N+1 查询。
|
||||
/// 返回 HashMap<article_id, Vec<tag_name>>。
|
||||
async fn batch_load_article_tags(
|
||||
state: &HealthState,
|
||||
article_ids: &[Uuid],
|
||||
) -> HealthResult<std::collections::HashMap<Uuid, Vec<String>>> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
if article_ids.is_empty() {
|
||||
return Ok(HashMap::new());
|
||||
}
|
||||
|
||||
// 1. 一次查询所有文章-标签关联
|
||||
let ids: Vec<Uuid> = article_ids.to_vec();
|
||||
let relations = article_article_tag::Entity::find()
|
||||
.filter(article_article_tag::Column::ArticleId.is_in(ids))
|
||||
.all(&state.db)
|
||||
.await?;
|
||||
|
||||
if relations.is_empty() {
|
||||
return Ok(HashMap::new());
|
||||
}
|
||||
|
||||
// 2. 收集所有 tag_id,按 article_id 分组
|
||||
let tag_ids: Vec<Uuid> = relations.iter().map(|r| r.tag_id).collect();
|
||||
let mut article_to_tag_ids: HashMap<Uuid, Vec<Uuid>> = HashMap::new();
|
||||
for r in &relations {
|
||||
article_to_tag_ids.entry(r.article_id).or_default().push(r.tag_id);
|
||||
}
|
||||
|
||||
// 3. 一次查询所有标签实体
|
||||
let tags = article_tag::Entity::find()
|
||||
.filter(article_tag::Column::Id.is_in(tag_ids))
|
||||
.filter(article_tag::Column::DeletedAt.is_null())
|
||||
.all(&state.db)
|
||||
.await?;
|
||||
|
||||
let tag_name_map: HashMap<Uuid, String> = tags.into_iter().map(|t| (t.id, t.name)).collect();
|
||||
|
||||
// 4. 组装结果
|
||||
let mut result = HashMap::new();
|
||||
for (article_id, tids) in article_to_tag_ids {
|
||||
let names: Vec<String> = tids
|
||||
.into_iter()
|
||||
.filter_map(|tid| tag_name_map.get(&tid).cloned())
|
||||
.collect();
|
||||
result.insert(article_id, names);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn save_article_tags(state: &HealthState, article_id: Uuid, tag_ids: &[Uuid]) -> HealthResult<()> {
|
||||
for tid in tag_ids {
|
||||
let active = article_article_tag::ActiveModel {
|
||||
|
||||
@@ -78,7 +78,7 @@ pub async fn acknowledge_alert(
|
||||
active.acknowledged_at = Set(Some(now));
|
||||
active.updated_at = Set(now);
|
||||
active.updated_by = Set(Some(responder_id));
|
||||
active.version = Set(active.version.unwrap() + 1);
|
||||
active.version = Set(active.version.take().unwrap_or(0) + 1);
|
||||
critical_alert::Entity::update(active)
|
||||
.exec(&state.db)
|
||||
.await?;
|
||||
@@ -133,7 +133,7 @@ pub async fn scan_escalation(
|
||||
active.escalation_level = Set(1);
|
||||
active.status = Set("escalated".to_string());
|
||||
active.updated_at = Set(now);
|
||||
active.version = Set(active.version.unwrap() + 1);
|
||||
active.version = Set(active.version.take().unwrap_or(0) + 1);
|
||||
critical_alert::Entity::update(active)
|
||||
.exec(&state.db)
|
||||
.await?;
|
||||
@@ -158,7 +158,7 @@ pub async fn scan_escalation(
|
||||
let mut active: critical_alert::ActiveModel = alert.clone().into();
|
||||
active.escalation_level = Set(2);
|
||||
active.updated_at = Set(now);
|
||||
active.version = Set(active.version.unwrap() + 1);
|
||||
active.version = Set(active.version.take().unwrap_or(0) + 1);
|
||||
critical_alert::Entity::update(active)
|
||||
.exec(&state.db)
|
||||
.await?;
|
||||
|
||||
@@ -46,13 +46,13 @@ pub async fn list_templates(
|
||||
.all(&state.db)
|
||||
.await?;
|
||||
|
||||
// 批量统计所有模板的字段数,避免 N+1 查询
|
||||
let template_ids: Vec<Uuid> = models.iter().map(|m| m.id).collect();
|
||||
let field_count_map = batch_count_template_fields(state, &template_ids).await?;
|
||||
|
||||
let mut data = Vec::with_capacity(models.len());
|
||||
for m in models {
|
||||
let field_count = follow_up_template_field::Entity::find()
|
||||
.filter(follow_up_template_field::Column::TemplateId.eq(m.id))
|
||||
.filter(follow_up_template_field::Column::DeletedAt.is_null())
|
||||
.count(&state.db)
|
||||
.await?;
|
||||
let field_count = field_count_map.get(&m.id).copied().unwrap_or(0);
|
||||
data.push(FollowUpTemplateListItemResp {
|
||||
id: m.id,
|
||||
name: m.name,
|
||||
@@ -344,3 +344,30 @@ fn validate_field_type(val: &str) -> HealthResult<()> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 批量统计模板字段数量,避免 N+1 查询。
|
||||
/// 返回 HashMap<template_id, count>。
|
||||
async fn batch_count_template_fields(
|
||||
state: &HealthState,
|
||||
template_ids: &[Uuid],
|
||||
) -> HealthResult<std::collections::HashMap<Uuid, u64>> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
if template_ids.is_empty() {
|
||||
return Ok(HashMap::new());
|
||||
}
|
||||
|
||||
let ids: Vec<Uuid> = template_ids.to_vec();
|
||||
let fields = follow_up_template_field::Entity::find()
|
||||
.filter(follow_up_template_field::Column::TemplateId.is_in(ids))
|
||||
.filter(follow_up_template_field::Column::DeletedAt.is_null())
|
||||
.all(&state.db)
|
||||
.await?;
|
||||
|
||||
let mut counts: HashMap<Uuid, u64> = HashMap::new();
|
||||
for f in fields {
|
||||
*counts.entry(f.template_id).or_insert(0) += 1;
|
||||
}
|
||||
|
||||
Ok(counts)
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ pub async fn earn_points(
|
||||
// 3. 检查每日上限(用 account.id 而非 patient_id)
|
||||
if rule.daily_cap > 0 {
|
||||
let today = Utc::now().date_naive();
|
||||
let today_start = today.and_hms_opt(0, 0, 0).unwrap().and_utc();
|
||||
let today_start = today.and_hms_opt(0, 0, 0).expect("00:00:00 is always a valid time").and_utc();
|
||||
let earned_today: i32 = points_transaction::Entity::find()
|
||||
.filter(points_transaction::Column::TenantId.eq(tenant_id))
|
||||
.filter(points_transaction::Column::AccountId.eq(acc.id))
|
||||
@@ -322,7 +322,7 @@ async fn earn_points_in_txn<C: sea_orm::ConnectionTrait>(
|
||||
// 3. 检查每日上限
|
||||
if rule.daily_cap > 0 {
|
||||
let today = Utc::now().date_naive();
|
||||
let today_start = today.and_hms_opt(0, 0, 0).unwrap().and_utc();
|
||||
let today_start = today.and_hms_opt(0, 0, 0).expect("00:00:00 is always a valid time").and_utc();
|
||||
let earned_today: i32 = points_transaction::Entity::find()
|
||||
.filter(points_transaction::Column::TenantId.eq(tenant_id))
|
||||
.filter(points_transaction::Column::AccountId.eq(acc.id))
|
||||
|
||||
Reference in New Issue
Block a user