Files
cma-management/backend/app/models/__init__.py
T

299 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""管理会计OS 数据模型"""
from sqlalchemy import Column, Integer, String, Text, Float, DateTime, ForeignKey, Boolean, JSON, func
from app.database import Base
from app.models.budget_plan import BudgetPlan
from app.models.cost_model import StandardCost, ActualCost, AbcActivity, AbcAllocation
from app.models.knowledge import KnowledgeEvent, KnowledgeSummary
class Entity(Base):
"""企业实体"""
__tablename__ = "entities"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), nullable=False, comment="企业全称")
short_name = Column(String(50), comment="企业简称")
industry = Column(String(50), comment="行业")
status = Column(String(20), default="active", comment="active/inactive/demo")
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class User(Base):
"""用户"""
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, nullable=False)
password_hash = Column(String(128), nullable=False)
name = Column(String(100), nullable=False)
role = Column(String(20), default="finance") # ceo / finance / business / it
phone = Column(String(20), nullable=True)
created_at = Column(DateTime, server_default=func.now())
class StrategicMap(Base):
"""战略地图"""
__tablename__ = "strategic_maps"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(200), nullable=False, comment="地图名称")
version = Column(String(20), default="v1.0", comment="版本号")
status = Column(String(20), default="draft", comment="draft/published")
dimensions = Column(JSON, nullable=True, comment="四维度和目标列表")
canvas_data = Column(JSON, nullable=True, comment="画布连线数据")
created_by = Column(Integer, nullable=True)
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class KPIDefinition(Base):
"""KPI字典"""
__tablename__ = "kpi_definitions"
id = Column(Integer, primary_key=True, index=True)
entity_id = Column(Integer, default=1, comment="企业ID")
map_id = Column(Integer, ForeignKey("strategic_maps.id"), nullable=True, comment="关联战略地图")
kpi_code = Column(String(50), unique=True, nullable=False, comment="KPI编码")
kpi_name = Column(String(200), nullable=False, comment="KPI名称")
dimension = Column(String(50), comment="所属维度: finance/customer/process/learning")
objective = Column(String(200), comment="关联战略目标")
formula = Column(Text, nullable=True, comment="计算公式")
formula_desc = Column(String(500), nullable=True, comment="公式说明")
data_source_type = Column(String(20), default="manual", comment="erp/business/excel/manual")
data_source_config = Column(JSON, nullable=True, comment="数据源配置")
frequency = Column(String(20), default="monthly", comment="daily/weekly/monthly/quarterly/yearly")
unit = Column(String(50), default="%", comment="单位")
target_value = Column(Float, nullable=True, comment="目标值")
threshold_green = Column(String(100), nullable=True, comment="绿灯阈值")
threshold_yellow = Column(String(100), nullable=True, comment="黄灯阈值")
threshold_red = Column(String(100), nullable=True, comment="红灯阈值")
category = Column(String(50), nullable=True, comment="BSC二级类别: revenue_growth/profitability/cost_control/asset_efficiency/cash_risk/customer_scale/customer_concentration/customer_satisfaction/supply_chain/delivery_quality/talent_pipeline/employee_engagement/innovation")
responsible_dept = Column(String(200), nullable=True, comment="负责部门")
responsible_user = Column(String(100), nullable=True, comment="负责人")
status = Column(String(20), default="active")
epic = Column(String(50), default="Epic2", comment="所属Epic")
created_by = Column(Integer, nullable=True)
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class KPIValue(Base):
"""KPI实际值"""
__tablename__ = "kpi_values"
id = Column(Integer, primary_key=True, index=True)
kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False)
period = Column(String(20), nullable=False, comment="期间 2026-05")
actual_value = Column(Float, nullable=True, comment="实际值")
source_type = Column(String(20), default="manual", comment="erp/excel/manual")
source_batch = Column(String(100), nullable=True, comment="导入批次号")
data_status = Column(String(20), default="pending", comment="pending/verified/error")
calculated_at = Column(DateTime, server_default=func.now())
remark = Column(String(500), nullable=True)
class DataSourceConfig(Base):
"""数据源配置"""
__tablename__ = "data_source_config"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(200), nullable=False, comment="数据源名称")
source_type = Column(String(20), nullable=False, comment="erp/business/excel")
api_endpoint = Column(String(500), nullable=True, comment="API地址")
api_key = Column(String(200), nullable=True, comment="API Key")
query_sql = Column(Text, nullable=True, comment="SQL查询语句")
sync_type = Column(String(20), default="realtime", comment="realtime/batch")
status = Column(String(20), default="active")
last_sync_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, server_default=func.now())
class KPIAlert(Base):
"""预警记录"""
__tablename__ = "kpi_alerts"
id = Column(Integer, primary_key=True, index=True)
kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False)
kpi_value_id = Column(Integer, ForeignKey("kpi_values.id"), nullable=True)
alert_level = Column(String(20), default="yellow", comment="green/yellow/red")
alert_message = Column(String(500), nullable=False)
status = Column(String(20), default="pending", comment="pending/processing/resolved")
assignee = Column(String(100), nullable=True, comment="处理人")
resolution = Column(Text, nullable=True, comment="处理结果")
resolved_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, server_default=func.now())
class OperationLog(Base):
"""操作日志"""
__tablename__ = "operation_logs"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, nullable=True)
action = Column(String(50), nullable=False, comment="create/update/delete/calculate/import")
target_type = Column(String(50), nullable=False, comment="kpi/map/alert/source")
target_id = Column(Integer, nullable=True)
detail = Column(JSON, nullable=True)
created_at = Column(DateTime, server_default=func.now())
class NotificationChannel(Base):
"""通知渠道配置"""
__tablename__ = "notification_channels"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), nullable=False, comment="渠道名称")
channel_type = Column(String(30), nullable=False, comment="wecom/mail/sms")
config = Column(JSON, nullable=True, comment="渠道配置")
enabled = Column(Boolean, default=True)
created_at = Column(DateTime, server_default=func.now())
class NotificationLog(Base):
"""通知发送日志"""
__tablename__ = "notification_logs"
id = Column(Integer, primary_key=True, index=True)
alert_id = Column(Integer, ForeignKey("kpi_alerts.id"), nullable=True)
channel = Column(String(30), nullable=False, comment="wecom/mail")
recipient = Column(String(200), nullable=True, comment="收件人")
title = Column(String(200), nullable=True)
content = Column(Text, nullable=True)
status = Column(String(20), default="pending", comment="pending/sent/failed")
error_msg = Column(String(500), nullable=True)
sent_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, server_default=func.now())
class RolePermission(Base):
"""角色权限配置(单条记录,key-value"""
__tablename__ = "role_permissions"
id = Column(Integer, primary_key=True, index=True)
key = Column(String(50), unique=True, nullable=False, comment="配置键: route_permissions / action_permissions")
value = Column(JSON, nullable=False, comment="配置值")
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class ActionPlan(Base):
"""改善行动计划"""
__tablename__ = "action_plans"
id = Column(Integer, primary_key=True, index=True)
alert_id = Column(Integer, ForeignKey("kpi_alerts.id"), nullable=True, comment="关联预警")
kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="关联KPI")
title = Column(String(200), nullable=False, comment="计划标题")
description = Column(Text, nullable=True, comment="详细描述")
assignee = Column(String(100), nullable=True, comment="负责人")
priority = Column(String(20), default="medium", comment="high/medium/low")
due_date = Column(DateTime, nullable=True, comment="截止日期")
status = Column(String(20), default="pending", comment="pending/in_progress/completed/cancelled")
progress = Column(Integer, default=0, comment="完成进度 0-100")
result = Column(Text, nullable=True, comment="改善结果")
created_by = Column(String(100), nullable=True, comment="创建人")
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class OrgNode(Base):
"""组织节点: 集团→事业部→区域→部门→班组 5级"""
__tablename__ = "org_nodes"
id = Column(Integer, primary_key=True, index=True)
parent_id = Column(Integer, ForeignKey("org_nodes.id"), nullable=True, comment="父节点ID")
name = Column(String(100), nullable=False, comment="节点名称")
code = Column(String(50), unique=True, nullable=True, comment="编码")
level = Column(Integer, nullable=False, comment="1=集团 2=事业部 3=区域 4=部门 5=班组")
sort_order = Column(Integer, default=0, comment="排序")
enabled = Column(Integer, default=1, comment="1启用 0禁用")
path = Column(String(500), nullable=True, comment="路径")
remark = Column(String(200), nullable=True, comment="备注")
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class StrategicMapVersion(Base):
"""战略地图版本快照"""
__tablename__ = "strategic_map_versions"
id = Column(Integer, primary_key=True, index=True)
map_id = Column(Integer, ForeignKey("strategic_maps.id", ondelete="CASCADE"), nullable=False, comment="关联地图")
version = Column(String(20), nullable=False, comment="版本号 v1.0 v1.1 ...")
dimensions = Column(JSON, nullable=False, comment="维度数据快照")
canvas_data = Column(JSON, nullable=False, comment="画布数据快照")
comment = Column(String(500), nullable=True, comment="说明")
created_by = Column(Integer, nullable=True)
created_at = Column(DateTime, server_default=func.now())
class MapObjective(Base):
"""战略地图目标: 每个维度下的具体目标"""
__tablename__ = "map_objectives"
id = Column(Integer, primary_key=True, index=True)
map_id = Column(Integer, ForeignKey("strategic_maps.id", ondelete="CASCADE"), nullable=False, comment="关联地图")
dimension_key = Column(String(50), nullable=False, comment="所属维度: finance/customer/process/learning")
name = Column(String(200), nullable=False, comment="目标名称")
description = Column(Text, nullable=True, comment="描述")
icon = Column(String(50), default="target", comment="图标标识")
sort_order = Column(Integer, default=0, comment="排序")
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class KPICausality(Base):
"""KPI因果链 — 记录KPI间的因果关系"""
__tablename__ = "kpi_causality"
id = Column(Integer, primary_key=True, index=True)
source_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="源KPI(因)")
target_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="目标KPI(果)")
strength = Column(Float, default=0.5, comment="影响强度 0~1")
lag_months = Column(Integer, default=1, comment="滞后期(月)")
formula = Column(String(500), nullable=True, comment="影响公式描述")
direction = Column(String(10), default="positive", comment="positive/negative 正向/负向影响")
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class KpiDataQualityLog(Base):
"""数据质量监控日志"""
__tablename__ = "kpi_data_quality_log"
id = Column(Integer, primary_key=True, index=True)
kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="关联KPI")
check_type = Column(String(30), nullable=False, comment="abnormal_change/flat_data/missing_data/value_outlier")
severity = Column(String(20), default="warning", comment="info/warning/critical")
detail = Column(JSON, nullable=True, comment="检测详情")
suggestion = Column(String(500), nullable=True, comment="建议操作")
status = Column(String(20), default="open", comment="open/resolved/ignored")
resolved_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, server_default=func.now())
class BiReportTemplate(Base):
"""BI报表模板"""
__tablename__ = "bi_report_templates"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(200), nullable=False, comment="模板名称")
report_type = Column(String(50), nullable=False, comment="overview/trend/comparison/topn/causality")
config = Column(JSON, nullable=False, comment="报表配置")
is_system = Column(Integer, default=0, comment="系统预置模板")
created_by = Column(Integer, nullable=True)
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class BiReport(Base):
"""用户保存的BI报表"""
__tablename__ = "bi_reports"
id = Column(Integer, primary_key=True, index=True)
template_id = Column(Integer, ForeignKey("bi_report_templates.id"), nullable=True)
name = Column(String(200), nullable=False, comment="报表名称")
config = Column(JSON, nullable=False, comment="报表配置(行/列/值)")
chart_type = Column(String(50), default="auto", comment="图表类型")
is_shared = Column(Integer, default=0, comment="是否分享")
created_by = Column(Integer, nullable=True)
created_at = Column(DateTime, server_default=func.now())
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