From 813ac81950c5d01259facffbab7aba8c34fab09e Mon Sep 17 00:00:00 2001 From: caoxiaozhu Date: Mon, 18 May 2026 10:01:40 +0000 Subject: [PATCH] =?UTF-8?q?feat(finance):=20=E6=B7=BB=E5=8A=A0=E5=85=AC?= =?UTF-8?q?=E5=8F=B8=E9=80=9A=E4=BF=A1=E8=B4=B9=E6=8A=A5=E9=94=80=E8=A7=84?= =?UTF-8?q?=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增通信费报销规则代码和文件名常量 - 在初始化数据中创建公司通信费报销规则资产 - 添加对应的版本和审核记录 - 标记为 v1.0.0 版本并审核通过 --- .../app/services/agent_asset_spreadsheet.py | 2 + server/src/app/services/agent_foundation.py | 242 ++++++++++++++++++ 2 files changed, 244 insertions(+) diff --git a/server/src/app/services/agent_asset_spreadsheet.py b/server/src/app/services/agent_asset_spreadsheet.py index 4bc33eb..f9fe93c 100644 --- a/server/src/app/services/agent_asset_spreadsheet.py +++ b/server/src/app/services/agent_asset_spreadsheet.py @@ -22,6 +22,8 @@ RULE_SPREADSHEET_BLOCK_PATTERN = re.compile( COMPANY_TRAVEL_EXPENSE_RULE_CODE = "rule.expense.company_travel_expense_reimbursement" COMPANY_TRAVEL_EXPENSE_RULE_FILENAME = "公司差旅费报销规则.xlsx" +COMPANY_COMMUNICATION_EXPENSE_RULE_CODE = "rule.expense.company_communication_expense_reimbursement" +COMPANY_COMMUNICATION_EXPENSE_RULE_FILENAME = "公司通信费报销规则.xlsx" FINANCE_RULES_LIBRARY = "finance-rules" RISK_RULES_LIBRARY = "risk-rules" RULE_LIBRARY_NAMES = {FINANCE_RULES_LIBRARY, RISK_RULES_LIBRARY} diff --git a/server/src/app/services/agent_foundation.py b/server/src/app/services/agent_foundation.py index 88875cf..845f2a7 100644 --- a/server/src/app/services/agent_foundation.py +++ b/server/src/app/services/agent_foundation.py @@ -36,6 +36,8 @@ from app.models.financial_record import ( ) from app.services.agent_asset_spreadsheet import ( AgentAssetSpreadsheetManager, + COMPANY_COMMUNICATION_EXPENSE_RULE_CODE, + COMPANY_COMMUNICATION_EXPENSE_RULE_FILENAME, COMPANY_TRAVEL_EXPENSE_RULE_CODE, COMPANY_TRAVEL_EXPENSE_RULE_FILENAME, FINANCE_RULES_LIBRARY, @@ -88,6 +90,7 @@ LEGACY_RULE_CODES = ( ATTACHMENT_RULE_ASSET_CODE = "rule.expense.attachment_submission_requirements" COMPANY_TRAVEL_RULE_VERSION = "v1.0.0" +COMPANY_COMMUNICATION_RULE_VERSION = "v1.0.0" ATTACHMENT_RULE_RUNTIME_CONFIG = { "kind": "policy_rule_draft", @@ -279,6 +282,28 @@ class AgentFoundationService: "rule_template_label": "差旅报销 Excel 模板", }, ) + company_communication_rule = AgentAsset( + asset_type=AgentAssetType.RULE.value, + code=COMPANY_COMMUNICATION_EXPENSE_RULE_CODE, + name="公司通信费报销规则", + description="通过 Excel 明细表维护员工通信费报销标准、专项补充口径和审批要求。", + domain=AgentAssetDomain.EXPENSE.value, + scenario_json=["expense", "communication_expense", "expense_standard"], + owner="财务制度管理组", + reviewer="顾承宇", + status=AgentAssetStatus.ACTIVE.value, + current_version=COMPANY_COMMUNICATION_RULE_VERSION, + published_version=COMPANY_COMMUNICATION_RULE_VERSION, + working_version=COMPANY_COMMUNICATION_RULE_VERSION, + config_json={ + "severity": "medium", + "enabled": True, + "tag": "财务规则", + "detail_mode": "spreadsheet", + "rule_library": FINANCE_RULES_LIBRARY, + "rule_template_label": "通信费报销 Excel 模板", + }, + ) skill_expense_asset = AgentAsset( asset_type=AgentAssetType.SKILL.value, code="skill.expense.summary_lookup", @@ -406,6 +431,7 @@ class AgentFoundationService: scene_submission_rule, travel_policy_rule, company_travel_rule, + company_communication_rule, skill_expense_asset, skill_ar_asset, invoice_mcp_asset, @@ -423,6 +449,11 @@ class AgentFoundationService: version=COMPANY_TRAVEL_RULE_VERSION, actor_name="系统初始化", ) + company_communication_rule_meta = self._ensure_company_communication_rule_spreadsheet_seed( + company_communication_rule, + version=COMPANY_COMMUNICATION_RULE_VERSION, + actor_name="系统初始化", + ) self.db.add_all( [ @@ -484,6 +515,18 @@ class AgentFoundationService: change_note="初始化差旅费报销 Excel 规则表。", created_by="系统初始化", ), + AgentAssetVersion( + asset=company_communication_rule, + version=COMPANY_COMMUNICATION_RULE_VERSION, + content=AgentAssetSpreadsheetManager.build_version_markdown( + rule_name=company_communication_rule.name, + version=COMPANY_COMMUNICATION_RULE_VERSION, + metadata=company_communication_rule_meta, + ), + content_type=AgentAssetContentType.MARKDOWN.value, + change_note="初始化通信费报销 Excel 规则表。", + created_by="系统初始化", + ), AgentAssetVersion( asset=skill_expense_asset, version="v1.0.0", @@ -635,6 +678,14 @@ class AgentFoundationService: review_note="首版 Excel 规则表已确认,可作为财务规则使用。", reviewed_at=datetime.now(UTC), ), + AgentAssetReview( + asset=company_communication_rule, + version=COMPANY_COMMUNICATION_RULE_VERSION, + reviewer="顾承宇", + review_status=AgentReviewStatus.APPROVED.value, + review_note="首版 Excel 规则表已确认,可作为财务规则使用。", + reviewed_at=datetime.now(UTC), + ), ] ) @@ -1001,6 +1052,9 @@ class AgentFoundationService: company_travel_rule = self.db.scalar( select(AgentAsset).where(AgentAsset.code == COMPANY_TRAVEL_EXPENSE_RULE_CODE) ) + company_communication_rule = self.db.scalar( + select(AgentAsset).where(AgentAsset.code == COMPANY_COMMUNICATION_EXPENSE_RULE_CODE) + ) if ATTACHMENT_RULE_ASSET_CODE not in existing_codes: attachment_rule = self._create_seed_asset( @@ -1209,6 +1263,26 @@ class AgentFoundationService: "rule_template_label": "差旅报销 Excel 模板", }, ) + if COMPANY_COMMUNICATION_EXPENSE_RULE_CODE not in existing_codes: + company_communication_rule = self._create_seed_asset( + asset_type=AgentAssetType.RULE.value, + code=COMPANY_COMMUNICATION_EXPENSE_RULE_CODE, + name="公司通信费报销规则", + description="通过 Excel 明细表维护员工通信费报销标准、专项补充口径和审批要求。", + domain=AgentAssetDomain.EXPENSE.value, + scenario_json=["expense", "communication_expense", "expense_standard"], + owner="财务制度管理组", + reviewer="顾承宇", + status=AgentAssetStatus.ACTIVE.value, + current_version=COMPANY_COMMUNICATION_RULE_VERSION, + config_json={ + "severity": "medium", + "enabled": True, + "tag": "财务规则", + "detail_mode": "spreadsheet", + "rule_template_label": "通信费报销 Excel 模板", + }, + ) if company_travel_rule is not None: if not str(company_travel_rule.current_version or "").strip(): @@ -1256,6 +1330,52 @@ class AgentFoundationService: reviewed_at=datetime.now(UTC), ) + if company_communication_rule is not None: + if not str(company_communication_rule.current_version or "").strip(): + company_communication_rule.current_version = COMPANY_COMMUNICATION_RULE_VERSION + if not str(company_communication_rule.working_version or "").strip(): + company_communication_rule.working_version = company_communication_rule.current_version + if not str(company_communication_rule.published_version or "").strip(): + company_communication_rule.published_version = company_communication_rule.current_version + if not str(company_communication_rule.status or "").strip(): + company_communication_rule.status = AgentAssetStatus.ACTIVE.value + company_communication_rule.description = "通过 Excel 明细表维护员工通信费报销标准、专项补充口径和审批要求。" + company_communication_rule.config_json = { + **(company_communication_rule.config_json or {}), + "severity": "medium", + "enabled": True, + "tag": "财务规则", + "detail_mode": "spreadsheet", + "rule_library": FINANCE_RULES_LIBRARY, + "rule_template_label": "通信费报销 Excel 模板", + } + company_communication_rule_meta = self._ensure_company_communication_rule_spreadsheet_seed( + company_communication_rule, + version=str(company_communication_rule.current_version or COMPANY_COMMUNICATION_RULE_VERSION), + actor_name="系统初始化", + ) + self._ensure_asset_version( + company_communication_rule, + version=str(company_communication_rule.current_version or COMPANY_COMMUNICATION_RULE_VERSION), + content=AgentAssetSpreadsheetManager.build_version_markdown( + rule_name=company_communication_rule.name, + version=str(company_communication_rule.current_version or COMPANY_COMMUNICATION_RULE_VERSION), + metadata=company_communication_rule_meta, + ), + content_type=AgentAssetContentType.MARKDOWN.value, + change_note="初始化通信费报销 Excel 规则表。", + created_by="系统初始化", + ) + if str(company_communication_rule.current_version or "").strip() == COMPANY_COMMUNICATION_RULE_VERSION: + self._ensure_asset_review( + company_communication_rule, + version=COMPANY_COMMUNICATION_RULE_VERSION, + reviewer="顾承宇", + review_status=AgentReviewStatus.APPROVED.value, + review_note="首版 Excel 规则表已确认,可作为财务规则使用。", + reviewed_at=datetime.now(UTC), + ) + if "skill.ar.aging_summary" not in existing_codes: asset = self._create_seed_asset( asset_type=AgentAssetType.SKILL.value, @@ -1487,6 +1607,21 @@ class AgentFoundationService: } return metadata + def _ensure_company_communication_rule_spreadsheet_seed( + self, + asset: AgentAsset, + *, + version: str, + actor_name: str, + ): + return self._ensure_finance_rule_spreadsheet_seed( + asset, + version=version, + actor_name=actor_name, + file_name=COMPANY_COMMUNICATION_EXPENSE_RULE_FILENAME, + fallback_sheet_name="通信费报销规则", + ) + @staticmethod def _read_or_build_company_travel_rule_file( manager: AgentAssetSpreadsheetManager, @@ -1501,6 +1636,113 @@ class AgentFoundationService: return live_path.read_bytes() return AgentAssetSpreadsheetManager.build_blank_rule_workbook("差旅费报销规则") + def _ensure_finance_rule_spreadsheet_seed( + self, + asset: AgentAsset, + *, + version: str, + actor_name: str, + file_name: str, + fallback_sheet_name: str, + ): + manager = AgentAssetSpreadsheetManager() + manager.ensure_rule_library_dirs() + live_document = manager.store_rule_library_spreadsheet( + library=FINANCE_RULES_LIBRARY, + file_name=file_name, + content=self._read_or_build_finance_rule_file( + manager, + file_name=file_name, + fallback_sheet_name=fallback_sheet_name, + ), + actor_name=actor_name, + source="rule-library", + ) + existing_document = ( + asset.config_json.get("rule_document") + if isinstance(asset.config_json, dict) + else None + ) + storage_key = ( + str(existing_document.get("storage_key") or "").strip() + if isinstance(existing_document, dict) + else "" + ) + if storage_key: + try: + existing_path = manager.resolve_storage_path(storage_key) + except FileNotFoundError: + existing_path = None + if existing_path is not None and existing_path.exists(): + metadata = RuleSpreadsheetMeta( + file_name=str(existing_document.get("file_name") or file_name), + storage_key=storage_key, + mime_type=str(existing_document.get("mime_type") or "").strip() + or "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + size_bytes=int(existing_document.get("size_bytes") or existing_path.stat().st_size), + checksum=hashlib.sha256(existing_path.read_bytes()).hexdigest(), + updated_at=str(existing_document.get("updated_at") or "").strip() + or datetime.now(UTC).isoformat(), + updated_by=str(existing_document.get("updated_by") or actor_name).strip() + or actor_name, + source=str(existing_document.get("source") or "seed").strip() or "seed", + ) + asset.config_json = { + **(asset.config_json or {}), + "detail_mode": "spreadsheet", + "tag": "财务规则", + "rule_library": FINANCE_RULES_LIBRARY, + "rule_document": { + **AgentAssetSpreadsheetManager.build_rule_document_config( + live_document, + asset_version=version, + ), + "storage_key": live_document.storage_key, + }, + } + return metadata + + live_content = manager.resolve_storage_path(live_document.storage_key).read_bytes() + metadata = manager.store_spreadsheet( + asset_id=asset.id, + version=version, + file_name=file_name, + content=live_content, + actor_name=actor_name, + source="seed", + ) + asset.config_json = { + **(asset.config_json or {}), + "detail_mode": "spreadsheet", + "tag": "财务规则", + "rule_library": FINANCE_RULES_LIBRARY, + "rule_document": { + **AgentAssetSpreadsheetManager.build_rule_document_config( + live_document, + asset_version=version, + ), + "storage_key": live_document.storage_key, + }, + } + return metadata + + @staticmethod + def _read_or_build_finance_rule_file( + manager: AgentAssetSpreadsheetManager, + *, + file_name: str, + fallback_sheet_name: str, + ) -> bytes: + live_key = ( + Path("rules") + / FINANCE_RULES_LIBRARY + / file_name + ).as_posix() + live_path = manager.resolve_storage_path(live_key) + if live_path.exists(): + return live_path.read_bytes() + return AgentAssetSpreadsheetManager.build_blank_rule_workbook(fallback_sheet_name) + def _create_seed_asset( self, *,