diff --git a/Cargo.lock b/Cargo.lock index cd19d73..f715acf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1697,7 +1697,6 @@ dependencies = [ "erp-health", "erp-message", "erp-plugin", - "erp-points", "erp-server-migration", "erp-workflow", "metrics", diff --git a/apps/miniprogram/config/index.ts b/apps/miniprogram/config/index.ts index 6182532..0834fa1 100644 --- a/apps/miniprogram/config/index.ts +++ b/apps/miniprogram/config/index.ts @@ -14,6 +14,11 @@ export default defineConfig(async (merge) => { 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 'process.env.TARO_APP_API_URL': JSON.stringify(process.env.TARO_APP_API_URL || 'http://localhost:3000/api/v1'), 'process.env.TARO_APP_ENCRYPTION_KEY': JSON.stringify(process.env.TARO_APP_ENCRYPTION_KEY || ''), + 'process.env.TARO_APP_WX_TEMPLATE_APPOINTMENT': JSON.stringify(process.env.TARO_APP_WX_TEMPLATE_APPOINTMENT || ''), + 'process.env.TARO_APP_WX_TEMPLATE_FOLLOWUP': JSON.stringify(process.env.TARO_APP_WX_TEMPLATE_FOLLOWUP || ''), + 'process.env.TARO_APP_WX_TEMPLATE_REPORT': JSON.stringify(process.env.TARO_APP_WX_TEMPLATE_REPORT || ''), + 'process.env.TARO_APP_WX_TEMPLATE_CRITICAL_ALERT': JSON.stringify(process.env.TARO_APP_WX_TEMPLATE_CRITICAL_ALERT || ''), + 'process.env.TARO_APP_WX_TEMPLATE_HEALTH_ABNORMAL': JSON.stringify(process.env.TARO_APP_WX_TEMPLATE_HEALTH_ABNORMAL || ''), }, copy: { patterns: [], options: {} }, framework: 'react', diff --git a/apps/miniprogram/src/pages/mall/index.tsx b/apps/miniprogram/src/pages/mall/index.tsx index 85c8249..d3f1195 100644 --- a/apps/miniprogram/src/pages/mall/index.tsx +++ b/apps/miniprogram/src/pages/mall/index.tsx @@ -22,7 +22,7 @@ const TYPE_BG: Record = { }; export default function Mall() { - const { currentPatient } = useAuthStore(); + const { currentPatient, loadPatients } = useAuthStore(); const { account, checkinStatus, refresh: refreshPoints, doCheckin } = usePointsStore(); const [products, setProducts] = useState([]); const [productType, setProductType] = useState(''); @@ -66,13 +66,18 @@ export default function Mall() { async (type?: string) => { const t = type !== undefined ? type : productType; if (!currentPatient) { - setNoProfile(true); - return; + // 先尝试从服务端加载患者列表 + await loadPatients(); + const updated = useAuthStore.getState().currentPatient; + if (!updated) { + setNoProfile(true); + return; + } } setNoProfile(false); await Promise.all([refreshPoints(), fetchProducts(1, t, true)]); }, - [currentPatient, refreshPoints, fetchProducts, productType], + [currentPatient, loadPatients, refreshPoints, fetchProducts, productType], ); useDidShow(() => { diff --git a/apps/miniprogram/src/services/wechat-templates.ts b/apps/miniprogram/src/services/wechat-templates.ts index d3d963d..bd444bc 100644 --- a/apps/miniprogram/src/services/wechat-templates.ts +++ b/apps/miniprogram/src/services/wechat-templates.ts @@ -1,18 +1,18 @@ -// 微信订阅消息模板 ID — 需在微信公众平台注册后填入 +// 微信订阅消息模板 ID — 通过环境变量注入 // 注册路径:公众平台 → 功能 → 订阅消息 → 添加模板 -// TODO: 上线前必须配置 +// 环境变量:TARO_APP_WX_TEMPLATE_APPOINTMENT / FOLLOWUP / REPORT / CRITICAL_ALERT / HEALTH_ABNORMAL export const TEMPLATE_IDS = { - APPOINTMENT_REMINDER: '', - FOLLOWUP_REMINDER: '', - REPORT_NOTIFICATION: '', - CRITICAL_HEALTH_ALERT: '', - HEALTH_DATA_ABNORMAL: '', + APPOINTMENT_REMINDER: process.env.TARO_APP_WX_TEMPLATE_APPOINTMENT || '', + FOLLOWUP_REMINDER: process.env.TARO_APP_WX_TEMPLATE_FOLLOWUP || '', + REPORT_NOTIFICATION: process.env.TARO_APP_WX_TEMPLATE_REPORT || '', + CRITICAL_HEALTH_ALERT: process.env.TARO_APP_WX_TEMPLATE_CRITICAL_ALERT || '', + HEALTH_DATA_ABNORMAL: process.env.TARO_APP_WX_TEMPLATE_HEALTH_ABNORMAL || '', } as const; /** 检查模板 ID 是否已配置,未配置时返回 false 并打印警告 */ export function isTemplateConfigured(key: keyof typeof TEMPLATE_IDS): boolean { if (!TEMPLATE_IDS[key]) { - console.warn(`[wechat-templates] 模板 ${key} 未配置,请在微信公众平台注册并填入 ID`); + console.warn(`[wechat-templates] 模板 ${key} 未配置,请在微信公众平台注册并设置对应环境变量`); return false; } return true; diff --git a/crates/erp-health/src/handler/article_handler.rs b/crates/erp-health/src/handler/article_handler.rs index e6970db..9506ced 100644 --- a/crates/erp-health/src/handler/article_handler.rs +++ b/crates/erp-health/src/handler/article_handler.rs @@ -2,7 +2,7 @@ use axum::Extension; use axum::extract::{FromRef, Json, Path, Query, State}; use erp_core::error::AppError; -use erp_core::rbac::require_permission; +use erp_core::rbac::{require_any_permission, require_permission}; use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext}; use crate::dto::article_dto::{ArticleListItem, ArticleListParams, ArticleResp, CreateArticleReq, ReviewArticleReq, UpdateArticleReq}; @@ -21,9 +21,15 @@ where require_permission(&ctx, "health.articles.list")?; let page = params.page.unwrap_or(1); let page_size = params.page_size.unwrap_or(20); + // 非管理权限用户只能查看已发布文章,防止草稿泄露 + let status = if require_any_permission(&ctx, &["health.articles.manage", "health.articles.review"]).is_ok() { + params.status + } else { + Some("published".to_string()) + }; let result = article_service::list_articles( &state, ctx.tenant_id, page, page_size, - params.category, params.status, params.category_id, params.tag_id, params.keyword, + params.category, status, params.category_id, params.tag_id, params.keyword, ) .await?; Ok(Json(ApiResponse::ok(result))) @@ -39,7 +45,8 @@ where S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.articles.list")?; - let result = article_service::get_article(&state, ctx.tenant_id, id).await?; + let is_admin = require_any_permission(&ctx, &["health.articles.manage", "health.articles.review"]).is_ok(); + let result = article_service::get_article(&state, ctx.tenant_id, id, is_admin).await?; Ok(Json(ApiResponse::ok(result))) } diff --git a/crates/erp-health/src/service/article_service.rs b/crates/erp-health/src/service/article_service.rs index 018eb00..6c0357f 100644 --- a/crates/erp-health/src/service/article_service.rs +++ b/crates/erp-health/src/service/article_service.rs @@ -99,16 +99,21 @@ pub async fn list_articles( Ok(PaginatedResponse { data, total, page, page_size: limit, total_pages }) } -/// 获取文章详情(管理端,不过滤发布状态) +/// 获取文章详情(管理端可查看任意状态,非管理端仅已发布) pub async fn get_article( state: &HealthState, tenant_id: Uuid, id: Uuid, + is_admin: bool, ) -> HealthResult { - let model = article::Entity::find() + let mut query = article::Entity::find() .filter(article::Column::Id.eq(id)) .filter(article::Column::TenantId.eq(tenant_id)) - .filter(article::Column::DeletedAt.is_null()) + .filter(article::Column::DeletedAt.is_null()); + if !is_admin { + query = query.filter(article::Column::Status.eq("published")); + } + let model = query .one(&state.db) .await? .ok_or(HealthError::ArticleNotFound)?; diff --git a/crates/erp-server/Cargo.toml b/crates/erp-server/Cargo.toml index 2cd9dae..6f60618 100644 --- a/crates/erp-server/Cargo.toml +++ b/crates/erp-server/Cargo.toml @@ -31,7 +31,7 @@ erp-plugin.workspace = true erp-health.workspace = true erp-ai.workspace = true erp-dialysis.workspace = true -erp-points.workspace = true +# erp-points 已禁用,积分功能统一由 erp-health 提供 anyhow.workspace = true uuid.workspace = true chrono.workspace = true diff --git a/crates/erp-server/src/main.rs b/crates/erp-server/src/main.rs index 573e80d..b690ba1 100644 --- a/crates/erp-server/src/main.rs +++ b/crates/erp-server/src/main.rs @@ -349,13 +349,9 @@ async fn main() -> anyhow::Result<()> { "AI module initialized" ); - // Initialize points module - let points_module = erp_points::PointsModule; - tracing::info!( - module = points_module.name(), - version = points_module.version(), - "Points module initialized" - ); + // Points module 已统一到 erp-health(/health/points/* 路由) + // erp-points 的 /points/* 路由为重复实现(大部分 501),已禁用 + // Initialize dialysis module let dialysis_module = erp_dialysis::DialysisModule; @@ -373,7 +369,7 @@ async fn main() -> anyhow::Result<()> { .register(message_module) .register(health_module) .register(ai_module) - .register(points_module) + // erp-points 已禁用,积分功能统一由 erp-health 提供 .register(dialysis_module); tracing::info!( module_count = registry.modules().len(), @@ -564,7 +560,7 @@ async fn main() -> anyhow::Result<()> { .merge(erp_plugin::module::PluginModule::protected_routes()) .merge(erp_health::HealthModule::protected_routes()) .merge(erp_ai::AiModule::protected_routes()) - .merge(erp_points::PointsModule::protected_routes()) + // erp-points 已禁用,积分路由统一由 erp-health /health/points/* 提供 .merge(erp_dialysis::DialysisModule::protected_routes()) .merge(handlers::audit_log::audit_log_router()) .route( diff --git a/crates/erp-server/src/state.rs b/crates/erp-server/src/state.rs index aedd4b7..af00d9b 100644 --- a/crates/erp-server/src/state.rs +++ b/crates/erp-server/src/state.rs @@ -123,16 +123,6 @@ impl FromRef for erp_ai::AiState { } } -/// Allow erp-points handlers to extract their required state. -impl FromRef for erp_points::PointsState { - fn from_ref(state: &AppState) -> Self { - Self { - db: state.db.clone(), - event_bus: state.event_bus.clone(), - } - } -} - /// Allow erp-dialysis handlers to extract their required state. impl FromRef for erp_dialysis::DialysisState { fn from_ref(state: &AppState) -> Self {