feat: 增强规则资产管理与审计页面运行时调试
后端新增规则资产版本管理和规则文件 CRUD 接口,优化风险 规则生成模板执行和员工数据模型字段,知识库 RAG 增强本 地回退和文档提取能力,清理旧风险规则文件统一由生成引擎 管理,前端审计页面增加运行时调试面板和规则资产编辑交互, 补充单元测试覆盖。
This commit is contained in:
104
server/src/app/services/hermes_expense_report.py
Normal file
104
server/src/app/services/hermes_expense_report.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.logging import get_logger
|
||||
from app.models.financial_record import ExpenseClaim
|
||||
from app.services.runtime_chat import RuntimeChatService
|
||||
|
||||
logger = get_logger("app.services.hermes_expense_report")
|
||||
|
||||
|
||||
class HermesExpenseReportService:
|
||||
def __init__(self, db: Session) -> None:
|
||||
self.db = db
|
||||
self.chat_service = RuntimeChatService(db)
|
||||
|
||||
def generate_weekly_report(self, log_id: str | None = None) -> None:
|
||||
logger.info("Starting Hermes weekly expense report generation...")
|
||||
|
||||
# 1. 聚合数据
|
||||
aggregated_data = self._aggregate_recent_expenses(days=7)
|
||||
if not aggregated_data.get("total_amount"):
|
||||
logger.info("No expense data in the last 7 days. Skipping report.")
|
||||
return
|
||||
|
||||
# 2. 传入大模型分析
|
||||
report_markdown = self._generate_insights_with_llm(aggregated_data)
|
||||
|
||||
if not report_markdown:
|
||||
logger.warning("Failed to generate expense report from LLM.")
|
||||
return
|
||||
|
||||
# 3. 模拟发送报告
|
||||
self._deliver_report(report_markdown, log_id)
|
||||
logger.info("Hermes weekly expense report generation completed.")
|
||||
|
||||
def _aggregate_recent_expenses(self, days: int = 7) -> dict[str, Any]:
|
||||
target_date = datetime.now(timezone.utc) - timedelta(days=days)
|
||||
|
||||
# 基础过滤:最近N天且不是驳回状态的单据
|
||||
base_filter = [
|
||||
ExpenseClaim.occurred_at >= target_date,
|
||||
ExpenseClaim.status != "rejected"
|
||||
]
|
||||
|
||||
# 1. 按部门汇总
|
||||
dept_stmt = select(
|
||||
ExpenseClaim.department_name,
|
||||
func.sum(ExpenseClaim.amount).label("total")
|
||||
).where(*base_filter).group_by(ExpenseClaim.department_name)
|
||||
|
||||
dept_results = self.db.execute(dept_stmt).all()
|
||||
by_department = {row.department_name or "Unknown": float(row.total or 0) for row in dept_results}
|
||||
|
||||
# 2. 按类目汇总
|
||||
type_stmt = select(
|
||||
ExpenseClaim.expense_type,
|
||||
func.sum(ExpenseClaim.amount).label("total")
|
||||
).where(*base_filter).group_by(ExpenseClaim.expense_type)
|
||||
|
||||
type_results = self.db.execute(type_stmt).all()
|
||||
by_expense_type = {row.expense_type or "Unknown": float(row.total or 0) for row in type_results}
|
||||
|
||||
# 3. 总花费
|
||||
total_amount = sum(by_department.values())
|
||||
|
||||
return {
|
||||
"period": f"Last {days} days",
|
||||
"total_amount": total_amount,
|
||||
"by_department": by_department,
|
||||
"by_expense_type": by_expense_type
|
||||
}
|
||||
|
||||
def _generate_insights_with_llm(self, data: dict[str, Any]) -> str | None:
|
||||
system_prompt = (
|
||||
"你是公司的财务分析专家。请根据提供的最近期业务开销数据,撰写一份简洁有力的【高管费控洞察周报】。\n"
|
||||
"要求:\n"
|
||||
"1. 不要机械地罗列数字,要像人一样指出异常(例如:哪个部门花钱最多?打车费是不是异常高?)。\n"
|
||||
"2. 给出 1 条削减成本的实操建议。\n"
|
||||
"3. 纯 Markdown 格式输出,不超过 300 字。"
|
||||
)
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": f"开销统计数据:\n{json.dumps(data, ensure_ascii=False, indent=2)}"}
|
||||
]
|
||||
|
||||
response = self.chat_service.complete(
|
||||
messages,
|
||||
max_tokens=800,
|
||||
temperature=0.4
|
||||
)
|
||||
return response
|
||||
|
||||
def _deliver_report(self, report_markdown: str, log_id: str | None) -> None:
|
||||
# TODO: 未来在这里接入企微/钉钉机器人或邮件发送接口
|
||||
logger.info(f"\n================ Hermes Weekly Report [LogID: {log_id}] ================\n"
|
||||
f"{report_markdown}\n"
|
||||
f"==========================================================================")
|
||||
Reference in New Issue
Block a user