feat: 新增员工行为画像算法与费用风险标签体系

后端新增员工行为画像算法模块,支持标签规则引擎和评分计算,
完善员工模型、银行信息、序列化和导入逻辑,优化报销审批流
和工作流常量,增强 Hermes 同步和知识同步能力,前端新增费
用画像详情弹窗、雷达图和风险卡片组件,完善登录页和工作台
样式,优化文档中心和归档中心交互,补充单元测试。
This commit is contained in:
caoxiaozhu
2026-05-28 12:09:49 +08:00
parent 04cd6d0f81
commit 8a4a777be7
96 changed files with 9835 additions and 704 deletions

View File

@@ -13,6 +13,7 @@ from app.core.agent_enums import (
)
from app.core.logging import get_logger
from app.models.agent_asset import AgentAsset
from app.models.agent_run import AgentRun
from app.services.agent_asset_spreadsheet import (
COMPANY_COMMUNICATION_EXPENSE_RULE_CODE,
COMPANY_TRAVEL_EXPENSE_RULE_CODE,
@@ -26,6 +27,8 @@ from app.services.agent_foundation_constants import (
COMPANY_COMMUNICATION_RULE_VERSION,
COMPANY_TRAVEL_RULE_SCENARIO_JSON,
COMPANY_TRAVEL_RULE_VERSION,
DIGITAL_EMPLOYEE_FINANCE_POLICY_TASK_CODE,
DIGITAL_EMPLOYEE_LEGACY_TASK_CODES,
DIGITAL_EMPLOYEE_SKILL_CATEGORIES,
DIGITAL_EMPLOYEE_TASK_CATEGORY_MAP,
)
@@ -34,6 +37,26 @@ logger = get_logger("app.services.agent_foundation")
class AgentFoundationAssetTopUpMixin:
def _remove_legacy_digital_employee_assets(self) -> None:
assets = list(
self.db.scalars(
select(AgentAsset).where(AgentAsset.code.in_(DIGITAL_EMPLOYEE_LEGACY_TASK_CODES))
).all()
)
if not assets:
return
asset_ids = [asset.id for asset in assets]
runs = list(
self.db.scalars(select(AgentRun).where(AgentRun.task_id.in_(asset_ids))).all()
)
for run in runs:
run.task_id = None
self.db.add(run)
for asset in assets:
self.db.delete(asset)
def _sync_digital_employee_skill_categories(self) -> None:
category_options = list(DIGITAL_EMPLOYEE_SKILL_CATEGORIES)
has_changes = False
@@ -45,6 +68,10 @@ class AgentFoundationAssetTopUpMixin:
config_json = dict(asset.config_json or {})
changed = False
task_type = code.replace("task.hermes.", "").replace(".", "_")
if config_json.get("task_type") != task_type:
config_json["task_type"] = task_type
changed = True
if config_json.get("skill_category") != category:
config_json["skill_category"] = category
changed = True
@@ -63,6 +90,7 @@ class AgentFoundationAssetTopUpMixin:
def _top_up_agent_assets(self, existing_codes: set[str]) -> None:
self._remove_legacy_rule_assets()
self._remove_legacy_digital_employee_assets()
existing_codes = set(self.db.scalars(select(AgentAsset.code)).all())
self._sync_digital_employee_skill_categories()
@@ -572,91 +600,82 @@ class AgentFoundationAssetTopUpMixin:
created_by="系统初始化",
)
if "task.hermes.weekly_ar_summary" not in existing_codes:
finance_policy_cron = "0 3 * * *"
finance_policy_config = {
**self._digital_employee_task_config(
DIGITAL_EMPLOYEE_FINANCE_POLICY_TASK_CODE,
finance_policy_cron,
),
"schedule": finance_policy_cron,
"cron_expression": finance_policy_cron,
"skill_name": "finance-policy-knowledge-organizer",
"folder": "财务制度",
"changed_only": True,
"output_format": "knowledge_organizing_report",
}
if DIGITAL_EMPLOYEE_FINANCE_POLICY_TASK_CODE not in existing_codes:
asset = self._create_seed_asset(
asset_type=AgentAssetType.TASK.value,
code="task.hermes.weekly_ar_summary",
name="Hermes 每周应收账龄汇总",
description="每周汇总逾期应收、账龄分布和客户风险变化",
domain=AgentAssetDomain.SYSTEM.value,
scenario_json=["schedule", "accounts_receivable", "summary"],
owner="风控与审计部",
reviewer="顾承宇",
status=AgentAssetStatus.ACTIVE.value,
current_version="v1.0.0",
config_json=self._digital_employee_task_config("task.hermes.weekly_ar_summary", "0 10 * * 1"),
)
self._ensure_asset_version(
asset,
version="v1.0.0",
content=self._digital_employee_task_content(
"task.hermes.weekly_ar_summary",
"weekly_ar_summary",
"0 10 * * 1",
),
content_type=AgentAssetContentType.JSON.value,
change_note="初始化应收账龄汇总任务。",
created_by="系统初始化",
)
if "task.hermes.rule_review_digest" not in existing_codes:
asset = self._create_seed_asset(
asset_type=AgentAssetType.TASK.value,
code="task.hermes.rule_review_digest",
name="Hermes 规则待审摘要",
description="每天汇总待审规则、待补样例和被拒规则修订建议。",
domain=AgentAssetDomain.SYSTEM.value,
scenario_json=["schedule", "rule_center", "review_digest"],
owner="风控与审计部",
reviewer="顾承宇",
status=AgentAssetStatus.ACTIVE.value,
current_version="v1.0.0",
config_json=self._digital_employee_task_config("task.hermes.rule_review_digest", "0 18 * * *"),
)
self._ensure_asset_version(
asset,
version="v1.0.0",
content=self._digital_employee_task_content(
"task.hermes.rule_review_digest",
"rule_review_digest",
"0 18 * * *",
),
content_type=AgentAssetContentType.JSON.value,
change_note="初始化规则待审摘要任务。",
created_by="系统初始化",
)
if "task.hermes.knowledge_index_sync" not in existing_codes:
asset = self._create_seed_asset(
asset_type=AgentAssetType.TASK.value,
code="task.hermes.knowledge_index_sync",
name="Hermes ??????",
description="?????????? LightRAG ???????",
code=DIGITAL_EMPLOYEE_FINANCE_POLICY_TASK_CODE,
name="整理公司财务知识制度",
description="按计划整理公司财务制度、报销口径、审批要求和知识库资料,形成可复核的结构化知识",
domain=AgentAssetDomain.SYSTEM.value,
scenario_json=["schedule", "knowledge", "rule_center"],
owner="财务制度管理组",
reviewer="顾承宇",
status=AgentAssetStatus.ACTIVE.value,
current_version="v1.0.0",
config_json=self._digital_employee_task_config("task.hermes.knowledge_index_sync", "0 0 * * *"),
config_json=finance_policy_config,
)
self._ensure_asset_version(
asset,
version="v1.0.0",
content=self._digital_employee_task_content(
"task.hermes.knowledge_index_sync",
"knowledge_index_sync",
"0 0 * * *",
folder="报销制度",
changed_only=True,
),
content_type=AgentAssetContentType.JSON.value,
change_note="初始化制度知识与规则草稿形成任务。",
created_by="系统初始化",
else:
asset = self.db.scalar(
select(AgentAsset).where(AgentAsset.code == DIGITAL_EMPLOYEE_FINANCE_POLICY_TASK_CODE)
)
if asset is None:
return
existing_config = dict(asset.config_json or {})
existing_cron = (
existing_config.get("cron")
or existing_config.get("schedule")
or existing_config.get("cron_expression")
)
schedule_config = (
{
"cron": existing_cron,
"schedule": existing_cron,
"cron_expression": existing_cron,
}
if existing_cron
else {}
)
asset.name = "整理公司财务知识制度"
asset.description = "按计划整理公司财务制度、报销口径、审批要求和知识库资料,形成可复核的结构化知识。"
asset.owner = "财务制度管理组"
asset.domain = AgentAssetDomain.SYSTEM.value
asset.scenario_json = ["schedule", "knowledge", "rule_center"]
asset.config_json = {
**existing_config,
"agent": "hermes",
"task_type": "finance_policy_knowledge_organize",
"skill_category": "整理",
"skill_category_options": list(DIGITAL_EMPLOYEE_SKILL_CATEGORIES),
"skill_name": "finance-policy-knowledge-organizer",
"folder": existing_config.get("folder") or "财务制度",
"changed_only": existing_config.get("changed_only", True),
"output_format": "knowledge_organizing_report",
**schedule_config,
}
self.db.add(asset)
self._ensure_asset_version(
asset,
version="v1.0.0",
content=self._finance_policy_knowledge_skill_markdown(),
content_type=AgentAssetContentType.MARKDOWN.value,
change_note="初始化整理公司财务知识制度能力。",
created_by="系统初始化",
)