From 69f3e35d14dced007345de4ffc4ce33a806ea097 Mon Sep 17 00:00:00 2001 From: duxingchen Date: Wed, 12 Aug 2026 12:03:07 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Dashboard=E7=BB=9F=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E4=BF=AE=E5=A4=8D=20+=20=E7=94=9F=E4=BA=A7=E7=8E=AF?= =?UTF-8?q?=E5=A2=83SECRET=5FKEY=E5=BC=BA=E5=88=B6=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Dashboard统计Bug修复 - Task统计改用TASK_STATUS_PENDING/WIP/COMPLETED大写常量 - 旧代码使用小写"pending"/"in_progress"永远匹配不到数据 - 修复后tasks_pending/tasks_in_progress/tasks_completed返回真实值 2. 生产环境SECRET_KEY强制校验 - 新增model_validator:DEBUG=False且SECRET_KEY为默认值时抛出ValueError - 阻止使用默认密钥部署到生产环境 --- backend/app/core/config.py | 12 ++++++++++++ backend/app/services/dashboard_service.py | 8 ++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 00586af..6149f8c 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -1,5 +1,6 @@ """核心配置 — Pydantic Settings 自动从 .env 读取""" import json +from pydantic import model_validator from pydantic_settings import BaseSettings @@ -26,6 +27,17 @@ class Settings(BaseSettings): except (json.JSONDecodeError, TypeError): return ["http://localhost:1420", "tauri://localhost"] + @model_validator(mode="after") + def _validate_production_secret(self): + """生产环境强制校验:SECRET_KEY 禁止使用默认值""" + if not self.DEBUG and self.SECRET_KEY == "change-me-in-production": + raise ValueError( + "生产环境 (DEBUG=False) 禁止使用默认 SECRET_KEY。" + "请在 .env 中设置 SECRET_KEY 为至少 32 字符的随机值。" + "示例: python -c \"import secrets; print(secrets.token_urlsafe(32))\"" + ) + return self + class Config: env_file = ".env" extra = "ignore" diff --git a/backend/app/services/dashboard_service.py b/backend/app/services/dashboard_service.py index 6f24ebe..793b051 100644 --- a/backend/app/services/dashboard_service.py +++ b/backend/app/services/dashboard_service.py @@ -17,7 +17,7 @@ class DashboardStats(BaseModel): async def get_dashboard_stats(db: AsyncSession) -> DashboardStats: from app.models.product import Product - from app.models.task import Task + from app.models.task import Task, TASK_STATUS_PENDING, TASK_STATUS_WIP, TASK_STATUS_COMPLETED p_total = await db.scalar(select(func.count(Product.id))) p_pending = await db.scalar(select(func.count(Product.id)).where(Product.status == "pending")) @@ -25,9 +25,9 @@ async def get_dashboard_stats(db: AsyncSession) -> DashboardStats: p_done = await db.scalar(select(func.count(Product.id)).where(Product.status == "completed")) t_total = await db.scalar(select(func.count(Task.id))) - t_pending = await db.scalar(select(func.count(Task.id)).where(Task.status == "pending")) - t_progress = await db.scalar(select(func.count(Task.id)).where(Task.status == "in_progress")) - t_done = await db.scalar(select(func.count(Task.id)).where(Task.status == "completed")) + t_pending = await db.scalar(select(func.count(Task.id)).where(Task.status == TASK_STATUS_PENDING)) + t_progress = await db.scalar(select(func.count(Task.id)).where(Task.status == TASK_STATUS_WIP)) + t_done = await db.scalar(select(func.count(Task.id)).where(Task.status == TASK_STATUS_COMPLETED)) return DashboardStats( products_total=p_total or 0,