diff --git a/.gitignore b/.gitignore index c8cd9b34..9d858677 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,12 @@ -node_modules +app.db dist -*.local -.env +dist/ .DS_Store -*.tsbuildinfo +.env +*.local +node_modules +node_modules/ *.pyc __pycache__/ +*.tsbuildinfo venv/ -node_modules/ -dist/ diff --git a/backend/app/api/bsc_layers.py b/backend/app/api/bsc_layers.py new file mode 100644 index 00000000..929b27f7 --- /dev/null +++ b/backend/app/api/bsc_layers.py @@ -0,0 +1,41 @@ +"""BSC四层配置 API""" +from fastapi import APIRouter, Depends, Query +from sqlalchemy.orm import Session +from typing import List + +from app.database import get_db +from app.auth_middleware import require_auth +from app.models import BscLayerConfig, Entity + +router = APIRouter(prefix="/api/cma/bsc-layers", tags=["BSC层配置"], + dependencies=[Depends(require_auth)], +) + + +@router.get("") +def list_bsc_layers(entity_id: int = Query(1, description="企业ID"), db: Session = Depends(get_db)): + """获取某企业的BSC四层权重配置""" + # 验证企业存在 + entity = db.query(Entity).filter(Entity.id == entity_id).first() + if not entity: + from fastapi.responses import JSONResponse + return JSONResponse(status_code=404, content={"detail": "企业不存在"}) + + layers = db.query(BscLayerConfig).filter( + BscLayerConfig.entity_id == entity_id + ).order_by(BscLayerConfig.id).all() + + return { + "entity_id": entity_id, + "entity_name": entity.short_name or entity.name, + "layers": [ + { + "id": l.id, + "layer": l.layer, + "weight": float(l.weight), + "kpi_count_min": l.kpi_count_min, + "kpi_count_max": l.kpi_count_max, + } + for l in layers + ] + } diff --git a/backend/app/api/entities.py b/backend/app/api/entities.py index 0575682e..7a8be161 100644 --- a/backend/app/api/entities.py +++ b/backend/app/api/entities.py @@ -1,7 +1,8 @@ """企业实体 API""" from fastapi import APIRouter, Depends from sqlalchemy.orm import Session -from typing import List +from typing import List, Optional +from pydantic import BaseModel from app.database import get_db from app.auth_middleware import require_auth @@ -12,6 +13,20 @@ router = APIRouter(prefix="/api/cma/entities", tags=["企业实体"], ) +class EntityCreate(BaseModel): + name: str + short_name: Optional[str] = None + industry: Optional[str] = None + status: Optional[str] = "active" + + +class EntityUpdate(BaseModel): + name: Optional[str] = None + short_name: Optional[str] = None + industry: Optional[str] = None + status: Optional[str] = None + + @router.get("") def list_entities(db: Session = Depends(get_db)): """获取企业列表""" @@ -27,3 +42,52 @@ def list_entities(db: Session = Depends(get_db)): for e in entities ] } + + +@router.post("") +def create_entity(data: EntityCreate, db: Session = Depends(get_db)): + """创建企业""" + entity = Entity( + name=data.name, + short_name=data.short_name, + industry=data.industry, + status=data.status or "active", + ) + db.add(entity) + db.commit() + db.refresh(entity) + return { + "id": entity.id, + "name": entity.name, + "short_name": entity.short_name, + "industry": entity.industry, + "message": "企业创建成功", + } + + +@router.put("/{entity_id}") +def update_entity(entity_id: int, data: EntityUpdate, db: Session = Depends(get_db)): + """更新企业信息""" + entity = db.query(Entity).filter(Entity.id == entity_id).first() + if not entity: + from fastapi.responses import JSONResponse + return JSONResponse(status_code=404, content={"detail": "企业不存在"}) + + if data.name is not None: + entity.name = data.name + if data.short_name is not None: + entity.short_name = data.short_name + if data.industry is not None: + entity.industry = data.industry + if data.status is not None: + entity.status = data.status + + db.commit() + db.refresh(entity) + return { + "id": entity.id, + "name": entity.name, + "short_name": entity.short_name, + "industry": entity.industry, + "message": "企业更新成功", + } diff --git a/backend/app/main.py b/backend/app/main.py index e81e06e1..cce2bd9a 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -5,7 +5,7 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from dotenv import load_dotenv from app.database import init_db -from app.api import auth, kpis, templates, maps, dashboard, data, alerts, ai_analysis, alert_rules, users, thresholds, notifications, permissions, action_plans, alignment, org, objectives, versions, budget, cost, predict, reports, security, knowledge, bot_bridge, customer_dashboard, deviation_push, budget_generate, knowledge_articles, kpi_causality, data_quality, bi_reports, entities +from app.api import auth, kpis, templates, maps, dashboard, data, alerts, ai_analysis, alert_rules, users, thresholds, notifications, permissions, action_plans, alignment, org, objectives, versions, budget, cost, predict, reports, security, knowledge, bot_bridge, customer_dashboard, deviation_push, budget_generate, knowledge_articles, kpi_causality, data_quality, bi_reports, entities, bsc_layers from app.utils.cache import clear_all as clear_cache, delete as delete_cache from scripts.erp_sync import run_sync as run_erp_sync from app.auth_middleware import require_auth @@ -63,6 +63,7 @@ app.include_router(kpi_causality.router) app.include_router(data_quality.router) app.include_router(bi_reports.router) app.include_router(entities.router) +app.include_router(bsc_layers.router) @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index 218e20d9..e7a00c4d 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -282,6 +282,17 @@ class BiReport(Base): updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) +class BscLayerConfig(Base): + """BSC四层配置 — 不同企业的权重配置""" + __tablename__ = "bsc_layer_config" + id = Column(Integer, primary_key=True, index=True) + entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业ID") + layer = Column(String(20), nullable=False, comment="financial/customer/process/learning") + weight = Column(Float, nullable=False, comment="该层权重(%)") + kpi_count_min = Column(Integer, default=2, comment="最少KPI数") + kpi_count_max = Column(Integer, default=5, comment="最多KPI数") + + # 兼容性: P2开发新增的模板API需要的模型 # KPIDefinition 已存在,KPITemplate映射到同一定义 KPITemplate = KPIDefinition diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 596b711c..2ed17258 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -220,6 +220,12 @@ export const biReportApi = { exportReport: (data: any) => api.post('/bi-reports/export', data), } +export const entityApi = { + list: () => api.get('/entities'), + create: (data: any) => api.post('/entities', data), + update: (id: number, data: any) => api.put(`/entities/${id}`, data), +} + export const ethicsQuizApi = { getQuestions: () => api.get('/knowledge/ethics-quiz'), } diff --git a/frontend/src/views/KPIList.vue b/frontend/src/views/KPIList.vue index 946f1c05..3808f403 100644 --- a/frontend/src/views/KPIList.vue +++ b/frontend/src/views/KPIList.vue @@ -35,6 +35,20 @@
+ +
+ 🌐 企业: + + + + {{ currentEntityName }} +
+