678 lines
39 KiB
Python
678 lines
39 KiB
Python
"""管理会计OS 数据模型"""
|
||
from sqlalchemy import Column, Integer, String, Text, Float, DateTime, ForeignKey, Boolean, JSON, func, UniqueConstraint
|
||
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
|
||
from app.models.driver_budget import DriverFactorTemplate, DriverFactorBudget
|
||
|
||
|
||
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 UserEntity(Base):
|
||
"""用户-企业授权(账套模式多对多)— 新表必须带entity_id(开发规范)"""
|
||
__tablename__ = "user_entities"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True, comment="用户ID")
|
||
entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, index=True, comment="企业ID(账套)")
|
||
granted_by = Column(Integer, nullable=True, comment="授权人")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
__table_args__ = (UniqueConstraint("user_id", "entity_id", name="uq_user_entity"),)
|
||
|
||
|
||
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="数据源配置")
|
||
data_source = Column(String(500), default="待补充", comment="数据来源")
|
||
data_owner = Column(String(100), default="待指定", 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="负责人")
|
||
kpi_level = Column(String(20), default="operational", comment="strategic/operational")
|
||
status = Column(String(20), default="active")
|
||
bot_source = Column(String(50), nullable=True, comment="Bot标识: finance-bot/ops-bot等")
|
||
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)
|
||
alert_type = Column(String(30), default="actual", comment="actual/forecast — 实际值超限/预测值超限")
|
||
suggestion = Column(Text, nullable=True, comment="情景建议")
|
||
action_plan_linked_id = Column(Integer, nullable=True, comment="关联的改善计划ID")
|
||
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 Objective(Base):
|
||
"""OKR目标"""
|
||
__tablename__ = "objectives"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
title = Column(String(200), nullable=False, comment="目标标题")
|
||
description = Column(Text, nullable=True, comment="目标描述")
|
||
dimension = Column(String(50), nullable=True, comment="关联维度: finance/customer/process/learning")
|
||
strategic_map_id = Column(Integer, ForeignKey("strategic_maps.id"), nullable=True, comment="关联战略地图")
|
||
quarter = Column(String(20), nullable=False, comment="季度: 2026Q3")
|
||
owner = Column(String(100), nullable=True, comment="负责人")
|
||
status = Column(String(20), default="active", comment="active/completed/cancelled")
|
||
progress = Column(Integer, default=0, comment="整体进度 0-100")
|
||
confidence = Column(Integer, default=5, comment="信心指数 1-10")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
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")
|
||
objective_id = Column(Integer, ForeignKey("objectives.id"), nullable=True, comment="关联OKR目标")
|
||
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="改善结果")
|
||
monthly_milestones = Column(JSON, nullable=True, comment="月度里程碑: [{\"month\":\"2026-07\",\"label\":\"...\",\"status\":\"completed\"}]")
|
||
auto_verify_rule = Column(JSON, nullable=True, comment="自动验证规则: {\"condition\": \"value > target\"}")
|
||
verify_result = Column(String(20), nullable=True, comment="验证结果: pass/fail/pending")
|
||
verify_log = Column(JSON, nullable=True, comment="验证历史日志")
|
||
verify_status = Column(String(20), default="pending", comment="验证状态: pending/passed/failed/retrying/escalated")
|
||
verify_attempts = Column(Integer, default=0, comment="验证尝试次数")
|
||
verified_at = Column(DateTime, nullable=True, comment="验证完成时间")
|
||
kpi_current_before = Column(Float, nullable=True, comment="执行前KPI值")
|
||
kpi_current_after = Column(Float, nullable=True, comment="执行后KPI值")
|
||
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="图标标识")
|
||
kpis = Column(JSON, nullable=True, comment="关联KPI编码数组")
|
||
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 MpmResult(Base):
|
||
"""MPM财务Bot分析结果记录"""
|
||
__tablename__ = "mpm_results"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业实体ID")
|
||
period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
|
||
source = Column(String(50), default="finance-bot", comment="来源Bot标识")
|
||
raw_data = Column(JSON, nullable=False, comment="完整的MPM计算结果")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
|
||
class BotBridgeConfig(Base):
|
||
"""Bot桥接鉴权配置"""
|
||
__tablename__ = "bot_bridge_config"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
bot_name = Column(String(50), unique=True, nullable=False, comment="Bot名称")
|
||
token = Column(String(64), nullable=False, comment="鉴权Token")
|
||
is_active = Column(Boolean, default=True, comment="是否激活")
|
||
created_at = Column(DateTime, server_default=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数")
|
||
|
||
|
||
class KPIHierarchy(Base):
|
||
"""KPI层级关系 — 公司→部门→个人三级分解"""
|
||
__tablename__ = "kpi_hierarchy"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
entity_id = Column(Integer, ForeignKey("entities.id"), default=1, comment="企业ID")
|
||
parent_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="上级KPI")
|
||
child_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="下级KPI")
|
||
level = Column(Integer, default=1, comment="1=公司级 2=部门级 3=个人级")
|
||
weight = Column(Float, default=1.0, comment="下级对上级的贡献权重(%)")
|
||
child_name = Column(String(200), nullable=True, comment="下级节点名称(个人或部门名)")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
|
||
# 兼容性: P2开发新增的模板API需要的模型
|
||
# KPI模板(独立表)
|
||
class KPITemplate(Base):
|
||
__tablename__ = "kpi_templates"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
kpi_code = Column(String(50), unique=True, nullable=False)
|
||
kpi_name = Column(String(200), nullable=False)
|
||
dimension = Column(String(50))
|
||
category = Column(String(50))
|
||
formula = Column(Text)
|
||
formula_desc = Column(String(500))
|
||
unit = Column(String(50))
|
||
target_value = Column(Float)
|
||
description = Column(Text)
|
||
is_system = Column(Integer, default=0)
|
||
usage_count = Column(Integer, default=0)
|
||
created_by = Column(Integer)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class OKRTemplate(Base):
|
||
"""OKR模板库"""
|
||
__tablename__ = "okr_templates"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
name = Column(String(100), nullable=False, comment="O名称")
|
||
description = Column(Text, comment="O描述")
|
||
dimension = Column(String(20), nullable=False, comment="finance/customer/process/learning")
|
||
layer = Column(String(20), default="level1", comment="level1/level2/level3")
|
||
industry_tag = Column(String(50), default="general", comment="行业标签")
|
||
preset_krs = Column(JSON, nullable=False, comment="预设关键结果列表")
|
||
source = Column(String(20), default="system", comment="system/user/industry_pack")
|
||
use_count = Column(Integer, default=0, comment="使用次数")
|
||
sort_order = Column(Integer, default=0)
|
||
is_active = Column(Integer, default=1)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class CashForecast(Base):
|
||
"""现金流预测 — 每日未来30天预测"""
|
||
__tablename__ = "cash_forecasts"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业ID")
|
||
forecast_date = Column(DateTime, nullable=False, comment="预测日期(每天一条)")
|
||
predicted_cash = Column(Float, nullable=True, comment="预测现金余额")
|
||
lower_bound = Column(Float, nullable=True, comment="置信区间下界")
|
||
upper_bound = Column(Float, nullable=True, comment="置信区间上界")
|
||
alert_status = Column(String(20), default="green", comment="green/yellow/red")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
|
||
class CashPlan(Base):
|
||
"""收付款计划 — 资金管理智能体(唯一应收载体:含回款登记、负责人、数据来源)"""
|
||
__tablename__ = "cash_plans"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
entity_id = Column(Integer, ForeignKey("entities.id"), default=1, comment="企业ID")
|
||
plan_type = Column(String(10), nullable=False, comment="receive收/pay付")
|
||
amount = Column(Float, nullable=False, comment="金额(万元)")
|
||
plan_date = Column(DateTime, nullable=False, comment="计划日期(应收即到期日)")
|
||
counterparty = Column(String(200), nullable=True, comment="关联客户/供应商")
|
||
description = Column(String(500), nullable=True, comment="说明")
|
||
status = Column(String(20), default="pending", comment="pending/completed/cancelled")
|
||
owner = Column(String(100), nullable=True, comment="负责人/业务员(应收催收责任人)")
|
||
source = Column(String(50), default="manual", comment="数据来源: manual/bohai_ar/receivables_migrate")
|
||
paid_amount = Column(Float, default=0, comment="已回款金额(万元)")
|
||
completed_at = Column(DateTime, nullable=True, comment="完成时间")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class SystemConfig(Base):
|
||
"""系统配置 — key-value存储"""
|
||
__tablename__ = "system_configs"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
config_key = Column(String(100), unique=True, nullable=False, comment="配置键")
|
||
config_value = Column(String(500), nullable=True, comment="配置值")
|
||
description = Column(String(500), nullable=True, comment="配置说明")
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class BudgetDeviationAlert(Base):
|
||
"""预算偏差预警记录"""
|
||
__tablename__ = "budget_deviation_alerts"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="关联KPI")
|
||
period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
|
||
budget_value = Column(Float, nullable=True, comment="预算值")
|
||
actual_value = Column(Float, nullable=True, comment="实际值")
|
||
deviation_rate = Column(Float, nullable=True, comment="偏差率 %")
|
||
deviation_value = Column(Float, nullable=True, comment="偏差绝对值")
|
||
alert_level = Column(String(20), default="warning", comment="warning/critical")
|
||
status = Column(String(20), default="open", comment="open/resolved/ignored")
|
||
suggestion = Column(String(500), nullable=True, comment="处理建议")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
|
||
class ReportHistory(Base):
|
||
"""自动生成的经营分析报告记录"""
|
||
__tablename__ = "report_history"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
report_type = Column(String(20), nullable=False, comment="weekly/monthly/special")
|
||
period = Column(String(20), nullable=False, comment="期间: 2026-W30 / 2026-07 / 2026-Q2")
|
||
title = Column(String(200), nullable=False, comment="报告标题")
|
||
markdown_content = Column(Text, nullable=True, comment="Markdown格式报告(用于微信推送)")
|
||
json_content = Column(JSON, nullable=True, comment="JSON结构化数据(写入CMA系统)")
|
||
status = Column(String(20), default="generated", comment="generated/pushed/failed")
|
||
trigger_type = Column(String(20), default="manual", comment="manual/scheduled/event")
|
||
alert_ref = Column(String(50), nullable=True, comment="事件触发时的预警引用")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
|
||
class AnalysisResult(Base):
|
||
"""财务Bot分析结论 — 带置信度评分"""
|
||
__tablename__ = "analysis_results"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
|
||
conclusion = Column(String(1000), nullable=False, comment="分析结论")
|
||
confidence = Column(Integer, nullable=False, comment="置信度 0-100")
|
||
data_source = Column(String(500), nullable=True, comment="数据来源")
|
||
calculation_logic = Column(String(1000), nullable=True, comment="计算逻辑")
|
||
comparable_benchmark = Column(String(500), nullable=True, comment="可比基准")
|
||
limitations = Column(String(1000), nullable=True, comment="局限说明")
|
||
has_actual = Column(Integer, default=0, comment="有实际值")
|
||
has_target = Column(Integer, default=0, comment="有目标值")
|
||
has_trend = Column(Integer, default=0, comment="有历史趋势")
|
||
has_review = Column(Integer, default=0, comment="有人工复核")
|
||
kpi_code = Column(String(50), nullable=True, comment="关联KPI编码")
|
||
kpi_name = Column(String(200), nullable=True, comment="关联KPI名称")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
|
||
class ForecastAccuracy(Base):
|
||
"""预测准确率 — 上期预测 vs 本期实际"""
|
||
__tablename__ = "forecast_accuracy"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业ID")
|
||
period = Column(String(20), nullable=False, comment="期间 2026-07")
|
||
forecast_value = Column(Float, nullable=True, comment="预测值")
|
||
actual_value = Column(Float, nullable=True, comment="实际值")
|
||
mae = Column(Float, nullable=True, comment="绝对误差")
|
||
mape = Column(Float, nullable=True, comment="百分比误差")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
|
||
class ScenarioSuggestion(Base):
|
||
"""情景建议模板 — 根据不同预警类型自动生成建议"""
|
||
__tablename__ = "scenario_suggestions"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
alert_type = Column(String(30), nullable=False, comment="预警类型: cash_low/cash_critical/cost_high/revenue_drop")
|
||
title = Column(String(200), nullable=False, comment="建议标题")
|
||
description = Column(Text, nullable=True, comment="详细建议")
|
||
action_template = Column(Text, nullable=True, comment="改善行动模板")
|
||
priority = Column(String(20), default="medium", comment="high/medium/low")
|
||
sort_order = Column(Integer, default=0, comment="排序")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
|
||
# ============================================================
|
||
# 新30号准则模型 (2027)
|
||
# ============================================================
|
||
|
||
class Subject(Base):
|
||
"""会计科目 — 新30号准则分类"""
|
||
__tablename__ = "subjects"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
subject_code = Column(String(20), nullable=False, unique=True, comment="科目编码")
|
||
subject_name = Column(String(200), nullable=False, comment="科目名称")
|
||
parent_code = Column(String(20), nullable=True, comment="上级科目编码")
|
||
level = Column(Integer, default=1, comment="科目级别 1-4")
|
||
category = Column(String(50), nullable=True, comment="科目类别")
|
||
new_standard_category = Column(String(20), nullable=True, comment="新30号准则分类: operating/investing/financing/tax/discontinued")
|
||
is_active = Column(Integer, default=1, comment="是否启用")
|
||
remark = Column(String(500), nullable=True, comment="备注")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class VoucherDetail(Base):
|
||
"""凭证明细 — 新30号准则分类"""
|
||
__tablename__ = "voucher_details"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
voucher_no = Column(String(50), nullable=False, comment="凭证编号")
|
||
voucher_date = Column(DateTime, nullable=False, comment="凭证日期")
|
||
subject_code = Column(String(20), nullable=False, comment="科目编码")
|
||
subject_name = Column(String(200), nullable=True, comment="科目名称")
|
||
debit_amount = Column(Float, default=0, comment="借方金额")
|
||
credit_amount = Column(Float, default=0, comment="贷方金额")
|
||
summary = Column(String(500), nullable=True, comment="摘要")
|
||
new_standard_category = Column(String(20), nullable=True, comment="新30号准则分类")
|
||
period = Column(String(20), nullable=True, comment="期间 YYYY-MM")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
|
||
# ============================================================
|
||
# 费用审核智能体 (2026-08)
|
||
# ============================================================
|
||
|
||
class ExpenseRule(Base):
|
||
"""费用规则 — 自动校验报销单的标准"""
|
||
__tablename__ = "expense_rules"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
rule_name = Column(String(100), nullable=False, comment="规则名称")
|
||
dimension = Column(String(20), nullable=False, comment="维度: department/person/expense_type")
|
||
dimension_value = Column(String(100), nullable=True, comment="维度值: 部门名/人员名/费用类型(空=全局)")
|
||
expense_type = Column(String(30), nullable=False, comment="费用类型: entertainment/travel/office/management")
|
||
limit_type = Column(String(20), nullable=False, comment="限额类型: single/monthly/yearly 单笔/月度累计/年度累计")
|
||
limit_amount = Column(Float, nullable=False, comment="限额金额")
|
||
cycle = Column(String(20), default="monthly", comment="周期: single/monthly/yearly")
|
||
status = Column(String(20), default="active", comment="active/inactive")
|
||
remark = Column(String(500), nullable=True, comment="备注")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class ExpenseReimbursement(Base):
|
||
"""费用报销单 — 提交后自动校验规则,超限自动打回"""
|
||
__tablename__ = "expense_reimbursements"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
reimb_no = Column(String(50), unique=True, nullable=False, comment="报销单号")
|
||
applicant = Column(String(100), nullable=False, comment="申请人")
|
||
department = Column(String(100), nullable=True, comment="部门")
|
||
expense_type = Column(String(30), nullable=False, comment="费用类型: entertainment/travel/office/management")
|
||
title = Column(String(200), nullable=False, comment="事由/摘要")
|
||
amount = Column(Float, nullable=False, comment="报销金额")
|
||
expense_date = Column(DateTime, nullable=True, comment="费用发生日期")
|
||
attachment = Column(String(500), nullable=True, comment="附件文件名")
|
||
status = Column(String(20), default="pending", comment="pending待审批/approved已通过/rejected已拒绝/returned已打回")
|
||
check_result = Column(String(20), default="pass", comment="自动校验结果: pass/fail")
|
||
check_reason = Column(String(1000), nullable=True, comment="超限原因")
|
||
check_detail = Column(JSON, nullable=True, comment="校验明细: [{rule_name, limit, actual, passed}]")
|
||
checked_at = Column(DateTime, nullable=True, comment="自动校验时间")
|
||
approver = Column(String(100), nullable=True, comment="审批人")
|
||
approve_comment = Column(String(500), nullable=True, comment="审批意见")
|
||
approved_at = Column(DateTime, 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())
|
||
|
||
|
||
# ============================================================
|
||
# 税务合规智能体 (2026-08)
|
||
# ① 税负监控 ② 发票校验 ③ 社保比对
|
||
# ============================================================
|
||
|
||
class TaxRecord(Base):
|
||
"""税务记录 — 税负监控:应纳税额/实缴额/税负率 vs 行业基准"""
|
||
__tablename__ = "tax_records"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
entity_id = Column(Integer, default=1, comment="企业ID")
|
||
period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
|
||
tax_type = Column(String(30), nullable=False, comment="税种: vat增值税/income所得税/surtax附加税")
|
||
tax_payable = Column(Float, default=0, comment="应纳税额")
|
||
tax_paid = Column(Float, default=0, comment="实缴税额")
|
||
tax_rate = Column(Float, nullable=True, comment="适用税率 %")
|
||
income = Column(Float, default=0, comment="当期收入/计税收入(税负率分母)")
|
||
tax_burden_rate = Column(Float, nullable=True, comment="税负率 % = 实缴税额/收入×100")
|
||
burden_status = Column(String(20), default="normal", comment="normal正常/warning超基准±20%内/alert超基准±20%")
|
||
warning_msg = Column(String(500), nullable=True, comment="预警信息")
|
||
remark = Column(String(500), nullable=True, comment="备注")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class InvoiceCheck(Base):
|
||
"""发票校验 — 录入后按规则自动校验:号码格式/金额匹配报销单/供应商匹配合同"""
|
||
__tablename__ = "invoice_check"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
entity_id = Column(Integer, default=1, comment="企业ID")
|
||
invoice_no = Column(String(50), nullable=False, comment="发票号码")
|
||
amount = Column(Float, nullable=False, comment="发票金额")
|
||
invoice_type = Column(String(30), default="vat", comment="发票类型: vat专用/vat普通/electronic电子/other其他")
|
||
invoice_date = Column(DateTime, nullable=True, comment="开票日期")
|
||
supplier = Column(String(200), nullable=True, comment="供应商名称")
|
||
reimb_no = Column(String(50), nullable=True, comment="关联报销单号")
|
||
contract_no = Column(String(50), nullable=True, comment="关联合同编号")
|
||
check_status = Column(String(20), default="pending", comment="校验状态: pending待校验/valid通过/invalid异常/warning提醒")
|
||
check_result = Column(JSON, nullable=True, comment="校验明细: [{rule, passed, message}]")
|
||
check_reason = Column(String(1000), nullable=True, comment="异常原因汇总")
|
||
checked_at = Column(DateTime, nullable=True, comment="校验时间")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class SocialSecurity(Base):
|
||
"""社保缴费记录 — 社保比对:基数与工资匹配/单位缴纳比例/漏缴提醒"""
|
||
__tablename__ = "social_security"
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
entity_id = Column(Integer, default=1, comment="企业ID")
|
||
employee = Column(String(100), nullable=False, comment="人员姓名")
|
||
period = Column(String(20), nullable=False, comment="期间 YYYY-MM")
|
||
base_amount = Column(Float, default=0, comment="缴费基数")
|
||
salary = Column(Float, nullable=True, comment="申报工资")
|
||
company_amount = Column(Float, default=0, comment="单位缴纳金额")
|
||
personal_amount = Column(Float, default=0, comment="个人缴纳金额")
|
||
company_rate = Column(Float, nullable=True, comment="单位缴纳比例 % (养老16%+医疗8%+失业0.5%≈24.5%)")
|
||
check_status = Column(String(20), default="normal", comment="normal正常/warning基数或比例异常/alert漏缴")
|
||
warning_msg = Column(String(500), nullable=True, comment="异常提醒: 漏缴/基数不符/比例异常")
|
||
remark = Column(String(500), nullable=True, comment="备注")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|