From 0e861d8fa61b76805940da6cf07e2293e9e086cb Mon Sep 17 00:00:00 2001 From: caoxiaozhu Date: Tue, 26 May 2026 09:15:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=E9=A3=8E=E9=99=A9?= =?UTF-8?q?=E8=A7=84=E5=88=99=E7=94=9F=E6=88=90=E5=BC=95=E6=93=8E=E4=B8=8E?= =?UTF-8?q?=E9=A2=84=E7=AE=97=E4=B8=AD=E5=BF=83=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端拆分风险规则生成为解释器、语义分析、本体对齐等子模块, 优化模板执行和流程图生成,完善员工种子数据和导入逻辑,增强 报销单权限策略和草稿持久化,前端新增预算中心视图和趋势图 组件,重构审计页面和风险规则测试对话框交互,完善文档中心 和报销创建页面细节,补充单元测试覆盖。 --- .../development/budget-center/MASTER_TODO.md | 60 + document/development/budget-center/README.md | 58 + .../budget-center-ui-prototype.html | 1179 +++++++ .../budget-center/day_1_budget_foundation.md | 105 + .../budget-center/day_2_budget_center_ui.md | 82 + .../day_3_budget_transaction_service.md | 55 + .../day_4_application_claim_integration.md | 58 + .../day_5_approval_payment_archive_budget.md | 52 + .../day_6_budget_analytics_ontology.md | 51 + .../day_7_hardening_acceptance.md | 64 + ...travel.generated_20260525155459576686.json | 102 + ...travel.generated_20260526071430001717.json | 99 + ...travel.generated_20260526071633392020.json | 170 + .../src/app/api/v1/endpoints/agent_assets.py | 56 +- .../app/api/v1/endpoints/reimbursements.py | 2 +- server/src/app/core/agent_enums.py | 2 + server/src/app/schemas/agent_asset.py | 7 +- .../app/services/agent_asset_json_rules.py | 2 + .../services/agent_asset_risk_rule_level.py | 250 ++ .../agent_asset_risk_rule_simulation.py | 249 +- ...agent_asset_risk_rule_simulation_fields.py | 385 +++ .../services/agent_asset_risk_rule_testing.py | 19 +- server/src/app/services/agent_assets.py | 5 + .../services/agent_foundation_risk_rules.py | 137 +- server/src/app/services/document_numbering.py | 86 + server/src/app/services/employee.py | 150 +- server/src/app/services/employee_import.py | 14 +- server/src/app/services/employee_seed.py | 11 +- .../services/employee_seed_organizations.py | 131 +- .../src/app/services/employee_seed_part1.py | 38 +- .../src/app/services/employee_seed_part2.py | 36 +- .../src/app/services/employee_spreadsheet.py | 2 +- server/src/app/services/employee_time.py | 53 + .../services/expense_claim_access_policy.py | 44 +- .../expense_claim_application_handoff.py | 83 + .../expense_claim_draft_persistence.py | 21 +- .../services/expense_claim_platform_risk.py | 7 +- server/src/app/services/expense_claims.py | 37 +- server/src/app/services/ontology_detection.py | 7 + .../src/app/services/ontology_extraction.py | 4 +- .../app/services/risk_rule_flow_diagram.py | 206 +- .../src/app/services/risk_rule_generation.py | 388 +-- .../risk_rule_generation_interpreter.py | 17 + .../app/services/risk_rule_generation_jobs.py | 336 ++ .../services/risk_rule_generation_markdown.py | 39 + .../services/risk_rule_generation_ontology.py | 163 + .../services/risk_rule_generation_prompt.py | 147 + .../risk_rule_generation_semantics.py | 119 + .../services/risk_rule_manifest_normalizer.py | 199 ++ server/src/app/services/risk_rule_scoring.py | 322 ++ .../services/risk_rule_template_executor.py | 445 ++- .../app/services/user_agent_application.py | 218 +- .../user_agent_application_locations.py | 148 + .../2月20_武汉-上海.pdf | Bin 24995 -> 0 bytes .../2月20_武汉-上海.pdf.meta.json | 88 - .../2月20_武汉-上海.preview.png | Bin 136681 -> 0 bytes .../酒店3.jpg | Bin 153582 -> 0 bytes .../酒店3.jpg.meta.json | 79 - .../酒店3.preview.jpg | Bin 153582 -> 0 bytes .../2月23_上海-武汉.pdf | Bin 24940 -> 0 bytes .../2月23_上海-武汉.pdf.meta.json | 88 - .../2月23_上海-武汉.preview.png | Bin 137722 -> 0 bytes server/tests/test_document_numbering.py | 94 + server/tests/test_employee_service.py | 48 +- .../tests/test_employee_spreadsheet_import.py | 4 +- server/tests/test_expense_claim_service.py | 280 +- server/tests/test_ontology_service.py | 35 +- server/tests/test_orchestrator_review_flow.py | 10 +- .../test_risk_rule_composite_generation.py | 361 ++ server/tests/test_risk_rule_generation.py | 533 ++- server/tests/test_user_agent_service.py | 58 +- web/UI/预算中心.jpg | Bin 0 -> 150655 bytes .../components/risk-rule-test-dialog.css | 131 + .../assets/styles/views/audit-view-part2.css | 232 +- web/src/assets/styles/views/audit-view.css | 2952 +++++++++-------- .../styles/views/budget-center-view.css | 499 +++ .../travel-reimbursement-create-view.css | 168 + .../travel-request-detail-view-part2.css | 24 +- .../views/travel-request-detail-view.css | 135 +- .../components/charts/BudgetTrendChart.vue | 142 + web/src/components/layout/SidebarRail.vue | 44 +- .../components/shared/RiskRuleFlowDiagram.vue | 219 +- .../components/shared/RiskRuleTestDialog.vue | 120 +- .../shared/riskRuleTestDialogDisplay.js | 110 + web/src/composables/useAppShell.js | 98 +- web/src/composables/useDocumentCenterInbox.js | 176 + web/src/composables/useNavigation.js | 36 +- web/src/composables/useRequests.js | 1 + web/src/data/icons.js | 1 + web/src/router/index.js | 29 +- web/src/services/agentAssets.js | 8 + web/src/services/ontology.js | 5 +- web/src/utils/accessControl.js | 75 +- web/src/utils/applicationApproval.js | 105 + .../utils/assistantSuggestedActionPrefill.js | 2 +- web/src/utils/detailAlerts.js | 1 + web/src/utils/documentCenterNewState.js | 5 + web/src/utils/documentCenterRows.js | 47 + web/src/utils/documentClassification.js | 22 + web/src/utils/expenseApplicationDetail.js | 106 + web/src/utils/expenseApplicationOntology.js | 114 +- web/src/utils/expenseApplicationPreview.js | 528 +++ web/src/utils/expenseClaimArchive.js | 6 + web/src/utils/requestViewModel.js | 25 + web/src/views/AppShellRouteView.vue | 80 +- web/src/views/AuditView.vue | 2098 ++++++------ web/src/views/BudgetCenterView.vue | 160 + web/src/views/DocumentsCenterView.vue | 40 +- .../views/TravelReimbursementCreateView.vue | 86 +- web/src/views/TravelRequestDetailView.vue | 65 +- web/src/views/scripts/ArchiveCenterView.js | 14 +- web/src/views/scripts/AuditView.js | 99 +- web/src/views/scripts/BudgetCenterView.js | 216 ++ .../views/scripts/EmployeeManagementView.js | 2 +- .../scripts/TravelReimbursementCreateView.js | 121 +- .../views/scripts/TravelRequestDetailView.js | 118 +- web/src/views/scripts/auditViewMetadata.js | 24 +- web/src/views/scripts/auditViewModel.js | 74 +- .../views/scripts/auditViewRiskRuleModel.js | 300 +- .../travelReimbursementConversationModel.js | 5 +- .../travelReimbursementExpenseQueryModel.js | 8 +- .../travelReimbursementGuidedFlowModel.js | 5 +- .../travelRequestDetailExpenseModel.js | 1 + .../scripts/useApplicationPreviewEditor.js | 105 + .../useTravelReimbursementGuidedFlow.js | 20 + .../useTravelReimbursementSubmitComposer.js | 158 + web/tests/accessControl.test.mjs | 17 + web/tests/app-shell-detail-alerts.test.mjs | 4 +- ...p-shell-financial-assistant-entry.test.mjs | 10 + web/tests/application-approval-info.test.mjs | 54 + .../assistant-session-draft-delete.test.mjs | 4 +- ...ssistant-suggested-action-prefill.test.mjs | 2 +- ...tachment-association-confirmation.test.mjs | 2 +- .../document-center-archived-scope.test.mjs | 50 + web/tests/document-center-new-state.test.mjs | 11 + .../documents-center-status-filter.test.mjs | 3 + .../expense-application-fast-preview.test.mjs | 291 ++ .../expense-application-ontology.test.mjs | 16 + ...e-application-submit-rich-confirm.test.mjs | 2 +- web/tests/expense-claim-archive.test.mjs | 22 +- .../navigation-route-resolution.test.mjs | 15 +- web/tests/requestProgressSteps.test.mjs | 4 +- web/tests/requestViewModel.test.mjs | 41 +- .../sidebar-document-unread-dot.test.mjs | 43 + .../travel-reimbursement-guided-flow.test.mjs | 11 + ...eimbursement-review-drawer-switch.test.mjs | 15 +- ...el-request-detail-leader-approval.test.mjs | 26 +- .../travel-request-detail-responsive.test.mjs | 27 + ...travel-request-detail-risk-advice.test.mjs | 11 +- ...vel-request-detail-submit-confirm.test.mjs | 8 + 150 files changed, 14953 insertions(+), 4099 deletions(-) create mode 100644 document/development/budget-center/MASTER_TODO.md create mode 100644 document/development/budget-center/README.md create mode 100644 document/development/budget-center/budget-center-ui-prototype.html create mode 100644 document/development/budget-center/day_1_budget_foundation.md create mode 100644 document/development/budget-center/day_2_budget_center_ui.md create mode 100644 document/development/budget-center/day_3_budget_transaction_service.md create mode 100644 document/development/budget-center/day_4_application_claim_integration.md create mode 100644 document/development/budget-center/day_5_approval_payment_archive_budget.md create mode 100644 document/development/budget-center/day_6_budget_analytics_ontology.md create mode 100644 document/development/budget-center/day_7_hardening_acceptance.md create mode 100644 server/rules/risk-rules/risk.expense.travel.generated_20260525155459576686.json create mode 100644 server/rules/risk-rules/risk.expense.travel.generated_20260526071430001717.json create mode 100644 server/rules/risk-rules/risk.expense.travel.generated_20260526071633392020.json create mode 100644 server/src/app/services/agent_asset_risk_rule_level.py create mode 100644 server/src/app/services/agent_asset_risk_rule_simulation_fields.py create mode 100644 server/src/app/services/document_numbering.py create mode 100644 server/src/app/services/employee_time.py create mode 100644 server/src/app/services/expense_claim_application_handoff.py create mode 100644 server/src/app/services/risk_rule_generation_interpreter.py create mode 100644 server/src/app/services/risk_rule_generation_jobs.py create mode 100644 server/src/app/services/risk_rule_generation_markdown.py create mode 100644 server/src/app/services/risk_rule_generation_ontology.py create mode 100644 server/src/app/services/risk_rule_generation_prompt.py create mode 100644 server/src/app/services/risk_rule_generation_semantics.py create mode 100644 server/src/app/services/risk_rule_manifest_normalizer.py create mode 100644 server/src/app/services/risk_rule_scoring.py create mode 100644 server/src/app/services/user_agent_application_locations.py delete mode 100644 server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.pdf delete mode 100644 server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.pdf.meta.json delete mode 100644 server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.preview.png delete mode 100644 server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/12aab9cc-fa31-4cde-8935-16ef24fb94a4/酒店3.jpg delete mode 100644 server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/12aab9cc-fa31-4cde-8935-16ef24fb94a4/酒店3.jpg.meta.json delete mode 100644 server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/12aab9cc-fa31-4cde-8935-16ef24fb94a4/酒店3.preview.jpg delete mode 100644 server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/2014d004-4ef9-4655-8ef0-8f3ad64df2f7/2月23_上海-武汉.pdf delete mode 100644 server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/2014d004-4ef9-4655-8ef0-8f3ad64df2f7/2月23_上海-武汉.pdf.meta.json delete mode 100644 server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/2014d004-4ef9-4655-8ef0-8f3ad64df2f7/2月23_上海-武汉.preview.png create mode 100644 server/tests/test_document_numbering.py create mode 100644 server/tests/test_risk_rule_composite_generation.py create mode 100644 web/UI/预算中心.jpg create mode 100644 web/src/assets/styles/views/budget-center-view.css create mode 100644 web/src/components/charts/BudgetTrendChart.vue create mode 100644 web/src/components/shared/riskRuleTestDialogDisplay.js create mode 100644 web/src/composables/useDocumentCenterInbox.js create mode 100644 web/src/utils/applicationApproval.js create mode 100644 web/src/utils/documentCenterRows.js create mode 100644 web/src/utils/documentClassification.js create mode 100644 web/src/utils/expenseApplicationDetail.js create mode 100644 web/src/utils/expenseApplicationPreview.js create mode 100644 web/src/views/BudgetCenterView.vue create mode 100644 web/src/views/scripts/BudgetCenterView.js create mode 100644 web/src/views/scripts/useApplicationPreviewEditor.js create mode 100644 web/tests/application-approval-info.test.mjs create mode 100644 web/tests/document-center-archived-scope.test.mjs create mode 100644 web/tests/expense-application-fast-preview.test.mjs create mode 100644 web/tests/sidebar-document-unread-dot.test.mjs create mode 100644 web/tests/travel-request-detail-responsive.test.mjs diff --git a/document/development/budget-center/MASTER_TODO.md b/document/development/budget-center/MASTER_TODO.md new file mode 100644 index 0000000..639f416 --- /dev/null +++ b/document/development/budget-center/MASTER_TODO.md @@ -0,0 +1,60 @@ +# 预算中心 MASTER TODO + +## 总目标 + +把预算从首页静态展示升级为真实费控底座,让费用申请和报销都必须经过预算口径校验。 + +## 状态图 + +```text +预算额度 + -> 申请提交: 预占 + -> 申请退回/撤回/驳回: 释放 + -> 申请通过: 保持预占 + -> 报销提交: 校验申请与预算 + -> 报销审批通过: 核销 + -> 报销退回/撤回: 释放或回滚 +``` + +## 总 TODO + +- [ ] 新增预算中心开发文档并纳入每日核对。 +- [ ] 定义预算维度:部门、成本中心、项目、费用科目、期间。 +- [ ] 定义预算模型:预算额度、预算交易、预算占用。 +- [ ] 定义预算状态:正常、预警、超预算、冻结。 +- [ ] 定义预算交易类型:初始化、调整、预占、释放、核销、回滚。 +- [ ] 新增预算列表接口。 +- [ ] 新增预算详情接口。 +- [ ] 新增预算台账接口。 +- [ ] 新增预算占用接口或内部服务。 +- [ ] 新增预算释放接口或内部服务。 +- [ ] 新增预算核销接口或内部服务。 +- [ ] 费用申请提交时写入预算预占。 +- [ ] 费用申请驳回、撤回、取消时释放预算。 +- [ ] 费用申请转报销时保留预算来源。 +- [ ] 报销提交时校验预算归属和可用余额。 +- [ ] 报销审批通过时核销预算。 +- [ ] 报销退回时回滚预算状态。 +- [ ] 报销详情展示预算占用和核销信息。 +- [ ] 申请详情展示预算占用和剩余额度。 +- [ ] 预算中心页面展示执行率、已占用、已核销、可用余额。 +- [ ] 预算台账展示每笔来源单据和交易类型。 +- [ ] 首页预算执行率改为后端真实数据。 +- [ ] 本体识别支持预算维度字段。 +- [ ] AI对话能解释预算不足、预算归属缺失、超预算原因。 +- [ ] 添加后端单元测试。 +- [ ] 添加前端预算视图测试。 +- [ ] 添加申请到报销的端到端预算验收场景。 + +## 验收场景 + +- [ ] 有预算时,费用申请提交成功并预占预算。 +- [ ] 预算不足时,申请提交被阻断或进入超预算复核。 +- [ ] 申请驳回后,预算预占被释放。 +- [ ] 申请审批通过后,预算仍保持预占。 +- [ ] 申请转报销后,报销单继承预算来源。 +- [ ] 报销审批通过后,预算从预占转为核销。 +- [ ] 报销退回后,预算核销回滚。 +- [ ] 预算中心能看到完整交易台账。 +- [ ] 首页预算执行率与预算中心汇总一致。 + diff --git a/document/development/budget-center/README.md b/document/development/budget-center/README.md new file mode 100644 index 0000000..117d829 --- /dev/null +++ b/document/development/budget-center/README.md @@ -0,0 +1,58 @@ +# 预算中心开发总览 + +## 目标 + +预算中心先作为费控平台的前置底座建设,优先打通: + +```text +预算编制 -> 预算可用额度 -> 费用申请预占 -> 报销核销 -> 释放/调整 -> 预算看板 +``` + +第一版不追求完整预算编制系统,而是先让申请、报销、审批、付款、归档都有真实预算口径。 + +## 当前项目基础 + +- 员工和组织已有 `cost_center` 成本中心字段,可作为预算归属维度。 +- 报销单已有部门、项目、费用类型、金额、状态等字段,可接入预算核销。 +- 首页已有静态预算执行率展示,但还不是后端真实预算数据。 +- 费用申请已有前端意图识别和申请草稿痕迹,但预算占用还没有真实台账。 + +## 第一版预算中心范围 + +必须做: + +- 预算主体:部门、成本中心、项目、费用科目。 +- 预算期间:月度、季度、年度。 +- 预算额度:总额、已占用、已核销、已释放、可用余额。 +- 预算台账:每一次占用、核销、释放、调整都落账。 +- 申请联动:费用申请提交时预占预算,驳回/撤回时释放。 +- 报销联动:报销提交或审批通过时核销预算。 +- 风险拦截:预算不足、超预算、缺预算归属时阻断或进入复核。 +- 预算中心页面:列表、详情、台账、执行率、异常预算。 + +暂缓: + +- 完整预算编制审批流。 +- 多版本预算测算。 +- 外部 ERP 预算接口。 +- 真正多币种预算。 +- 复杂滚动预算和预测模型。 + +## 关键原则 + +- 预算中心是独立业务域,不塞进报销 Service。 +- 所有预算变化必须通过预算交易台账记录。 +- 不直接改写已用金额,必须由交易汇总得到。 +- 申请、报销、付款只是预算事件来源。 +- 预算不足的判断必须来自后端,不依赖前端显示。 + +## 7天开发路径 + +- Day 1:预算模型、状态机、接口契约。 +- Day 2:预算中心页面、列表、详情、台账视图。 +- Day 3:预算占用/释放/核销服务。 +- Day 4:费用申请与报销联动预算。 +- Day 5:审批、付款、归档中的预算状态传递。 +- Day 6:预算看板、本体识别、AI提示。 +- Day 7:端到端验收、演示数据、测试补齐。 + diff --git a/document/development/budget-center/budget-center-ui-prototype.html b/document/development/budget-center/budget-center-ui-prototype.html new file mode 100644 index 0000000..4d8c4e5 --- /dev/null +++ b/document/development/budget-center/budget-center-ui-prototype.html @@ -0,0 +1,1179 @@ + + + + + + X-Financial 预算中心 UI 原型 + + + +
+ + +
+
+
+

预算中心

+

按部门、成本中心、项目和费用科目管理预算,申请预占与报销核销统一落入台账。

+
+
+ + + +
+
+ +
+
+
+ 年度预算总额 + +
+ ¥ 8,260,000 +
+ 覆盖 18 个成本中心 + +12% +
+
+
+
+ 已预占 + +
+ ¥ 1,384,200 +
+ 来自 42 个申请 + 16.8% +
+
+
+
+ 已核销 + +
+ ¥ 3,962,580 +
+ 审批通过报销 + 48.0% +
+
+
+
+ 可用余额 + +
+ ¥ 2,913,220 +
+ 按实时台账计算 + 35.2% +
+
+
+
+ 预警预算 + +
+ 7 +
+ 执行率超过 80% + 需关注 +
+
+
+
+ 超预算事项 + +
+ 3 +
+ 等待复核 + 阻断 +
+
+
+ +
+
+
+
+ + + +
+
+ + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
预算编号归属费用科目预算总额已预占已核销可用余额执行率状态
+
+ BUD-2026-CC2108-TRAVEL + 2026 年度 +
+
解决方案部 / CC-2108差旅费¥ 860,000¥ 168,000¥ 451,600¥ 240,400 +
+
72%
+
预警
+
+ BUD-2026-CC4102-MEETING + 2026 年度 +
+
市场部 / CC-4102会议培训费¥ 520,000¥ 96,000¥ 184,500¥ 239,500 +
+
54%
+
正常
+
+ BUD-2026-CC6112-PURCHASE + 2026 年度 +
+
交付中心 / CC-6112办公采购¥ 280,000¥ 72,600¥ 226,800- ¥ 19,400 +
+
107%
+
超预算
+
+ BUD-2026-PRJ-A09-TRAVEL + 2026 Q2 +
+
华东电力项目 / PRJ-A09项目差旅¥ 420,000¥ 63,200¥ 128,900¥ 227,900 +
+
46%
+
正常
+
+ BUD-2026-CC2120-ENT + 2026-05 月度 +
+
财务部 / CC-2120商务招待¥ 90,000¥ 12,800¥ 77,200¥ 0 +
+
100%
+
冻结
+
+
+ + +
+ +

移动端会保留指标、筛选、预算列表和详情入口,台账与风险信息折叠到预算详情中。

+
+
+ + diff --git a/document/development/budget-center/day_1_budget_foundation.md b/document/development/budget-center/day_1_budget_foundation.md new file mode 100644 index 0000000..59cf164 --- /dev/null +++ b/document/development/budget-center/day_1_budget_foundation.md @@ -0,0 +1,105 @@ +# Day 1 - 预算模型与接口契约 + +## 目标 + +先把预算中心的数据边界和接口边界定稳,避免后续把预算逻辑散落在申请、报销、审批和付款模块里。 + +## 开发任务 + +- [ ] 新增预算模型设计。 +- [ ] 新增预算交易台账设计。 +- [ ] 新增预算服务边界设计。 +- [ ] 新增预算接口契约。 +- [ ] 新增预算状态与交易类型常量。 +- [ ] 明确申请、报销、付款对预算服务的调用点。 + +## 建议模型 + +预算额度: + +```text +BudgetAllocation +- id +- budget_no +- fiscal_year +- period_type +- period_key +- department_id +- department_name +- cost_center +- project_code +- subject_code +- subject_name +- original_amount +- adjusted_amount +- status +- warning_threshold +- created_at +- updated_at +``` + +预算交易: + +```text +BudgetTransaction +- id +- transaction_no +- allocation_id +- source_type +- source_id +- source_no +- transaction_type +- amount +- before_available_amount +- after_available_amount +- operator +- reason +- created_at +``` + +交易类型: + +```text +init 初始化 +adjust 调整 +reserve 预占 +release 释放 +consume 核销 +rollback 回滚 +freeze 冻结 +unfreeze 解冻 +``` + +预算汇总字段由交易汇总得到: + +```text +total_amount = original_amount + adjusted_amount +reserved_amount = reserve - release +consumed_amount = consume - rollback +available_amount = total_amount - reserved_amount - consumed_amount +``` + +## 接口契约 + +```text +GET /api/v1/budgets/allocations +POST /api/v1/budgets/allocations +GET /api/v1/budgets/allocations/{id} +GET /api/v1/budgets/allocations/{id}/transactions +POST /api/v1/budgets/allocations/{id}/adjust +POST /api/v1/budgets/check +POST /api/v1/budgets/reserve +POST /api/v1/budgets/release +POST /api/v1/budgets/consume +POST /api/v1/budgets/rollback +GET /api/v1/budgets/summary +``` + +## 验收 + +- [ ] 能创建一条部门月度预算。 +- [ ] 能查询预算列表和详情。 +- [ ] 能查询预算台账。 +- [ ] 能根据部门、成本中心、项目、费用科目定位预算。 +- [ ] 预算不足时 `check` 接口能返回明确原因。 + diff --git a/document/development/budget-center/day_2_budget_center_ui.md b/document/development/budget-center/day_2_budget_center_ui.md new file mode 100644 index 0000000..7917c88 --- /dev/null +++ b/document/development/budget-center/day_2_budget_center_ui.md @@ -0,0 +1,82 @@ +# Day 2 - 预算中心页面 + +## 目标 + +新增预算中心作为独立主菜单,让预算不再只是首页指标,而是可操作、可追踪、可解释的费控入口。 + +## 页面入口 + +主菜单建议: + +```text +费用申请 +报销中心 +审批中心 +预算中心 +付款中心 +归档中心 +经营分析 +``` + +## 页面结构 + +顶部指标: + +- 预算总额 +- 已预占 +- 已核销 +- 可用余额 +- 超预算事项 +- 预警预算数 + +列表字段: + +- 预算编号 +- 预算期间 +- 部门 +- 成本中心 +- 项目 +- 费用科目 +- 预算总额 +- 已预占 +- 已核销 +- 可用余额 +- 执行率 +- 状态 + +筛选条件: + +- 年度 +- 月份/季度 +- 部门 +- 成本中心 +- 项目 +- 费用科目 +- 状态 + +详情页: + +- 基本信息 +- 执行概览 +- 来源单据 +- 交易台账 +- 风险提示 +- 调整记录 + +## 操作 + +- 新增预算 +- 调整预算 +- 冻结预算 +- 查看台账 +- 查看关联申请 +- 查看关联报销 + +## 验收 + +- [ ] 预算中心能从主菜单进入。 +- [ ] 列表能展示后端预算数据。 +- [ ] 点击预算能进入详情。 +- [ ] 详情能展示交易台账。 +- [ ] 首页预算执行率能跳转到预算中心。 + diff --git a/document/development/budget-center/day_3_budget_transaction_service.md b/document/development/budget-center/day_3_budget_transaction_service.md new file mode 100644 index 0000000..99c79de --- /dev/null +++ b/document/development/budget-center/day_3_budget_transaction_service.md @@ -0,0 +1,55 @@ +# Day 3 - 预算占用、释放、核销服务 + +## 目标 + +把预算变化统一收敛到预算服务,申请、报销、付款都只能通过预算服务改变预算状态。 + +## 服务能力 + +预算检查: + +- 校验预算归属是否存在。 +- 校验预算是否被冻结。 +- 校验可用余额是否充足。 +- 返回超预算金额和处理建议。 + +预算预占: + +- 用于费用申请提交。 +- 写入 `reserve` 交易。 +- 记录来源单据。 + +预算释放: + +- 用于申请撤回、退回、驳回、取消。 +- 写入 `release` 交易。 +- 必须找到原始预占来源。 + +预算核销: + +- 用于报销审批通过。 +- 写入 `consume` 交易。 +- 如果来源申请已有预占,应先释放预占或转换为核销,不能重复占用。 + +预算回滚: + +- 用于报销退回或撤销审批。 +- 写入 `rollback` 交易。 + +## 关键防错 + +- 同一来源单据不能重复预占。 +- 同一报销单不能重复核销。 +- 释放金额不能超过原预占金额。 +- 核销金额不能超过可用余额加当前来源预占余额。 +- 所有预算交易必须有来源单据和操作人。 + +## 验收 + +- [ ] 预算预占后可用余额减少。 +- [ ] 预算释放后可用余额恢复。 +- [ ] 预算核销后已核销金额增加。 +- [ ] 重复预占会被阻断。 +- [ ] 重复核销会被阻断。 +- [ ] 台账能解释每一次余额变化。 + diff --git a/document/development/budget-center/day_4_application_claim_integration.md b/document/development/budget-center/day_4_application_claim_integration.md new file mode 100644 index 0000000..d0ebc75 --- /dev/null +++ b/document/development/budget-center/day_4_application_claim_integration.md @@ -0,0 +1,58 @@ +# Day 4 - 费用申请与报销联动预算 + +## 目标 + +让预算成为申请和报销之间的硬约束,先申请、再占用、再报销、再核销。 + +## 费用申请联动 + +提交申请时: + +- 根据申请人部门、成本中心、项目、费用科目定位预算。 +- 预算充足则预占。 +- 预算不足则阻断或进入超预算复核。 +- 申请详情展示预算占用结果。 + +申请退回/驳回/撤回时: + +- 释放对应预算预占。 +- 记录释放原因。 + +申请审批通过时: + +- 保持预算预占。 +- 允许转报销。 + +申请转报销时: + +- 报销单继承申请预算来源。 +- 报销金额默认不超过申请金额。 +- 超过申请金额时进入风险提示或复核。 + +## 报销联动 + +报销提交时: + +- 校验是否需要事前申请。 +- 校验是否有关联已通过申请。 +- 校验预算来源是否存在。 + +报销审批通过时: + +- 将预算预占转为核销。 +- 记录核销台账。 + +报销退回时: + +- 回滚核销。 +- 视状态保留或释放预占。 + +## 验收 + +- [ ] 有预算的申请提交后形成预占。 +- [ ] 预算不足的申请不能直接提交。 +- [ ] 驳回申请释放预算。 +- [ ] 已通过申请能转报销。 +- [ ] 报销审批通过后预算转核销。 +- [ ] 未关联预算的报销不能绕过预算校验。 + diff --git a/document/development/budget-center/day_5_approval_payment_archive_budget.md b/document/development/budget-center/day_5_approval_payment_archive_budget.md new file mode 100644 index 0000000..d73666f --- /dev/null +++ b/document/development/budget-center/day_5_approval_payment_archive_budget.md @@ -0,0 +1,52 @@ +# Day 5 - 审批、付款、归档中的预算口径 + +## 目标 + +预算信息不能只停留在申请和报销页面,还要贯穿审批、付款和归档。 + +## 审批中心 + +审批列表增加预算提示: + +- 是否预算内 +- 是否超预算 +- 已预占金额 +- 可用余额 +- 预算归属 + +审批详情增加预算区块: + +- 当前单据金额 +- 对应预算额度 +- 已预占 +- 已核销 +- 剩余额度 +- 超预算原因 + +## 付款中心预留 + +第一版付款中心可以暂缓实现完整页面,但预算中心需要为付款预留: + +- 付款来源单据 +- 付款金额 +- 付款状态 +- 是否已核销预算 +- 是否存在预算异常 + +## 归档中心 + +归档包需要包含预算信息: + +- 预算归属 +- 预算交易流水 +- 申请预占记录 +- 报销核销记录 +- 超预算审批意见 + +## 验收 + +- [ ] 审批人能看到预算是否充足。 +- [ ] 超预算审批能看到超额金额。 +- [ ] 归档详情能看到预算台账摘要。 +- [ ] 预算异常不会在付款/归档阶段丢失。 + diff --git a/document/development/budget-center/day_6_budget_analytics_ontology.md b/document/development/budget-center/day_6_budget_analytics_ontology.md new file mode 100644 index 0000000..4b0a1f2 --- /dev/null +++ b/document/development/budget-center/day_6_budget_analytics_ontology.md @@ -0,0 +1,51 @@ +# Day 6 - 预算看板、本体识别与AI解释 + +## 目标 + +让预算中心不只是数据表,还能被 AI 对话、本体识别和经营分析调用。 + +## 看板指标 + +- 本月预算总额 +- 本月已预占 +- 本月已核销 +- 本月可用余额 +- 部门预算排行 +- 费用科目执行率 +- 超预算事项数量 +- 预算预警事项数量 + +## 本体字段 + +新增或强化字段: + +```text +cost_center +project_code +budget_subject +budget_period +budget_amount +available_amount +reserved_amount +consumed_amount +over_budget +budget_warning +``` + +## AI解释能力 + +需要支持的问题: + +- 这个申请为什么预算不足? +- 这个报销占用了哪个预算? +- 本月哪个部门预算快超了? +- 某个项目还剩多少预算? +- 超预算申请需要谁审批? + +## 验收 + +- [ ] 本体能识别预算相关问题。 +- [ ] AI能解释预算不足原因。 +- [ ] 首页预算看板来自后端真实汇总。 +- [ ] 预算中心和AI回答的金额一致。 + diff --git a/document/development/budget-center/day_7_hardening_acceptance.md b/document/development/budget-center/day_7_hardening_acceptance.md new file mode 100644 index 0000000..baf0d19 --- /dev/null +++ b/document/development/budget-center/day_7_hardening_acceptance.md @@ -0,0 +1,64 @@ +# Day 7 - 联调、测试与演示验收 + +## 目标 + +冻结新增需求,只修预算闭环缺口,确保演示链路稳定。 + +## 端到端验收链路 + +链路一:预算内申请到报销 + +```text +创建预算 -> 发起费用申请 -> 预占预算 -> 审批通过 +-> 转报销 -> 报销审批通过 -> 核销预算 -> 归档 +``` + +链路二:预算不足 + +```text +创建低额度预算 -> 发起高金额申请 -> 预算不足 +-> 阻断提交或进入超预算复核 -> 审批意见留痕 +``` + +链路三:申请驳回释放预算 + +```text +申请提交 -> 预算预占 -> 审批驳回 -> 预算释放 -> 台账可追溯 +``` + +链路四:重复操作防护 + +```text +重复提交 / 重复审批 / 重复核销 -> 后端阻断 -> 台账不重复 +``` + +## 测试要求 + +- [ ] 后端预算服务单元测试。 +- [ ] 申请预算预占测试。 +- [ ] 报销预算核销测试。 +- [ ] 预算不足阻断测试。 +- [ ] 前端预算中心列表测试。 +- [ ] 前端预算详情台账测试。 +- [ ] 首页预算汇总测试。 + +## 演示数据 + +至少准备: + +- 一个预算充足的部门预算。 +- 一个预算不足的部门预算。 +- 一个项目预算。 +- 一个会议培训大额预算。 +- 一个已经预占的申请。 +- 一个已经核销的报销。 +- 一个超预算待审批事项。 + +## 最终验收 + +- [ ] 预算中心能解释每一分钱从哪里来、到哪里去。 +- [ ] 费用申请不能绕过预算。 +- [ ] 报销审批不能绕过预算。 +- [ ] 审批、归档、看板显示同一套预算数据。 +- [ ] 演示链路可连续跑通。 + diff --git a/server/rules/risk-rules/risk.expense.travel.generated_20260525155459576686.json b/server/rules/risk-rules/risk.expense.travel.generated_20260525155459576686.json new file mode 100644 index 0000000..ecf5ca6 --- /dev/null +++ b/server/rules/risk-rules/risk.expense.travel.generated_20260525155459576686.json @@ -0,0 +1,102 @@ +{ + "schema_version": "2.0", + "rule_code": "risk.expense.travel.generated_20260525155459576686", + "name": "住宿日期与差旅行程不匹配", + "description": "面向业务用户的说明", + "enabled": true, + "requires_attachment": true, + "risk_dimension": "natural_language_rule", + "risk_category": "差旅费", + "ontology_signal": "natural_language_risk", + "evaluator": "template_rule", + "template_key": "field_required_v1", + "applies_to": { + "domains": [ + "expense" + ], + "expense_categories": [ + "travel" + ] + }, + "inputs": { + "fields": [ + { + "key": "attachment.hotel_city", + "label": "住宿城市", + "type": "text", + "source": "attachment" + }, + { + "key": "claim.location", + "label": "申报地点", + "type": "text", + "source": "claim" + }, + { + "key": "attachment.route_cities", + "label": "行程城市", + "type": "list", + "source": "attachment" + }, + { + "key": "claim.reason", + "label": "报销事由", + "type": "text", + "source": "claim" + } + ] + }, + "params": { + "template_key": "field_required_v1", + "field_keys": [ + "attachment.hotel_city", + "claim.location", + "attachment.route_cities", + "claim.reason" + ], + "condition_summary": "检查住宿城市、申报地点、行程城市是否满足必填和完整性要求", + "natural_language": "差旅住宿报销时,先确认已上传住宿发票或酒店水单;再读取报销事由、申报目的地、住宿城市、开票日期和行程城市。若住宿票据显示的城市不在本次差旅行程范围内,或住宿发生时间明显早于出差开始、晚于出差结束,且没有延期、改签、临时任务等说明,则标记为高风险,要求补充住宿原因、行程证明或重新提交票据。", + "required_fields": [ + "attachment.hotel_city", + "claim.location", + "attachment.route_cities", + "claim.reason" + ] + }, + "outcomes": { + "pass": { + "severity": "none", + "action": "continue" + }, + "fail": { + "severity": "medium", + "action": "manual_review" + } + }, + "metadata": { + "owner": "admin", + "stability": "generated_draft", + "source_ref": "自然语言风险规则", + "created_at": "2026-05-25T15:54:59.576686+00:00", + "created_by": "admin", + "requires_attachment": true, + "rule_title": "住宿日期与差旅行程不匹配", + "expense_category": "travel", + "expense_category_label": "差旅费", + "natural_language": "差旅住宿报销时,先确认已上传住宿发票或酒店水单;再读取报销事由、申报目的地、住宿城市、开票日期和行程城市。若住宿票据显示的城市不在本次差旅行程范围内,或住宿发生时间明显早于出差开始、晚于出差结束,且没有延期、改签、临时任务等说明,则标记为高风险,要求补充住宿原因、行程证明或重新提交票据。", + "business_explanation": "面向业务用户的说明", + "condition_summary": "检查住宿城市、申报地点、行程城市是否满足必填和完整性要求", + "flow": { + "start": "提交业务单据", + "evidence": "读取住宿城市、申报地点、行程城市", + "decision": "检查住宿城市、申报地点、行程城市是否满足必填和完整性要求", + "pass": "继续流转", + "fail": "提示风险" + }, + "risk_level": "medium", + "risk_level_label": "中风险", + "risk_level_updated_at": "2026-05-25T16:05:15.691638+00:00" + }, + "flow_diagram_svg": "\n 住宿日期与差旅行程不匹配流程说明\n 风险规则只读流程图,展示从业务单据提交到风险复核的判断路径。\n \n \n \n \n \n \n \n RULE FLOW\n \n \n \n 业务输入\n 提交业务单据\n \n \n \n \n 字段取数\n 读取字段证据\n \n \n \n 判断依据\n 检查住宿城市、申\n 报地点、行程城…\n \n \n \n \n 继续流转\n 继续流转\n \n \n \n \n 进入复核\n 提示风险\n \n \n \n BASIS\n 检查住宿城市、申报地点、行程城市是否满足必…\n \n \n \n \n \n \n \n", + "severity": "medium" +} diff --git a/server/rules/risk-rules/risk.expense.travel.generated_20260526071430001717.json b/server/rules/risk-rules/risk.expense.travel.generated_20260526071430001717.json new file mode 100644 index 0000000..d540751 --- /dev/null +++ b/server/rules/risk-rules/risk.expense.travel.generated_20260526071430001717.json @@ -0,0 +1,99 @@ +{ + "schema_version": "2.0", + "rule_code": "risk.expense.travel.generated_20260526071430001717", + "name": "123", + "description": "当差旅费业务满足“差旅报销时,先检查是否上传了交通票据、住宿票据或其他差旅附件;再读取发票号码、购买方名称、商品服务名称、票据全文、报销人和部门。若票据已上传但发票号码、购买方名称或商品服务名称缺失,且报销事由、人员和部门信息能够说明费用归属,则标记为低风险,提醒补齐票据要素或重新上传清晰附件。”时,系统会按中风险进行提示,并要求经办人或审核人补充核对依据。", + "enabled": true, + "requires_attachment": false, + "risk_dimension": "natural_language_rule", + "risk_category": "差旅费", + "ontology_signal": "natural_language_risk", + "evaluator": "template_rule", + "template_key": "field_required_v1", + "semantic_type": null, + "applies_to": { + "domains": [ + "expense" + ], + "expense_categories": [ + "travel" + ] + }, + "inputs": { + "fields": [ + { + "key": "attachment.hotel_city", + "label": "住宿城市", + "type": "text", + "source": "attachment" + }, + { + "key": "attachment.invoice_no", + "label": "发票号码", + "type": "text", + "source": "attachment" + }, + { + "key": "attachment.goods_name", + "label": "商品服务名称", + "type": "text", + "source": "attachment" + }, + { + "key": "claim.reason", + "label": "报销事由", + "type": "text", + "source": "claim" + } + ] + }, + "params": { + "template_key": "field_required_v1", + "field_keys": [ + "attachment.hotel_city", + "attachment.invoice_no", + "attachment.goods_name", + "claim.reason" + ], + "condition_summary": "检查住宿城市、发票号码、商品服务名称是否满足必填和完整性要求", + "natural_language": "差旅报销时,先检查是否上传了交通票据、住宿票据或其他差旅附件;再读取发票号码、购买方名称、商品服务名称、票据全文、报销人和部门。若票据已上传但发票号码、购买方名称或商品服务名称缺失,且报销事由、人员和部门信息能够说明费用归属,则标记为低风险,提醒补齐票据要素或重新上传清晰附件。", + "required_fields": [ + "attachment.hotel_city", + "attachment.invoice_no", + "attachment.goods_name", + "claim.reason" + ] + }, + "outcomes": { + "pass": { + "severity": "none", + "action": "continue" + }, + "fail": { + "severity": "medium", + "action": "manual_review" + } + }, + "metadata": { + "owner": "admin", + "stability": "generated_draft", + "source_ref": "自然语言风险规则", + "created_at": "2026-05-26T07:14:30.001717+08:00", + "created_by": "admin", + "requires_attachment": false, + "rule_title": "123", + "expense_category": "travel", + "expense_category_label": "差旅费", + "natural_language": "差旅报销时,先检查是否上传了交通票据、住宿票据或其他差旅附件;再读取发票号码、购买方名称、商品服务名称、票据全文、报销人和部门。若票据已上传但发票号码、购买方名称或商品服务名称缺失,且报销事由、人员和部门信息能够说明费用归属,则标记为低风险,提醒补齐票据要素或重新上传清晰附件。", + "business_explanation": "当差旅费业务满足“差旅报销时,先检查是否上传了交通票据、住宿票据或其他差旅附件;再读取发票号码、购买方名称、商品服务名称、票据全文、报销人和部门。若票据已上传但发票号码、购买方名称或商品服务名称缺失,且报销事由、人员和部门信息能够说明费用归属,则标记为低风险,提醒补齐票据要素或重新上传清晰附件。”时,系统会按中风险进行提示,并要求经办人或审核人补充核对依据。", + "condition_summary": "检查住宿城市、发票号码、商品服务名称是否满足必填和完整性要求", + "flow": { + "start": "差旅费单据提交", + "evidence": "读取住宿城市、发票号码、商品服务名称", + "decision": "检查住宿城市、发票号码、商品服务名称是否满足必填和完整性要求", + "pass": "未命中风险,继续业务流转", + "fail": "命中中风险,提示复核" + } + }, + "flow_diagram_svg": "\n 123流程说明\n 风险规则只读流程图,展示从业务单据提交到风险复核的判断路径。\n \n \n \n \n \n \n \n RULE FLOW\n \n \n \n 业务输入\n 差旅费单据提交\n \n \n \n \n 字段取数\n 读取字段证据\n \n \n \n 判断依据\n 检查住宿城市、发\n 票号码、商品服…\n \n \n \n \n 继续流转\n 未命中风险,继续业…\n \n \n \n \n 进入复核\n 命中中风险,提示复核\n \n \n \n BASIS\n 检查住宿城市、发票号码、商品服务名称是否满…\n \n \n \n \n \n \n \n" +} diff --git a/server/rules/risk-rules/risk.expense.travel.generated_20260526071633392020.json b/server/rules/risk-rules/risk.expense.travel.generated_20260526071633392020.json new file mode 100644 index 0000000..231361b --- /dev/null +++ b/server/rules/risk-rules/risk.expense.travel.generated_20260526071633392020.json @@ -0,0 +1,170 @@ +{ + "schema_version": "2.0", + "rule_code": "risk.expense.travel.generated_20260526071633392020", + "name": "222", + "description": "当差旅费业务满足“差旅报销时,先检查是否已上传交通票据、住宿票据或其他能识别城市的附件;再读取申报目的地、明细发生地点、交通票行程城市和住宿发票城市。若交通票或住宿票据中的城市均无法与申报目的地、明细地点形成一致关系,且报销事由中没有说明绕行、跨城办事或临时改签原因,则标记为高风险,要求补充行程说明或退回修改。”时,系统会按中风险进行提示,并要求经办人或审核人补充核对依据。", + "enabled": true, + "requires_attachment": false, + "risk_dimension": "natural_language_rule", + "risk_category": "差旅费", + "ontology_signal": "natural_language_risk", + "evaluator": "template_rule", + "template_key": "field_compare_v1", + "semantic_type": "travel_route_city_consistency", + "applies_to": { + "domains": [ + "expense" + ], + "expense_categories": [ + "travel" + ] + }, + "inputs": { + "fields": [ + { + "key": "attachment.route_cities", + "label": "行程城市", + "type": "list", + "source": "attachment" + }, + { + "key": "attachment.hotel_city", + "label": "住宿城市", + "type": "text", + "source": "attachment" + }, + { + "key": "claim.location", + "label": "申报地点", + "type": "text", + "source": "claim" + }, + { + "key": "item.item_location", + "label": "明细地点", + "type": "text", + "source": "item" + }, + { + "key": "employee.location", + "label": "员工常驻地", + "type": "text", + "source": "employee" + }, + { + "key": "claim.reason", + "label": "报销事由", + "type": "text", + "source": "claim" + }, + { + "key": "item.item_reason", + "label": "明细事由", + "type": "text", + "source": "item" + } + ] + }, + "params": { + "template_key": "field_compare_v1", + "field_keys": [ + "attachment.route_cities", + "attachment.hotel_city", + "claim.location", + "item.item_location", + "employee.location", + "claim.reason", + "item.item_reason" + ], + "condition_summary": "判断公式:A=交通票行程城市∪住宿发票城市,B=申报目的地∪明细发生地点,C=员工常驻地/合理出发地。若A或B为空则要求补充识别;若A与B无交集且无合理说明,或票据路线中存在不属于B∪C的额外城市,则命中目的地不一致/中途周转异常风险。", + "natural_language": "差旅报销时,先检查是否已上传交通票据、住宿票据或其他能识别城市的附件;再读取申报目的地、明细发生地点、交通票行程城市和住宿发票城市。若交通票或住宿票据中的城市均无法与申报目的地、明细地点形成一致关系,且报销事由中没有说明绕行、跨城办事或临时改签原因,则标记为高风险,要求补充行程说明或退回修改。", + "semantic_type": "travel_route_city_consistency", + "attachment_city_fields": [ + "attachment.route_cities", + "attachment.hotel_city" + ], + "reference_city_fields": [ + "claim.location", + "item.item_location" + ], + "home_city_fields": [ + "employee.location" + ], + "exception_fields": [ + "claim.reason", + "item.item_reason" + ], + "exception_keywords": [ + "绕行", + "跨城办事", + "跨城", + "临时改签", + "改签", + "变更" + ], + "keywords": [], + "route_anomaly_policy": "flag_unexpected_intermediate_cities", + "exception_handling": "exception_text_is_evidence_not_auto_pass_for_route_anomaly", + "formula": "A=UNION(attachment.route_cities, attachment.hotel_city); B=UNION(claim.location, item.item_location); C=UNION(employee.location); HIT WHEN (A∩B=∅ AND NOT CONTAINS_ANY(exception_fields, exception_keywords)) OR EXISTS(city IN A WHERE city NOT IN B∪C)", + "conditions": [ + { + "left_group": [ + "attachment.route_cities", + "attachment.hotel_city" + ], + "operator": "route_city_consistency", + "right_group": [ + "claim.location", + "item.item_location" + ], + "home_group": [ + "employee.location" + ], + "exception_fields": [ + "claim.reason", + "item.item_reason" + ], + "exception_keywords": [ + "绕行", + "跨城办事", + "跨城", + "临时改签", + "改签", + "变更" + ] + } + ] + }, + "outcomes": { + "pass": { + "severity": "none", + "action": "continue" + }, + "fail": { + "severity": "medium", + "action": "manual_review" + } + }, + "metadata": { + "owner": "admin", + "stability": "generated_draft", + "source_ref": "自然语言风险规则", + "created_at": "2026-05-26T07:16:33.392020+08:00", + "created_by": "admin", + "requires_attachment": false, + "rule_title": "222", + "expense_category": "travel", + "expense_category_label": "差旅费", + "natural_language": "差旅报销时,先检查是否已上传交通票据、住宿票据或其他能识别城市的附件;再读取申报目的地、明细发生地点、交通票行程城市和住宿发票城市。若交通票或住宿票据中的城市均无法与申报目的地、明细地点形成一致关系,且报销事由中没有说明绕行、跨城办事或临时改签原因,则标记为高风险,要求补充行程说明或退回修改。", + "business_explanation": "当差旅费业务满足“差旅报销时,先检查是否已上传交通票据、住宿票据或其他能识别城市的附件;再读取申报目的地、明细发生地点、交通票行程城市和住宿发票城市。若交通票或住宿票据中的城市均无法与申报目的地、明细地点形成一致关系,且报销事由中没有说明绕行、跨城办事或临时改签原因,则标记为高风险,要求补充行程说明或退回修改。”时,系统会按中风险进行提示,并要求经办人或审核人补充核对依据。", + "condition_summary": "判断公式:A=交通票行程城市∪住宿发票城市,B=申报目的地∪明细发生地点,C=员工常驻地/合理出发地。若A或B为空则要求补充识别;若A与B无交集且无合理说明,或票据路线中存在不属于B∪C的额外城市,则命中目的地不一致/中途周转异常风险。", + "flow": { + "start": "差旅报销单据提交,并上传交通票据、住宿票据或其他可识别城市的附件", + "evidence": "读取员工常驻地、申报目的地、明细发生地点、交通票行程城市、住宿发票城市和报销事由", + "decision": "附件城市是否覆盖申报行程,且票据路线是否出现申报目的地和常驻地之外的中转城市", + "pass": "票据城市覆盖申报行程,且未出现申报目的地和常驻地之外的额外城市", + "fail": "票据路线存在目的地不一致或额外中转城市,命中中风险并要求补充说明或退回修改" + } + }, + "flow_diagram_svg": "\n 222流程说明\n 风险规则只读流程图,展示从业务单据提交到风险复核的判断路径。\n \n \n \n \n \n \n \n RULE FLOW\n \n \n \n 业务输入\n 差旅报销单据提交,…\n \n \n \n \n 字段取数\n 读取字段证据\n \n \n \n 判断依据\n 附件城市是否覆盖\n 申报行程,且票…\n \n \n \n \n 继续流转\n 票据城市覆盖申报行…\n \n \n \n \n 进入复核\n 票据路线存在目的地…\n \n \n \n BASIS\n 判断公式:A=交通票行程城市∪住宿发票城市…\n \n \n \n \n \n \n \n" +} diff --git a/server/src/app/api/v1/endpoints/agent_assets.py b/server/src/app/api/v1/endpoints/agent_assets.py index 570a52f..79513a3 100644 --- a/server/src/app/api/v1/endpoints/agent_assets.py +++ b/server/src/app/api/v1/endpoints/agent_assets.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import Annotated -from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, status +from fastapi import APIRouter, BackgroundTasks, Body, Depends, Header, HTTPException, Query, status from fastapi.responses import FileResponse from sqlalchemy.orm import Session @@ -14,6 +14,7 @@ from app.api.deps import ( require_rule_editor_user, require_rule_reviewer_user, ) +from app.db.session import get_session_factory from app.schemas.agent_asset import ( AgentAssetCreate, AgentAssetListItem, @@ -26,6 +27,7 @@ from app.schemas.agent_asset import ( AgentAssetRiskRuleEnabledUpdate, AgentAssetRiskRuleGenerateRequest, AgentAssetRiskRuleLatestTestSummary, + AgentAssetRiskRuleLevelUpdate, AgentAssetRiskRuleReportRequest, AgentAssetRiskRuleReturnRequest, AgentAssetRiskRuleSampleTestRequest, @@ -43,7 +45,7 @@ from app.schemas.agent_asset import ( ) from app.schemas.common import ErrorResponse from app.services.agent_assets import AgentAssetService -from app.services.risk_rule_generation import RiskRuleGenerationService +from app.services.risk_rule_generation_jobs import RiskRuleGenerationJobService router = APIRouter(prefix="/agent-assets") DbSession = Annotated[Session, Depends(get_db)] @@ -71,6 +73,25 @@ def _handle_asset_error(exc: Exception) -> None: raise exc +def _complete_risk_rule_generation_task( + asset_id: str, + payload: dict, + actor: str, + request_id: str | None, +) -> None: + db = get_session_factory()() + try: + body = AgentAssetRiskRuleGenerateRequest.model_validate(payload) + RiskRuleGenerationJobService(db).complete_rule_asset_generation( + asset_id, + body, + actor=actor, + request_id=request_id, + ) + finally: + db.close() + + @router.get( "", response_model=list[AgentAssetListItem], @@ -284,6 +305,7 @@ def save_agent_asset_rule_json( ) def generate_agent_asset_risk_rule( payload: AgentAssetRiskRuleGenerateRequest, + background_tasks: BackgroundTasks, current_user: RuleEditorUser, db: DbSession, x_actor: ActorHeader = None, @@ -291,11 +313,18 @@ def generate_agent_asset_risk_rule( ) -> AgentAssetRead: try: actor = (x_actor or current_user.name or "system").strip() or "system" - asset_id = RiskRuleGenerationService(db).generate_rule_asset( + asset_id = RiskRuleGenerationJobService(db).enqueue_rule_asset_generation( payload, actor=actor, request_id=x_request_id, ) + background_tasks.add_task( + _complete_risk_rule_generation_task, + asset_id, + payload.model_dump(mode="json"), + actor, + x_request_id, + ) asset = AgentAssetService(db).get_asset(asset_id) if asset is None: raise LookupError("Asset not found") @@ -736,6 +765,27 @@ def set_agent_asset_risk_rule_enabled( _handle_asset_error(exc) +@router.post( + "/{asset_id}/risk-rule-level", + response_model=AgentAssetRead, + summary="风险规则风险等级已由评分模型接管", + description="风险规则等级和分数由自然语言规则评分模型生成,不再允许人工调整。", +) +def set_agent_asset_risk_rule_level( + asset_id: str, + payload: AgentAssetRiskRuleLevelUpdate, + current_user: RuleEditorUser, + db: DbSession, + x_actor: ActorHeader = None, + x_request_id: RequestIdHeader = None, +) -> AgentAssetRead: + try: + del asset_id, payload, current_user, db, x_actor, x_request_id + raise ValueError("风险等级和分数由评分模型自动计算,不能手动修改。") + except Exception as exc: + _handle_asset_error(exc) + + @router.post( "/{asset_id}/return", response_model=AgentAssetRiskRuleLatestTestSummary, diff --git a/server/src/app/api/v1/endpoints/reimbursements.py b/server/src/app/api/v1/endpoints/reimbursements.py index 477a8ac..38f2eca 100644 --- a/server/src/app/api/v1/endpoints/reimbursements.py +++ b/server/src/app/api/v1/endpoints/reimbursements.py @@ -571,7 +571,7 @@ def approve_expense_claim( "/claims/{claim_id}", response_model=ExpenseClaimActionResponse, summary="删除报销单", - description="申请人仅可删除自己的草稿、待补充或退回单据;高级管理人员可删除可见单据,财务人员没有删除权限。", + description="申请人仅可删除自己的草稿、待补充或退回单据;高级管理人员可删除可见的非归档单据;已归档单据仅高级管理员可删除,财务人员没有删除权限。", responses={ status.HTTP_404_NOT_FOUND: { "model": ErrorResponse, diff --git a/server/src/app/core/agent_enums.py b/server/src/app/core/agent_enums.py index 2522884..79e8211 100644 --- a/server/src/app/core/agent_enums.py +++ b/server/src/app/core/agent_enums.py @@ -11,10 +11,12 @@ class AgentAssetType(StrEnum): class AgentAssetStatus(StrEnum): + GENERATING = "generating" DRAFT = "draft" REVIEW = "review" ACTIVE = "active" DISABLED = "disabled" + FAILED = "failed" class AgentReviewStatus(StrEnum): diff --git a/server/src/app/schemas/agent_asset.py b/server/src/app/schemas/agent_asset.py index d80c54a..7752efa 100644 --- a/server/src/app/schemas/agent_asset.py +++ b/server/src/app/schemas/agent_asset.py @@ -113,7 +113,8 @@ class AgentAssetRuleJsonRead(BaseModel): class AgentAssetRiskRuleGenerateRequest(BaseModel): business_domain: AgentAssetDomain = AgentAssetDomain.EXPENSE expense_category: str | None = Field(default=None, max_length=40) - risk_level: str = Field(default="medium", pattern="^(low|medium|high)$") + rule_title: str | None = Field(default=None, max_length=80) + risk_level: str | None = Field(default=None, pattern="^(low|medium|high|critical)$") natural_language: str = Field(min_length=8, max_length=2000) requires_attachment: bool = False @@ -195,6 +196,10 @@ class AgentAssetRiskRuleEnabledUpdate(BaseModel): enabled: bool +class AgentAssetRiskRuleLevelUpdate(BaseModel): + risk_level: str = Field(pattern="^(low|medium|high|critical)$") + + class AgentAssetRiskRuleTestRunRead(BaseModel): model_config = ConfigDict(from_attributes=True) diff --git a/server/src/app/services/agent_asset_json_rules.py b/server/src/app/services/agent_asset_json_rules.py index f9d8008..e64bc19 100644 --- a/server/src/app/services/agent_asset_json_rules.py +++ b/server/src/app/services/agent_asset_json_rules.py @@ -3,6 +3,7 @@ from __future__ import annotations from app.models.agent_asset import AgentAsset from app.schemas.agent_asset import AgentAssetRuleJsonRead, AgentAssetRuleJsonWrite from app.services.agent_asset_spreadsheet import RISK_RULES_LIBRARY, RULE_LIBRARY_NAMES +from app.services.risk_rule_manifest_normalizer import normalize_risk_rule_manifest class AgentAssetJsonRuleMixin: @@ -35,6 +36,7 @@ class AgentAssetJsonRuleMixin: library=rule_library, file_name=file_name, ) + payload = normalize_risk_rule_manifest(payload) return AgentAssetRuleJsonRead( file_name=file_name, rule_code=str(payload.get("rule_code") or asset.code or ""), diff --git a/server/src/app/services/agent_asset_risk_rule_level.py b/server/src/app/services/agent_asset_risk_rule_level.py new file mode 100644 index 0000000..0a69f16 --- /dev/null +++ b/server/src/app/services/agent_asset_risk_rule_level.py @@ -0,0 +1,250 @@ +from __future__ import annotations + +import json +import re +from datetime import UTC, datetime +from typing import Any + +from app.core.agent_enums import AgentAssetType +from app.models.agent_asset import AgentAsset +from app.services.risk_rule_flow_diagram import ( + RiskRuleFlowDiagramField, + RiskRuleFlowDiagramRenderer, + RiskRuleFlowDiagramSpec, + build_risk_rule_flow_diagram_details, +) + + +RISK_RULE_LEVEL_LABELS = { + "low": "低风险", + "medium": "中风险", + "high": "高风险", + "critical": "极高风险", +} + + +class AgentAssetRiskRuleLevelMixin: + _RUNTIME_JSON_BLOCK_PATTERN = re.compile(r"```json\s*\n.*?\n```", re.DOTALL) + _RISK_LEVEL_TEXT_PATTERN = re.compile(r"(低|中|高)风险") + + def set_risk_rule_level( + self, + asset_id: str, + *, + risk_level: str, + actor: str, + request_id: str | None = None, + ) -> AgentAsset: + asset = self._resolve_asset(asset_id) + self._require_json_risk_asset(asset) + normalized_level = self._normalize_risk_rule_level(risk_level) + before = { + **self._asset_snapshot(asset), + "risk_level": self._resolve_asset_risk_level(asset), + } + + rule_library, file_name = self._resolve_json_risk_rule_document(asset) + manifest = self.rule_library_manager.read_rule_library_json( + library=rule_library, + file_name=file_name, + ) + updated_manifest = self._apply_risk_level_to_manifest(asset, manifest, normalized_level) + self.rule_library_manager.write_rule_library_json( + library=rule_library, + file_name=file_name, + payload=updated_manifest, + ) + + config_json = dict(asset.config_json or {}) + config_json["severity"] = normalized_level + config_json["risk_level"] = normalized_level + config_json["risk_level_label"] = RISK_RULE_LEVEL_LABELS[normalized_level] + asset.config_json = config_json + self._sync_current_version_runtime_json(asset, updated_manifest) + updated = self.repository.save_asset(asset) + self.audit_service.log_action( + actor=actor, + action="set_risk_rule_level", + resource_type=AgentAssetType.RULE.value, + resource_id=asset.id, + before_json=before, + after_json={ + **self._asset_snapshot(updated), + "risk_level": normalized_level, + }, + request_id=request_id, + ) + return updated + + def _apply_risk_level_to_manifest( + self, + asset: AgentAsset, + manifest: dict[str, Any], + risk_level: str, + ) -> dict[str, Any]: + label = RISK_RULE_LEVEL_LABELS[risk_level] + payload = dict(manifest or {}) + payload["severity"] = risk_level + + outcomes = dict(payload.get("outcomes") or {}) + fail_outcome = dict(outcomes.get("fail") or {}) + fail_outcome["severity"] = risk_level + outcomes["fail"] = fail_outcome + outcomes.setdefault("pass", {"severity": "none", "action": "continue"}) + payload["outcomes"] = outcomes + + metadata = dict(payload.get("metadata") or {}) + metadata["risk_level"] = risk_level + metadata["risk_level_label"] = label + metadata["risk_level_updated_at"] = datetime.now(UTC).isoformat() + if metadata.get("business_explanation"): + metadata["business_explanation"] = self._replace_risk_level_text( + metadata["business_explanation"], + label, + ) + flow = dict(metadata.get("flow") or {}) + flow["fail"] = self._replace_risk_level_text( + str(flow.get("fail") or f"命中{label},进入人工复核"), + label, + ) + metadata["flow"] = flow + payload["metadata"] = metadata + + if payload.get("description"): + payload["description"] = self._replace_risk_level_text(payload["description"], label) + + params = dict(payload.get("params") or {}) + params_flow = params.get("flow") + if isinstance(params_flow, dict): + next_params_flow = dict(params_flow) + next_params_flow["fail"] = self._replace_risk_level_text( + str(next_params_flow.get("fail") or f"命中{label},进入人工复核"), + label, + ) + params["flow"] = next_params_flow + payload["params"] = params + + payload["flow_diagram_svg"] = self._build_risk_level_flow_diagram_svg( + asset, + payload, + risk_level=risk_level, + ) + return payload + + def _build_risk_level_flow_diagram_svg( + self, + asset: AgentAsset, + manifest: dict[str, Any], + *, + risk_level: str, + ) -> str: + metadata = manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {} + flow = metadata.get("flow") if isinstance(metadata.get("flow"), dict) else {} + params = manifest.get("params") if isinstance(manifest.get("params"), dict) else {} + fields = self._resolve_risk_rule_diagram_fields(manifest) + details = build_risk_rule_flow_diagram_details(manifest, fields) + risk_label = RISK_RULE_LEVEL_LABELS[risk_level] + return RiskRuleFlowDiagramRenderer().render( + RiskRuleFlowDiagramSpec( + title=self._clean_risk_level_text(manifest.get("name")) or asset.name, + domain_label=self._resolve_risk_rule_domain_label(asset, manifest), + severity=risk_level, + severity_label=risk_label, + fields=tuple(fields), + start=self._clean_risk_level_text(flow.get("start")) or "业务单据提交", + evidence=self._clean_risk_level_text(flow.get("evidence")) or "读取规则字段", + decision=( + self._clean_risk_level_text(flow.get("decision")) + or self._clean_risk_level_text(params.get("condition_summary")) + or "判断是否命中风险" + ), + basis=( + self._clean_risk_level_text(metadata.get("condition_summary")) + or self._clean_risk_level_text(params.get("condition_summary")) + or "根据规则字段判断是否命中风险" + ), + pass_text=self._clean_risk_level_text(flow.get("pass")) or "未命中风险,继续流转", + fail_text=self._clean_risk_level_text(flow.get("fail")) + or f"命中{risk_label},进入人工复核", + fact_lines=details["fact_lines"], + condition_lines=details["condition_lines"], + hit_logic=str(details["hit_logic"] or ""), + ) + ) + + def _resolve_risk_rule_diagram_fields( + self, + manifest: dict[str, Any], + ) -> list[RiskRuleFlowDiagramField]: + inputs = manifest.get("inputs") if isinstance(manifest.get("inputs"), dict) else {} + rows = inputs.get("fields") if isinstance(inputs.get("fields"), list) else [] + fields: list[RiskRuleFlowDiagramField] = [] + for row in rows: + if not isinstance(row, dict): + continue + key = self._clean_risk_level_text(row.get("key")) + label = self._clean_risk_level_text(row.get("label")) or key + if key or label: + fields.append(RiskRuleFlowDiagramField(key=key, label=label)) + return fields + + def _resolve_risk_rule_domain_label( + self, + asset: AgentAsset, + manifest: dict[str, Any], + ) -> str: + config_json = dict(asset.config_json or {}) + return ( + self._clean_risk_level_text(manifest.get("risk_category")) + or self._clean_risk_level_text(config_json.get("risk_category")) + or self._clean_risk_level_text(config_json.get("expense_category_label")) + or "报销" + ) + + def _sync_current_version_runtime_json( + self, + asset: AgentAsset, + manifest: dict[str, Any], + ) -> None: + version_name = self._resolve_working_version(asset) + if not version_name: + return + version = self.repository.get_version(asset.id, version_name) + if version is None or version.content_type != "markdown": + return + + runtime_block = f"```json\n{json.dumps(manifest, ensure_ascii=False, indent=2)}\n```" + content = str(version.content or "") + if self._RUNTIME_JSON_BLOCK_PATTERN.search(content): + version.content = self._RUNTIME_JSON_BLOCK_PATTERN.sub( + lambda _: runtime_block, + content, + count=1, + ) + else: + version.content = f"{content.rstrip()}\n\n## 运行时 JSON\n\n{runtime_block}" + self.db.add(version) + + def _resolve_asset_risk_level(self, asset: AgentAsset) -> str: + config_json = dict(asset.config_json or {}) + return self._normalize_risk_rule_level(config_json.get("severity") or "medium") + + @classmethod + def _replace_risk_level_text(cls, value: Any, risk_label: str) -> str: + text = str(value or "").strip() + if not text: + return text + if cls._RISK_LEVEL_TEXT_PATTERN.search(text): + return cls._RISK_LEVEL_TEXT_PATTERN.sub(risk_label, text) + return text + + @staticmethod + def _normalize_risk_rule_level(value: Any) -> str: + normalized = str(value or "").strip().lower() + if normalized not in RISK_RULE_LEVEL_LABELS: + raise ValueError("风险等级仅支持 low、medium、high、critical。") + return normalized + + @staticmethod + def _clean_risk_level_text(value: Any) -> str: + return str(value or "").strip() diff --git a/server/src/app/services/agent_asset_risk_rule_simulation.py b/server/src/app/services/agent_asset_risk_rule_simulation.py index faf29fe..da237ec 100644 --- a/server/src/app/services/agent_asset_risk_rule_simulation.py +++ b/server/src/app/services/agent_asset_risk_rule_simulation.py @@ -1,7 +1,6 @@ from __future__ import annotations -import re -from datetime import UTC, date, datetime +from datetime import UTC, datetime from typing import Any from app.schemas.agent_asset import ( @@ -9,6 +8,19 @@ from app.schemas.agent_asset import ( AgentAssetRiskRuleSimulationRead, AgentAssetRiskRuleSimulationRequest, ) +from app.services.agent_asset_risk_rule_simulation_fields import ( + derive_attachment_field_value, + extract_amount, + extract_city_mentions, + extract_invoice_no, + extract_iso_date, + field_matches_simulation_key, + has_meaningful_value, + infer_goods_name, + infer_item_type, + looks_like_city_field, + refine_simulation_values_with_model, +) from app.services.risk_rule_template_executor import RiskRuleTemplateExecutor @@ -106,6 +118,7 @@ class AgentAssetRiskRuleSimulationMixin: safe_explicit_values = explicit_values if isinstance(explicit_values, dict) else {} corpus = self._build_simulation_corpus(message, attachments) city_mentions = self._extract_city_mentions(corpus) + message_city_mentions = self._extract_city_mentions(message) for field in fields: key = field["key"] @@ -123,17 +136,41 @@ class AgentAssetRiskRuleSimulationMixin: values[key] = attachment_value source_map[key] = "ocr" continue - inferred = self._infer_simulation_value( - key, - field.get("label") or key, - corpus=corpus, - city_mentions=city_mentions, - ) + if attachments and key.startswith("attachment.") and ( + "city" in key or "location" in key + ): + inferred = None + else: + inferred = self._infer_simulation_value( + key, + field.get("label") or key, + corpus=corpus, + city_mentions=( + message_city_mentions + if key.startswith(("claim.", "item.")) + else city_mentions + ), + ) if self._has_meaningful_value(inferred): values[key] = inferred source_map[key] = "inferred" - self._apply_compare_city_hints(manifest, values, source_map, city_mentions) + allowed_keys = {field["key"] for field in fields} + model_values = refine_simulation_values_with_model( + getattr(self, "db", None), + fields, + message=message, + attachments=attachments, + ) + for key, value in model_values.items(): + if not self._has_meaningful_value(value) or key not in allowed_keys: + continue + if source_map.get(key) == "manual": + continue + values[key] = value + source_map[key] = "model_refined" + + self._apply_compare_city_hints(manifest, values, source_map, message_city_mentions) recognized_fields = self._build_recognized_fields(fields, values, source_map) return values, source_map, recognized_fields @@ -163,7 +200,14 @@ class AgentAssetRiskRuleSimulationMixin: return city_mentions[0] if city_mentions else "" if field_key.endswith("amount"): return self._extract_amount(corpus) - if field_key.endswith("issue_date") or field_key.endswith("item_date"): + if ( + field_key.endswith("issue_date") + or field_key.endswith("item_date") + or field_key.endswith("trip_start_date") + or field_key.endswith("trip_end_date") + or field_key.endswith("stay_start_date") + or field_key.endswith("stay_end_date") + ): return self._extract_iso_date(corpus) if field_key.endswith("invoice_no"): return self._extract_invoice_no(corpus) @@ -195,12 +239,18 @@ class AgentAssetRiskRuleSimulationMixin: right = str(condition.get("right") or "").strip() if not left or not right: continue - if self._looks_like_city_field(left): + if self._looks_like_city_field(left) and ( + not self._has_meaningful_value(values.get(left)) + or source_map.get(left) == "inferred" + ): values[left] = city_mentions[0] - source_map[left] = source_map.get(left) or "inferred" - if self._looks_like_city_field(right): + source_map[left] = "inferred" + if self._looks_like_city_field(right) and ( + not self._has_meaningful_value(values.get(right)) + or source_map.get(right) == "inferred" + ): values[right] = city_mentions[1] - source_map[right] = source_map.get(right) or "inferred" + source_map[right] = "inferred" @staticmethod def _normalize_simulation_attachments( @@ -263,6 +313,9 @@ class AgentAssetRiskRuleSimulationMixin: ) -> Any: short_key = field_key.removeprefix("attachment.") for attachment in attachments: + derived_value = derive_attachment_field_value(short_key, attachment) + if self._has_meaningful_value(derived_value): + return derived_value if short_key == "ocr_text": value = attachment.get("ocr_text") or attachment.get("summary") if self._has_meaningful_value(value): @@ -272,34 +325,12 @@ class AgentAssetRiskRuleSimulationMixin: continue candidate_key = str(field.get("key") or "").strip().lower() candidate_label = str(field.get("label") or "").strip() - if self._field_matches_simulation_key( + if field_matches_simulation_key( candidate_key, candidate_label, short_key, label ): return field.get("value") return None - @staticmethod - def _field_matches_simulation_key( - candidate_key: str, - candidate_label: str, - short_key: str, - target_label: str, - ) -> bool: - compact_candidate = candidate_key.replace("_", "") - compact_target = short_key.replace("_", "").lower() - if compact_target and compact_target in compact_candidate: - return True - label_text = f"{candidate_label} {target_label}" - label_map = { - "invoice_no": ("发票号", "发票号码", "票号"), - "hotel_city": ("住宿城市", "酒店城市", "酒店地点", "住宿", "酒店"), - "route_cities": ("行程", "路线", "目的地", "出差城市"), - "goods_name": ("品名", "商品", "服务名称"), - "amount": ("金额", "价税合计", "合计"), - "issue_date": ("日期", "开票日期", "发票日期"), - } - return any(token in label_text for token in label_map.get(short_key, ())) - def _extract_execution_field_keys(self, manifest: dict[str, Any]) -> list[str]: params = manifest.get("params") if isinstance(manifest.get("params"), dict) else {} template_key = str(manifest.get("template_key") or params.get("template_key") or "").strip() @@ -308,9 +339,22 @@ class AgentAssetRiskRuleSimulationMixin: conditions = ( params.get("conditions") if isinstance(params.get("conditions"), list) else [] ) + for group_name in ( + "attachment_city_fields", + "reference_city_fields", + "home_city_fields", + "exception_fields", + ): + for key in self._read_string_list(params.get(group_name)): + if key not in keys: + keys.append(key) for condition in conditions: if not isinstance(condition, dict): continue + for group_name in ("left_group", "right_group", "exception_fields"): + for key in self._read_string_list(condition.get(group_name)): + if key not in keys: + keys.append(key) for side in ("left", "right"): key = str(condition.get(side) or "").strip() if key and key not in keys: @@ -323,6 +367,24 @@ class AgentAssetRiskRuleSimulationMixin: keys.append(key) elif template_key == "field_required_v1": return [] + elif template_key == "composite_rule_v1": + conditions = ( + params.get("conditions") if isinstance(params.get("conditions"), list) else [] + ) + for condition in conditions: + if not isinstance(condition, dict): + continue + for group_name in ( + "fields", + "left_fields", + "right_fields", + "date_fields", + "range_start_fields", + "range_end_fields", + ): + for key in self._read_string_list(condition.get(group_name)): + if key not in keys: + keys.append(key) return keys def _build_missing_fields( @@ -334,6 +396,28 @@ class AgentAssetRiskRuleSimulationMixin: required_keys: list[str], ) -> list[dict[str, Any]]: labels = {field["key"]: field["label"] for field in self._extract_manifest_fields(manifest)} + params = manifest.get("params") if isinstance(manifest.get("params"), dict) else {} + if self._is_city_consistency_manifest(manifest): + declared_keys = self._read_string_list( + params.get("search_fields") or params.get("field_keys") + ) + candidate_keys = [*required_keys, *declared_keys] + reference_keys = [ + key for key in candidate_keys if key in {"claim.location", "item.item_location"} + ] + attachment_keys = [ + key + for key in candidate_keys + if key in {"attachment.route_cities", "attachment.hotel_city"} + ] + missing_groups: list[dict[str, Any]] = [] + if not any(self._has_meaningful_value(field_values.get(key)) for key in reference_keys): + missing_groups.append({"key": "claim.location", "label": labels.get("claim.location", "申报地点")}) + if not any(self._has_meaningful_value(field_values.get(key)) for key in attachment_keys): + missing_groups.append({"key": "attachment.route_cities", "label": labels.get("attachment.route_cities", "行程城市")}) + return missing_groups + if str(params.get("template_key") or manifest.get("template_key") or "") == "composite_rule_v1": + return [] missing: list[dict[str, Any]] = [] for key in required_keys: value = field_values.get(key) @@ -341,6 +425,29 @@ class AgentAssetRiskRuleSimulationMixin: missing.append({"key": key, "label": labels.get(key, key)}) return missing + @staticmethod + def _is_city_consistency_manifest(manifest: dict[str, Any]) -> bool: + params = manifest.get("params") if isinstance(manifest.get("params"), dict) else {} + if str(params.get("semantic_type") or "").strip() in { + "travel_city_consistency", + "travel_route_city_consistency", + }: + return True + field_keys = AgentAssetRiskRuleSimulationMixin._read_string_list( + params.get("search_fields") or params.get("field_keys") + ) + has_reference = any(key in {"claim.location", "item.item_location"} for key in field_keys) + has_attachment_city = any( + key in {"attachment.route_cities", "attachment.hotel_city"} for key in field_keys + ) + if not (has_reference and has_attachment_city): + return False + text = "\n".join( + str(params.get(key) or "") + for key in ("natural_language", "condition_summary", "message_template") + ) + return any(term in text for term in ("一致", "不一致", "匹配", "不符", "对应", "出现在")) + def _resolve_simulation_block( self, manifest: dict[str, Any], @@ -454,87 +561,35 @@ class AgentAssetRiskRuleSimulationMixin: @staticmethod def _extract_city_mentions(text: str) -> list[str]: - city_names = [ - "北京", - "上海", - "广州", - "深圳", - "杭州", - "南京", - "成都", - "武汉", - "重庆", - "天津", - "苏州", - "西安", - ] - pattern = "|".join(re.escape(city) for city in city_names) - found: list[str] = [] - for match in re.finditer(pattern, text): - city = match.group(0) - if city not in found: - found.append(city) - return found + return extract_city_mentions(text) @staticmethod def _extract_amount(text: str) -> str: - match = re.search(r"(\d{2,8}(?:\.\d{1,2})?)\s*(?:元|块|人民币|CNY)?", text, re.IGNORECASE) - return match.group(1) if match else "" + return extract_amount(text) @staticmethod def _extract_iso_date(text: str) -> str: - match = re.search(r"(20\d{2})[-/.年](\d{1,2})[-/.月](\d{1,2})", text) - if not match: - return "" - year, month, day = (int(part) for part in match.groups()) - try: - return date(year, month, day).isoformat() - except ValueError: - return "" + return extract_iso_date(text) @staticmethod def _extract_invoice_no(text: str) -> str: - match = re.search(r"(?:发票号|发票号码|票号)[::\s]*([A-Z0-9-]{6,32})", text, re.IGNORECASE) - return match.group(1) if match else "" + return extract_invoice_no(text) @staticmethod def _infer_item_type(text: str) -> str: - if not text: - return "" - if any(keyword in text for keyword in ("酒店", "住宿", "宾馆")): - return "住宿费" - if any(keyword in text for keyword in ("机票", "航班", "火车", "高铁", "打车")): - return "交通费" - if any(keyword in text for keyword in ("餐饮", "餐费", "招待")): - return "餐饮费" - return "差旅费" + return infer_item_type(text) @staticmethod def _infer_goods_name(text: str) -> str: - if not text: - return "" - if any(keyword in text for keyword in ("酒店", "住宿", "宾馆")): - return "住宿服务" - if any(keyword in text for keyword in ("机票", "航班", "火车", "高铁", "打车")): - return "交通服务" - if any(keyword in text for keyword in ("餐饮", "餐费", "招待")): - return "餐饮服务" - return "报销服务" + return infer_goods_name(text) @staticmethod def _looks_like_city_field(field_key: str) -> bool: - lowered = field_key.lower() - return "city" in lowered or "location" in lowered or lowered.endswith("route_cities") + return looks_like_city_field(field_key) @staticmethod def _has_meaningful_value(value: Any) -> bool: - if value is None: - return False - if isinstance(value, str): - return bool(value.strip()) - if isinstance(value, (list, tuple, set, dict)): - return bool(value) - return True + return has_meaningful_value(value) @staticmethod def _risk_severity_label(severity: str) -> str: diff --git a/server/src/app/services/agent_asset_risk_rule_simulation_fields.py b/server/src/app/services/agent_asset_risk_rule_simulation_fields.py new file mode 100644 index 0000000..9bb62aa --- /dev/null +++ b/server/src/app/services/agent_asset_risk_rule_simulation_fields.py @@ -0,0 +1,385 @@ +from __future__ import annotations + +import json +import re +from datetime import date +from typing import Any + +from app.services.runtime_chat import RuntimeChatService + + +CITY_NAMES = [ + "北京", + "上海", + "广州", + "深圳", + "杭州", + "南京", + "成都", + "武汉", + "重庆", + "天津", + "苏州", + "西安", + "长沙", + "郑州", + "青岛", + "厦门", + "福州", + "合肥", + "济南", + "沈阳", + "大连", + "宁波", + "温州", + "无锡", + "常州", + "昆明", + "贵阳", + "南宁", + "南昌", + "太原", + "石家庄", + "哈尔滨", + "长春", + "兰州", + "银川", + "西宁", + "乌鲁木齐", + "呼和浩特", + "海口", + "三亚", + "佛山", + "东莞", + "珠海", + "惠州", + "嘉兴", + "绍兴", + "金华", + "台州", + "泉州", + "烟台", + "徐州", + "扬州", + "南通", + "镇江", + "洛阳", + "襄阳", + "宜昌", +] + + +def refine_simulation_values_with_model( + db: Any, + fields: list[dict[str, str]], + *, + message: str, + attachments: list[dict[str, Any]], +) -> dict[str, Any]: + if not fields or not attachments or db is None: + return {} + + compact_attachments = [ + { + "name": item.get("name") or "", + "document_type_label": item.get("document_type_label") or "", + "scene_label": item.get("scene_label") or "", + "summary": item.get("summary") or "", + "ocr_text": str(item.get("ocr_text") or "")[:2500], + "document_fields": list(item.get("document_fields") or [])[:20], + } + for item in attachments[:4] + ] + messages = [ + { + "role": "system", + "content": ( + "你是风险规则仿真测试的票据字段过滤器。只根据用户输入、OCR 文本和结构化字段," + "把信息归一化到给定字段 key。不要判断风险,不要编造不存在的信息。只输出 JSON。" + ), + }, + { + "role": "user", + "content": json.dumps( + { + "available_fields": fields, + "user_message": str(message or ""), + "attachments": compact_attachments, + "output_shape": { + "field_values": { + "claim.location": "城市或地点", + "attachment.route_cities": ["出发城市", "到达城市"], + } + }, + "rules": [ + "字段值必须来自 OCR 或用户输入。", + "车票路线要拆成城市数组,例如 上海虹桥-武汉 -> [\"上海\", \"武汉\"]。", + "没有把握的字段不要返回。", + ], + }, + ensure_ascii=False, + ), + }, + ] + try: + answer = RuntimeChatService(db).complete( + messages, + max_tokens=700, + temperature=0.0, + timeout_seconds=8, + max_attempts=1, + ) + except Exception: + return {} + + payload = extract_json_payload(answer) + if not isinstance(payload, dict): + return {} + field_values = payload.get("field_values") if isinstance(payload.get("field_values"), dict) else {} + allowed = {field["key"] for field in fields} + return { + key: value + for key, value in field_values.items() + if key in allowed and has_meaningful_value(value) + } + + +def derive_attachment_field_value(short_key: str, attachment: dict[str, Any]) -> Any: + fields = [field for field in list(attachment.get("document_fields") or []) if isinstance(field, dict)] + text_parts = [ + str(attachment.get("summary") or ""), + str(attachment.get("ocr_text") or ""), + *[str(field.get("value") or "") for field in fields], + ] + corpus = "\n".join(part for part in text_parts if part) + + if short_key == "route_cities": + for field in fields: + key = str(field.get("key") or "").strip().lower() + label = str(field.get("label") or "").strip() + if looks_like_route_field(key, label): + cities = extract_route_cities(str(field.get("value") or "")) + if cities: + return cities + cities = extract_route_cities(corpus) + return cities or None + + if short_key in {"hotel_city", "city"}: + for field in fields: + key = str(field.get("key") or "").strip().lower() + label = str(field.get("label") or "").strip() + if looks_like_route_field(key, label): + continue + if looks_like_city_field(f"{key} {label}"): + cities = extract_city_mentions(str(field.get("value") or "")) + if cities: + return cities[0] + value = str(field.get("value") or "").strip() + if value: + return value + return None + + alias_keys = { + "invoice_no": {"invoice_no", "invoice_number", "ticket_number", "order_no", "order_number"}, + "issue_date": {"issue_date", "invoice_date", "date", "trip_date", "travel_date"}, + "stay_start_date": {"stay_start_date", "check_in_date", "arrival_date", "入住日期"}, + "stay_end_date": {"stay_end_date", "check_out_date", "departure_date", "离店日期"}, + "amount": {"amount", "total_amount", "payment_amount", "paid_amount"}, + "goods_name": {"goods_name", "service_name", "item_name", "merchant_name"}, + }.get(short_key, set()) + if alias_keys: + for field in fields: + key = str(field.get("key") or "").strip().lower() + label = str(field.get("label") or "").strip() + if ( + key in alias_keys + or field_matches_simulation_key(key, label, short_key, short_key) + ) and has_meaningful_value(field.get("value")): + return field.get("value") + return None + + +def field_matches_simulation_key( + candidate_key: str, + candidate_label: str, + short_key: str, + target_label: str, +) -> bool: + compact_candidate = candidate_key.replace("_", "") + compact_target = short_key.replace("_", "").lower() + if compact_target and compact_target in compact_candidate: + return True + label_text = f"{candidate_label} {target_label}" + label_map = { + "invoice_no": ("发票号", "发票号码", "票号"), + "hotel_city": ("住宿城市", "酒店城市", "酒店地点", "住宿", "酒店"), + "route_cities": ("行程", "路线", "目的地", "出差城市"), + "goods_name": ("品名", "商品", "服务名称"), + "amount": ("金额", "价税合计", "合计"), + "issue_date": ("日期", "开票日期", "发票日期"), + "stay_start_date": ("入住日期", "住宿开始", "入住时间", "开始日期"), + "stay_end_date": ("离店日期", "退房日期", "住宿结束", "结束日期"), + } + return any(token in label_text for token in label_map.get(short_key, ())) + + +def extract_json_payload(response_text: str | None) -> dict[str, Any] | None: + if not response_text: + return None + cleaned = re.sub( + r".*?", + "", + str(response_text), + flags=re.DOTALL | re.IGNORECASE, + ).strip() + if not cleaned: + return None + candidates: list[str] = [] + fenced_match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", cleaned, flags=re.DOTALL) + if fenced_match: + candidates.append(fenced_match.group(1)) + start = cleaned.find("{") + end = cleaned.rfind("}") + if start >= 0 and end > start: + candidates.append(cleaned[start : end + 1]) + candidates.append(cleaned) + for candidate in candidates: + try: + parsed = json.loads(candidate) + except json.JSONDecodeError: + continue + if isinstance(parsed, dict): + return parsed + return None + + +def extract_city_mentions(text: str) -> list[str]: + content = str(text or "") + if not content: + return [] + pattern = "|".join(re.escape(city) for city in CITY_NAMES) + found: list[str] = [] + for match in re.finditer(pattern, content): + city = match.group(0) + if city not in found: + found.append(city) + return found + + +def extract_route_cities(text: str) -> list[str]: + mentions = extract_city_mentions(text) + if mentions: + return mentions + route_text = str(text or "") + if not route_text: + return [] + fragments = re.split(r"(?:->|→|—|--|-|至|到|开往|前往|赴)", route_text) + cities: list[str] = [] + for fragment in fragments: + candidate = normalize_city_token(fragment) + if candidate and candidate not in cities: + cities.append(candidate) + return cities[:4] + + +def normalize_city_token(value: str) -> str: + text = re.sub(r"[^\u4e00-\u9fff]", "", str(value or "")) + if not text: + return "" + for suffix in ( + "火车站", + "高铁站", + "汽车站", + "机场", + "虹桥", + "东站", + "西站", + "南站", + "北站", + "站", + "市", + ): + text = text.replace(suffix, "") + if 2 <= len(text) <= 6: + return text + return "" + + +def extract_amount(text: str) -> str: + match = re.search(r"(\d{2,8}(?:\.\d{1,2})?)\s*(?:元|块|人民币|CNY)?", text, re.IGNORECASE) + return match.group(1) if match else "" + + +def extract_iso_date(text: str) -> str: + match = re.search(r"(20\d{2})[-/.年](\d{1,2})[-/.月](\d{1,2})", text) + if not match: + return "" + year, month, day = (int(part) for part in match.groups()) + try: + return date(year, month, day).isoformat() + except ValueError: + return "" + + +def extract_invoice_no(text: str) -> str: + match = re.search(r"(?:发票号|发票号码|票号)[::\s]*([A-Z0-9-]{6,32})", text, re.IGNORECASE) + return match.group(1) if match else "" + + +def infer_item_type(text: str) -> str: + if not text: + return "" + if any(keyword in text for keyword in ("酒店", "住宿", "宾馆")): + return "住宿费" + if any(keyword in text for keyword in ("机票", "航班", "火车", "高铁", "打车")): + return "交通费" + if any(keyword in text for keyword in ("餐饮", "餐费", "招待")): + return "餐饮费" + return "差旅费" + + +def infer_goods_name(text: str) -> str: + if not text: + return "" + if any(keyword in text for keyword in ("酒店", "住宿", "宾馆")): + return "住宿服务" + if any(keyword in text for keyword in ("机票", "航班", "火车", "高铁", "打车")): + return "交通服务" + if any(keyword in text for keyword in ("餐饮", "餐费", "招待")): + return "餐饮服务" + return "报销服务" + + +def looks_like_city_field(field_key: str) -> bool: + lowered = field_key.lower() + return "city" in lowered or "location" in lowered or lowered.endswith("route_cities") + + +def looks_like_route_field(candidate_key: str, candidate_label: str = "") -> bool: + text = f"{candidate_key} {candidate_label}".lower() + return any( + token in text + for token in ( + "route", + "trip_route", + "travel_route", + "from_to", + "origin_destination", + "行程", + "路线", + "出发", + "到达", + "目的地", + "车次", + ) + ) + + +def has_meaningful_value(value: Any) -> bool: + if value is None: + return False + if isinstance(value, str): + return bool(value.strip()) + if isinstance(value, (list, tuple, set, dict)): + return bool(value) + return True diff --git a/server/src/app/services/agent_asset_risk_rule_testing.py b/server/src/app/services/agent_asset_risk_rule_testing.py index 2c6055d..2e9b21c 100644 --- a/server/src/app/services/agent_asset_risk_rule_testing.py +++ b/server/src/app/services/agent_asset_risk_rule_testing.py @@ -14,6 +14,7 @@ from app.core.agent_enums import ( AgentReviewStatus, ) from app.models.agent_asset import AgentAsset, AgentAssetReview, AgentAssetTestRun +from app.models.employee import Employee from app.models.financial_record import ExpenseClaim, ExpenseClaimItem from app.schemas.agent_asset import ( AgentAssetRiskRuleLatestTestSummary, @@ -25,6 +26,7 @@ from app.schemas.agent_asset import ( ) from app.services.expense_claims import ExpenseClaimService from app.services.risk_rule_template_executor import RiskRuleTemplateExecutor +from app.services.risk_rule_manifest_normalizer import normalize_risk_rule_manifest class AgentAssetRiskRuleTestingMixin: @@ -333,6 +335,7 @@ class AgentAssetRiskRuleTestingMixin: library=rule_library, file_name=file_name, ) + manifest = normalize_risk_rule_manifest(manifest) return asset, target_version, manifest def _create_test_run( @@ -451,6 +454,13 @@ class AgentAssetRiskRuleTestingMixin: item_amount=self._to_decimal(values.get("item.item_amount") or claim.amount), ) claim.items = [item] + if values.get("employee.location"): + claim.employee = Employee( + employee_no="TEST-EMPLOYEE", + name=claim.employee_name, + email="risk-rule-test@example.com", + location=str(values.get("employee.location") or ""), + ) attachment_fields = [] document_info: dict[str, Any] = {"fields": attachment_fields} @@ -585,7 +595,14 @@ class AgentAssetRiskRuleTestingMixin: def _default_value_for_field(field_key: str) -> Any: if field_key.endswith("amount"): return "100.00" - if field_key.endswith("issue_date"): + if ( + field_key.endswith("issue_date") + or field_key.endswith("stay_start_date") + or field_key.endswith("stay_end_date") + or field_key.endswith("trip_start_date") + or field_key.endswith("trip_end_date") + or field_key.endswith("item_date") + ): return date.today().isoformat() if field_key.endswith("route_cities"): return ["北京"] diff --git a/server/src/app/services/agent_assets.py b/server/src/app/services/agent_assets.py index d0195a0..55d31fe 100644 --- a/server/src/app/services/agent_assets.py +++ b/server/src/app/services/agent_assets.py @@ -28,6 +28,7 @@ from app.schemas.agent_asset import ( ) from app.services.agent_asset_json_rules import AgentAssetJsonRuleMixin from app.services.agent_asset_onlyoffice import AgentAssetOnlyOfficeMixin +from app.services.agent_asset_risk_rule_level import AgentAssetRiskRuleLevelMixin from app.services.agent_asset_risk_rule_simulation import AgentAssetRiskRuleSimulationMixin from app.services.agent_asset_risk_rule_testing import AgentAssetRiskRuleTestingMixin from app.services.agent_asset_rule_library import AgentAssetRuleLibraryManager @@ -43,6 +44,7 @@ logger = get_logger("app.services.agent_assets") class AgentAssetService( AgentAssetOnlyOfficeMixin, AgentAssetSpreadsheetHelperMixin, + AgentAssetRiskRuleLevelMixin, AgentAssetRiskRuleTestingMixin, AgentAssetRiskRuleSimulationMixin, AgentAssetTimelineMixin, @@ -106,6 +108,9 @@ class AgentAssetService( latest_test_summary=self.get_latest_risk_rule_test_summary(asset) if str((asset.config_json or {}).get("detail_mode") or "").strip().lower() == "json_risk" + and working_version + and asset.status + not in {AgentAssetStatus.GENERATING.value, AgentAssetStatus.FAILED.value} else None, ) diff --git a/server/src/app/services/agent_foundation_risk_rules.py b/server/src/app/services/agent_foundation_risk_rules.py index 208dc1e..af24651 100644 --- a/server/src/app/services/agent_foundation_risk_rules.py +++ b/server/src/app/services/agent_foundation_risk_rules.py @@ -1,62 +1,25 @@ from __future__ import annotations -import hashlib -import json -from datetime import UTC, date, datetime -from decimal import Decimal -from pathlib import Path +from datetime import UTC, datetime -from sqlalchemy import inspect, select, text +from sqlalchemy import select from app.core.agent_enums import ( AgentAssetContentType, AgentAssetDomain, AgentAssetStatus, AgentAssetType, - AgentName, - AgentPermissionLevel, AgentReviewStatus, - AgentRunSource, - AgentRunStatus, - AgentToolType, -) -from app.models.agent_asset import AgentAsset, AgentAssetReview, AgentAssetVersion -from app.models.agent_run import AgentRun, AgentToolCall, SemanticParseLog -from app.models.audit_log import AuditLog -from app.models.financial_record import ( - AccountsPayableRecord, - AccountsReceivableRecord, - ExpenseClaim, - ExpenseClaimItem, -) -from app.services.agent_asset_rule_library import AgentAssetRuleLibraryManager -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, - RISK_RULES_LIBRARY, -) -from app.services.expense_rule_runtime import ( - build_scene_submission_standard_markdown, - build_travel_risk_control_standard_markdown, -) -from app.services.agent_foundation_constants import ( - ATTACHMENT_RULE_ASSET_CODE, - ATTACHMENT_RULE_RUNTIME_CONFIG, - COMPANY_COMMUNICATION_RULE_SCENARIO_JSON, - COMPANY_COMMUNICATION_RULE_VERSION, - COMPANY_TRAVEL_RULE_SCENARIO_JSON, - COMPANY_TRAVEL_RULE_VERSION, - DEMO_EXPENSE_CLAIM_SIGNATURES, - DEMO_PAYABLE_SIGNATURES, - DEMO_RECEIVABLE_SIGNATURES, - LEGACY_RULE_CODES, - PLATFORM_DESTINATION_LOCATION_RULE_FILENAME, ) from app.core.logging import get_logger +from app.models.agent_asset import AgentAsset +from app.services.agent_asset_rule_library import AgentAssetRuleLibraryManager +from app.services.agent_asset_spreadsheet import ( + RISK_RULES_LIBRARY, +) +from app.services.agent_foundation_constants import ( + PLATFORM_DESTINATION_LOCATION_RULE_FILENAME, +) logger = get_logger("app.services.agent_foundation") @@ -67,20 +30,51 @@ class AgentFoundationRiskRuleMixin: manifests: list[tuple[str, dict[str, object]]] = [] - for file_name in sorted(manager.list_rule_library_json_files(library=RISK_RULES_LIBRARY)): + for file_name in sorted( + manager.list_rule_library_json_files(library=RISK_RULES_LIBRARY) + ): - payload = manager.read_rule_library_json(library=RISK_RULES_LIBRARY, file_name=file_name) + payload = manager.read_rule_library_json( + library=RISK_RULES_LIBRARY, + file_name=file_name, + ) if payload.get("enabled") is False: continue + if self._is_user_generated_risk_manifest(payload): + + continue + manifests.append((file_name, payload)) return manifests @staticmethod + def _is_user_generated_risk_manifest(manifest: dict[str, object]) -> bool: + + rule_code = str(manifest.get("rule_code") or "").strip().lower() + + metadata = manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {} + + stability = str(metadata.get("stability") or "").strip().lower() + + source_ref = str(metadata.get("source_ref") or "").strip() + + if stability == "generated_draft": + + return True + + if source_ref == "自然语言风险规则": + + return True + + return ".generated_" in rule_code + + @staticmethod + def _resolve_platform_risk_category(manifest: dict[str, object]) -> str: explicit = str(manifest.get("risk_category") or "").strip() @@ -91,7 +85,9 @@ class AgentFoundationRiskRuleMixin: rule_code = str(manifest.get("rule_code") or "").strip().lower() - applies_to = manifest.get("applies_to") if isinstance(manifest.get("applies_to"), dict) else {} + applies_to = ( + manifest.get("applies_to") if isinstance(manifest.get("applies_to"), dict) else {} + ) domains = {str(item or "").strip().lower() for item in applies_to.get("domains") or []} @@ -133,7 +129,9 @@ class AgentFoundationRiskRuleMixin: return [category] if category else ["通用"] - def _platform_risk_config_json(self, file_name: str, manifest: dict[str, object]) -> dict[str, object]: + def _platform_risk_config_json( + self, file_name: str, manifest: dict[str, object] + ) -> dict[str, object]: outcomes = manifest.get("outcomes") if isinstance(manifest.get("outcomes"), dict) else {} @@ -191,7 +189,9 @@ class AgentFoundationRiskRuleMixin: continue - metadata = manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {} + metadata = ( + manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {} + ) source_ref = str(metadata.get("source_ref") or "").strip() @@ -255,7 +255,11 @@ class AgentFoundationRiskRuleMixin: "Platform risk rules synced from library", - extra={"manifest_count": manifest_count, "created_count": synced, "total": len(after_codes)}, + extra={ + "manifest_count": manifest_count, + "created_count": synced, + "total": len(after_codes), + }, ) @@ -271,7 +275,9 @@ class AgentFoundationRiskRuleMixin: continue - metadata = manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {} + metadata = ( + manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {} + ) source_ref = str(metadata.get("source_ref") or "").strip() @@ -347,7 +353,11 @@ class AgentFoundationRiskRuleMixin: version="v1.0.0", - content=self._platform_risk_rule_markdown(asset, manifest=manifest, file_name=file_name), + content=self._platform_risk_rule_markdown( + asset, + manifest=manifest, + file_name=file_name, + ), content_type=AgentAssetContentType.MARKDOWN.value, @@ -389,19 +399,25 @@ class AgentFoundationRiskRuleMixin: config = asset.config_json if isinstance(asset.config_json, dict) else {} - rule_document = config.get("rule_document") if isinstance(config.get("rule_document"), dict) else {} + rule_document = ( + config.get("rule_document") if isinstance(config.get("rule_document"), dict) else {} + ) resolved_file_name = file_name or str(rule_document.get("file_name") or "").strip() evaluator = str(config.get("evaluator") or (manifest or {}).get("evaluator") or "").strip() - ontology_signal = str(config.get("ontology_signal") or (manifest or {}).get("ontology_signal") or "").strip() + ontology_signal = str( + config.get("ontology_signal") or (manifest or {}).get("ontology_signal") or "" + ).strip() source_ref = str(config.get("source_ref") or "").strip() if not source_ref and isinstance(manifest, dict): - metadata = manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {} + metadata = ( + manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {} + ) source_ref = str(metadata.get("source_ref") or "").strip() @@ -457,7 +473,10 @@ class AgentFoundationRiskRuleMixin: return AgentFoundationRiskRuleMixin._platform_risk_rule_markdown( - AgentAsset(name="申报地点与票据地点一致", config_json={"evaluator": "location_consistency"}), + AgentAsset( + name="申报地点与票据地点一致", + config_json={"evaluator": "location_consistency"}, + ), manifest={ diff --git a/server/src/app/services/document_numbering.py b/server/src/app/services/document_numbering.py new file mode 100644 index 0000000..2a95d39 --- /dev/null +++ b/server/src/app/services/document_numbering.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import re +import secrets +from datetime import UTC, datetime +from typing import Callable, Literal + +from sqlalchemy import select +from sqlalchemy.orm import Session + +from app.models.financial_record import ExpenseClaim + +DocumentNumberKind = Literal["application", "reimbursement", "audit"] + +DOCUMENT_NUMBER_PREFIXES: dict[DocumentNumberKind, str] = { + "application": "AP", + "reimbursement": "RE", + "audit": "AD", +} +DOCUMENT_NUMBER_TOKEN_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" +DOCUMENT_NUMBER_TOKEN_LENGTH = 8 +DOCUMENT_NUMBER_PATTERN = re.compile( + rf"^(?:AP|RE|AD)-\d{{14}}-[{DOCUMENT_NUMBER_TOKEN_ALPHABET}]{{{DOCUMENT_NUMBER_TOKEN_LENGTH}}}$", + flags=re.IGNORECASE, +) +DOCUMENT_NUMBER_EXTRACT_PATTERN = re.compile( + rf"(?:AP|RE|AD)-\d{{14}}-[{DOCUMENT_NUMBER_TOKEN_ALPHABET}]{{{DOCUMENT_NUMBER_TOKEN_LENGTH}}}" + r"|APP-\d{8}-[A-Z0-9]{6}" + r"|EXP-\d{6}-\d{3}", + flags=re.IGNORECASE, +) + + +def generate_document_token() -> str: + return "".join( + secrets.choice(DOCUMENT_NUMBER_TOKEN_ALPHABET) + for _ in range(DOCUMENT_NUMBER_TOKEN_LENGTH) + ) + + +def build_document_number( + kind: DocumentNumberKind, + *, + timestamp: datetime | None = None, + token: str | None = None, +) -> str: + prefix = DOCUMENT_NUMBER_PREFIXES[kind] + generated_at = timestamp or datetime.now(UTC) + if generated_at.tzinfo is None: + generated_at = generated_at.replace(tzinfo=UTC) + normalized_token = (token or generate_document_token()).strip().upper() + if not re.fullmatch( + rf"[{DOCUMENT_NUMBER_TOKEN_ALPHABET}]{{{DOCUMENT_NUMBER_TOKEN_LENGTH}}}", + normalized_token, + ): + raise ValueError("document number token must be 8 chars from the configured alphabet") + return f"{prefix}-{generated_at.astimezone(UTC):%Y%m%d%H%M%S}-{normalized_token}" + + +def generate_unique_expense_claim_no( + db: Session, + kind: DocumentNumberKind, + *, + timestamp: datetime | None = None, + token_factory: Callable[[], str] = generate_document_token, + max_attempts: int = 8, +) -> str: + for _ in range(max_attempts): + candidate = build_document_number( + kind, + timestamp=timestamp, + token=token_factory(), + ) + exists = db.scalar( + select(ExpenseClaim.id) + .where(ExpenseClaim.claim_no == candidate) + .limit(1) + ) + if exists is None: + return candidate + raise RuntimeError(f"failed to generate a unique {kind} document number") + + +def is_application_claim_no(value: object) -> bool: + normalized = str(value or "").strip().upper() + return normalized.startswith(("AP-", "APP-")) diff --git a/server/src/app/services/employee.py b/server/src/app/services/employee.py index 44f1a8c..50b74df 100644 --- a/server/src/app/services/employee.py +++ b/server/src/app/services/employee.py @@ -3,7 +3,6 @@ from __future__ import annotations from collections import Counter from datetime import UTC, date, datetime from typing import Any -from zoneinfo import ZoneInfo from sqlalchemy import inspect, select, text from sqlalchemy.orm import Session @@ -29,24 +28,31 @@ from app.schemas.employee import ( EmployeeUpdate, ) from app.services.employee_import import EmployeeImportCoordinator -from app.services.employee_serialization import ( - format_history_datetime as serialize_history_datetime, - serialize_employee, -) +from app.services.employee_serialization import serialize_employee from app.services.employee_spreadsheet import build_import_template_bytes from app.services.employee_seed import ( + CANONICAL_DEPARTMENT_CODES, EMPLOYEE_DEFINITIONS, EMPLOYEE_PROFILE_REPAIRS, + LEGACY_ORGANIZATION_UNIT_CODE_MAP, ORGANIZATION_DEFINITIONS, ROLE_DEFINITIONS, ROLE_DISPLAY_ORDER, ROLE_PERMISSION_MAP, + normalize_organization_unit_code, +) +from app.services.employee_time import ( + format_date, + format_datetime, + format_history_datetime, + normalize_optional_text, + parse_date, + parse_datetime, ) logger = get_logger("app.services.employee") DEFAULT_EMPLOYEE_PASSWORD = "123456" MAX_EMPLOYEE_CHANGE_LOGS = 5 -DISPLAY_TIMEZONE = ZoneInfo("Asia/Shanghai") STATUS_TONE_MAP = { "在职": "success", @@ -85,6 +91,7 @@ class EmployeeService: self._seed_roles() self._seed_organization_units() self._seed_employees() + self._normalize_legacy_employee_departments() self.db.commit() except Exception: self.db.rollback() @@ -132,6 +139,7 @@ class EmployeeService: for role in self._sorted_roles(self.repository.list_roles()) ] + canonical_department_codes = set(CANONICAL_DEPARTMENT_CODES) organization_options = [ EmployeeOrganizationRead( id=unit.id, @@ -143,7 +151,11 @@ class EmployeeService: managerName=unit.manager_name, ) for unit in sorted( - self.repository.list_organization_units(), + ( + unit + for unit in self.repository.list_organization_units() + if unit.unit_code in canonical_department_codes + ), key=lambda item: item.name, ) ] @@ -185,7 +197,8 @@ class EmployeeService: ) if payload.organization_unit_code: - employee.organization_unit = self.repository.get_organization_by_code(payload.organization_unit_code) + organization_code = normalize_organization_unit_code(payload.organization_unit_code) + employee.organization_unit = self.repository.get_organization_by_code(organization_code) if payload.manager_employee_no: employee.manager = self.repository.get_by_employee_no(payload.manager_employee_no) @@ -224,7 +237,7 @@ class EmployeeService: changed_fields.append("姓名") if "gender" in payload.model_fields_set: - gender = self._normalize_optional_text(payload.gender) + gender = normalize_optional_text(payload.gender) if gender != employee.gender: employee.gender = gender changed_fields.append("性别") @@ -236,7 +249,7 @@ class EmployeeService: changed_fields.append("出生日期") if "phone" in payload.model_fields_set: - phone = self._normalize_optional_text(payload.phone) + phone = normalize_optional_text(payload.phone) if phone != employee.phone: employee.phone = phone changed_fields.append("手机号") @@ -257,7 +270,7 @@ class EmployeeService: changed_fields.append("入职日期") if "location" in payload.model_fields_set: - location = self._normalize_optional_text(payload.location) + location = normalize_optional_text(payload.location) if location != employee.location: employee.location = location changed_fields.append("办公地点") @@ -279,19 +292,21 @@ class EmployeeService: changed_fields.append("职级") if "cost_center" in payload.model_fields_set: - cost_center = self._normalize_optional_text(payload.cost_center) + cost_center = normalize_optional_text(payload.cost_center) if cost_center != employee.cost_center: employee.cost_center = cost_center changed_fields.append("成本中心") if "finance_owner_name" in payload.model_fields_set: - finance_owner_name = self._normalize_optional_text(payload.finance_owner_name) + finance_owner_name = normalize_optional_text(payload.finance_owner_name) if finance_owner_name != employee.finance_owner_name: employee.finance_owner_name = finance_owner_name changed_fields.append("财务归口") if "organization_unit_code" in payload.model_fields_set: - organization_code = self._normalize_optional_text(payload.organization_unit_code) + organization_code = normalize_organization_unit_code( + normalize_optional_text(payload.organization_unit_code) + ) current_code = ( employee.organization_unit.unit_code if employee.organization_unit else None ) @@ -306,7 +321,7 @@ class EmployeeService: changed_fields.append("所属部门") if "manager_employee_no" in payload.model_fields_set: - manager_employee_no = self._normalize_optional_text(payload.manager_employee_no) + manager_employee_no = normalize_optional_text(payload.manager_employee_no) current_manager_no = employee.manager.employee_no if employee.manager else None if manager_employee_no: @@ -448,8 +463,8 @@ class EmployeeService: self.repository, sorted_roles=self._sorted_roles, append_change_log=self._append_change_log, - format_date=self._format_date, - format_datetime=self._format_datetime, + format_date=format_date, + format_datetime=format_datetime, default_password=DEFAULT_EMPLOYEE_PASSWORD, ) @@ -487,6 +502,12 @@ class EmployeeService: ) self.db.add(organization) existing_by_code[organization.unit_code] = organization + else: + organization.name = definition["name"] + organization.unit_type = definition["unit_type"] + organization.cost_center = definition.get("cost_center") + organization.location = definition.get("location") + organization.manager_name = definition.get("manager_name") self.db.flush() @@ -496,12 +517,30 @@ class EmployeeService: continue organization = existing_by_code[definition["unit_code"]] - if organization.parent_id: + parent = existing_by_code.get(parent_code) + if parent is not None and organization.parent_id != parent.id: + organization.parent = parent + + self.db.flush() + + def _normalize_legacy_employee_departments(self) -> None: + if not LEGACY_ORGANIZATION_UNIT_CODE_MAP: + return + + organizations_by_code = { + unit.unit_code: unit for unit in self.repository.list_organization_units() + } + for employee in self.repository.list(): + current_code = ( + employee.organization_unit.unit_code if employee.organization_unit else None + ) + next_code = normalize_organization_unit_code(current_code) + if not next_code or next_code == current_code: continue - parent = existing_by_code.get(parent_code) - if parent is not None: - organization.parent = parent + organization = organizations_by_code.get(next_code) + if organization is not None: + employee.organization_unit = organization self.db.flush() @@ -524,9 +563,9 @@ class EmployeeService: name=definition["name"], email=definition["email"], gender=definition.get("gender"), - birth_date=self._parse_date(definition.get("birth_date")), + birth_date=parse_date(definition.get("birth_date")), phone=definition.get("phone"), - join_date=self._parse_date(definition.get("join_date")), + join_date=parse_date(definition.get("join_date")), location=definition.get("location"), position=definition.get("position", "员工"), grade=definition.get("grade", "P3"), @@ -535,8 +574,8 @@ class EmployeeService: employment_status=definition.get("employment_status", "在职"), sync_state=definition.get("sync_state", "已同步"), spotlight=bool(definition.get("spotlight")), - last_sync_at=self._parse_datetime(definition.get("last_sync_at")), - updated_at=self._parse_datetime(definition.get("updated_at")), + last_sync_at=parse_datetime(definition.get("last_sync_at")), + updated_at=parse_datetime(definition.get("updated_at")), ) self.db.add(employee) employees_by_no[employee_no] = employee @@ -659,7 +698,7 @@ class EmployeeService: def _seed_employee_history(self, employee: Employee, definition: dict[str, Any]) -> None: existing_keys = { - (item.action, item.owner, self._format_datetime(item.occurred_at)) + (item.action, item.owner, format_datetime(item.occurred_at)) for item in employee.change_logs } @@ -674,14 +713,14 @@ class EmployeeService: ] for history in history_items: - occurred_at = self._parse_datetime(history.get("occurred_at")) + occurred_at = parse_datetime(history.get("occurred_at")) if occurred_at is None: continue identity = ( history["action"], history["owner"], - self._format_datetime(occurred_at), + format_datetime(occurred_at), ) if identity in existing_keys: continue @@ -743,9 +782,9 @@ class EmployeeService: employee, sorted_roles=self._sorted_roles(list(employee.roles)), sorted_change_logs=self._sorted_change_logs(employee), - format_date=self._format_date, - format_datetime=self._format_datetime, - format_history_datetime=self._format_history_datetime, + format_date=format_date, + format_datetime=format_datetime, + format_history_datetime=format_history_datetime, role_permission_map=ROLE_PERMISSION_MAP, status_tone_map=STATUS_TONE_MAP, max_change_logs=MAX_EMPLOYEE_CHANGE_LOGS, @@ -753,52 +792,3 @@ class EmployeeService: def _sorted_roles(self, roles: list[Role]) -> list[Role]: return sorted(roles, key=lambda item: (ROLE_DISPLAY_ORDER.get(item.role_code, 999), item.name)) - - @staticmethod - def _normalize_optional_text(value: str | None) -> str | None: - if value is None: - return None - text_value = value.strip() - return text_value or None - - @staticmethod - def _parse_date(value: str | None) -> date | None: - if not value: - return None - return datetime.strptime(value, "%Y-%m-%d").date() - - @staticmethod - def _parse_datetime(value: str | datetime | None) -> datetime | None: - if value is None: - return None - if isinstance(value, datetime): - return value - return datetime.strptime(value, "%Y-%m-%d %H:%M") - - @staticmethod - def _format_date(value: date | None) -> str | None: - if value is None: - return None - return value.strftime("%Y-%m-%d") - - @staticmethod - def _to_display_datetime(value: datetime) -> datetime: - if value.tzinfo is None: - normalized = value.replace(tzinfo=UTC) - else: - normalized = value.astimezone(UTC) - return normalized.astimezone(DISPLAY_TIMEZONE) - - @staticmethod - def _format_datetime(value: datetime | None) -> str | None: - if value is None: - return None - local = EmployeeService._to_display_datetime(value) - return local.strftime("%Y-%m-%d %H:%M") - - @staticmethod - def _format_history_datetime(value: datetime | None) -> str: - return serialize_history_datetime( - value, - to_display_datetime=EmployeeService._to_display_datetime, - ) diff --git a/server/src/app/services/employee_import.py b/server/src/app/services/employee_import.py index 2d3f61b..0632736 100644 --- a/server/src/app/services/employee_import.py +++ b/server/src/app/services/employee_import.py @@ -21,6 +21,7 @@ from app.services.employee_spreadsheet import ( build_export_workbook_bytes, parse_employee_workbook, ) +from app.services.employee_seed import normalize_organization_unit_code logger = get_logger("app.services.employee") @@ -52,6 +53,9 @@ class EmployeeImportCoordinator: for employee in employees: organization = employee.organization_unit role_codes = ",".join(role.role_code for role in self.sorted_roles(list(employee.roles))) + organization_code = ( + normalize_organization_unit_code(organization.unit_code) if organization else "" + ) rows.append( [ employee.employee_no, @@ -64,7 +68,7 @@ class EmployeeImportCoordinator: employee.location or "", employee.position, employee.grade, - organization.unit_code if organization else "", + organization_code or "", employee.manager.employee_no if employee.manager else "", employee.finance_owner_name or "", employee.cost_center or "", @@ -167,7 +171,8 @@ class EmployeeImportCoordinator: ) ) - if row.organization_unit_code and row.organization_unit_code not in organizations_by_code: + organization_code = normalize_organization_unit_code(row.organization_unit_code) + if organization_code and organization_code not in organizations_by_code: errors.append( EmployeeSpreadsheetError( row=row.row_number, @@ -266,8 +271,9 @@ class EmployeeImportCoordinator: employee.sync_state = "已同步" employee.last_sync_at = now - if row.organization_unit_code: - employee.organization_unit = organizations_by_code[row.organization_unit_code] + organization_code = normalize_organization_unit_code(row.organization_unit_code) + if organization_code: + employee.organization_unit = organizations_by_code[organization_code] else: employee.organization_unit = None diff --git a/server/src/app/services/employee_seed.py b/server/src/app/services/employee_seed.py index 7164e63..ecb9254 100644 --- a/server/src/app/services/employee_seed.py +++ b/server/src/app/services/employee_seed.py @@ -1,7 +1,13 @@ from __future__ import annotations from app.services.employee_seed_roles import ROLE_DEFINITIONS, ROLE_DISPLAY_ORDER, ROLE_PERMISSION_MAP -from app.services.employee_seed_organizations import EMPLOYEE_PROFILE_REPAIRS, ORGANIZATION_DEFINITIONS +from app.services.employee_seed_organizations import ( + CANONICAL_DEPARTMENT_CODES, + EMPLOYEE_PROFILE_REPAIRS, + LEGACY_ORGANIZATION_UNIT_CODE_MAP, + ORGANIZATION_DEFINITIONS, + normalize_organization_unit_code, +) from app.services.employee_seed_part1 import EMPLOYEE_DEFINITIONS_PART_1 from app.services.employee_seed_part2 import EMPLOYEE_DEFINITIONS_PART_2 @@ -11,7 +17,10 @@ __all__ = [ "ROLE_DISPLAY_ORDER", "ROLE_DEFINITIONS", "ROLE_PERMISSION_MAP", + "CANONICAL_DEPARTMENT_CODES", "ORGANIZATION_DEFINITIONS", + "LEGACY_ORGANIZATION_UNIT_CODE_MAP", "EMPLOYEE_PROFILE_REPAIRS", "EMPLOYEE_DEFINITIONS", + "normalize_organization_unit_code", ] diff --git a/server/src/app/services/employee_seed_organizations.py b/server/src/app/services/employee_seed_organizations.py index 497f658..99ac029 100644 --- a/server/src/app/services/employee_seed_organizations.py +++ b/server/src/app/services/employee_seed_organizations.py @@ -11,88 +11,89 @@ ORGANIZATION_DEFINITIONS = [ "manager_name": "李文静", }, { - "unit_code": "EXEC-OFFICE", - "name": "总经办", + "unit_code": "TECH-DEPT", + "name": "技术部", "unit_type": "department", "parent_code": "ORG-ROOT", - "cost_center": "CC-1001", - "location": "上海", - "manager_name": "李文静", - }, - { - "unit_code": "FIN-SSC", - "name": "财务共享中心", - "unit_type": "department", - "parent_code": "ORG-ROOT", - "cost_center": "CC-2108", - "location": "上海", - "manager_name": "张晓晴", - }, - { - "unit_code": "HR-OD", - "name": "人力与组织", - "unit_type": "department", - "parent_code": "ORG-ROOT", - "cost_center": "CC-3206", - "location": "杭州", - "manager_name": "陈硕", - }, - { - "unit_code": "SALES-SOUTH", - "name": "华南销售部", - "unit_type": "department", - "parent_code": "ORG-ROOT", - "cost_center": "CC-4102", - "location": "深圳", - "manager_name": "陈嘉", - }, - { - "unit_code": "SALES-EAST", - "name": "华东销售部", - "unit_type": "department", - "parent_code": "ORG-ROOT", - "cost_center": "CC-4108", - "location": "上海", - "manager_name": "秦墨然", - }, - { - "unit_code": "MKT-BRAND", - "name": "市场品牌部", - "unit_type": "department", - "parent_code": "ORG-ROOT", - "cost_center": "CC-5203", - "location": "北京", - "manager_name": "刘思雨", - }, - { - "unit_code": "RND-CENTER", - "name": "产品研发中心", - "unit_type": "department", - "parent_code": "ORG-ROOT", - "cost_center": "CC-6105", + "cost_center": "CC-6100", "location": "北京", "manager_name": "吴磊", }, { - "unit_code": "OPS-ADMIN", - "name": "行政采购部", + "unit_code": "MARKET-DEPT", + "name": "市场部", "unit_type": "department", "parent_code": "ORG-ROOT", - "cost_center": "CC-7204", + "cost_center": "CC-4100", + "location": "上海", + "manager_name": "刘思雨", + }, + { + "unit_code": "FINANCE-DEPT", + "name": "财务部", + "unit_type": "department", + "parent_code": "ORG-ROOT", + "cost_center": "CC-2100", + "location": "上海", + "manager_name": "张晓晴", + }, + { + "unit_code": "HR-DEPT", + "name": "人力资源部", + "unit_type": "department", + "parent_code": "ORG-ROOT", + "cost_center": "CC-3200", + "location": "杭州", + "manager_name": "陈硕", + }, + { + "unit_code": "PRODUCTION-DEPT", + "name": "生产部", + "unit_type": "department", + "parent_code": "ORG-ROOT", + "cost_center": "CC-7200", "location": "南京", "manager_name": "梁雨辰", }, { - "unit_code": "AUDIT-RISK", - "name": "风控与审计部", + "unit_code": "PRESIDENT-OFFICE", + "name": "总裁办", "unit_type": "department", "parent_code": "ORG-ROOT", - "cost_center": "CC-8102", + "cost_center": "CC-1000", "location": "上海", - "manager_name": "顾承宇", + "manager_name": "李文静", }, ] +CANONICAL_DEPARTMENT_CODES = ( + "TECH-DEPT", + "MARKET-DEPT", + "FINANCE-DEPT", + "HR-DEPT", + "PRODUCTION-DEPT", + "PRESIDENT-OFFICE", +) + +LEGACY_ORGANIZATION_UNIT_CODE_MAP = { + "RND-CENTER": "TECH-DEPT", + "SALES-SOUTH": "MARKET-DEPT", + "SALES-EAST": "MARKET-DEPT", + "MKT-BRAND": "MARKET-DEPT", + "FIN-SSC": "FINANCE-DEPT", + "AUDIT-RISK": "FINANCE-DEPT", + "HR-OD": "HR-DEPT", + "OPS-ADMIN": "PRODUCTION-DEPT", + "EXEC-OFFICE": "PRESIDENT-OFFICE", +} + + +def normalize_organization_unit_code(unit_code: str | None) -> str | None: + if not unit_code: + return unit_code + return LEGACY_ORGANIZATION_UNIT_CODE_MAP.get(unit_code, unit_code) + + EMPLOYEE_PROFILE_REPAIRS = [ { "employee_no": "E90919", @@ -101,7 +102,7 @@ EMPLOYEE_PROFILE_REPAIRS = [ "location": "武汉", "position": "财务智能化产品经理", "grade": "P5", - "organization_unit_code": "RND-CENTER", + "organization_unit_code": "TECH-DEPT", "manager_employee_no": "E11745", "finance_owner_name": "研发财务BP", "cost_center": "CC-6112", diff --git a/server/src/app/services/employee_seed_part1.py b/server/src/app/services/employee_seed_part1.py index ee29c54..d1fd4f7 100644 --- a/server/src/app/services/employee_seed_part1.py +++ b/server/src/app/services/employee_seed_part1.py @@ -12,7 +12,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "上海", "position": "高级财务总监", "grade": "D2", - "organization_unit_code": "EXEC-OFFICE", + "organization_unit_code": "PRESIDENT-OFFICE", "manager_employee_no": None, "finance_owner_name": "集团财务", "cost_center": "CC-1001", @@ -34,7 +34,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "上海", "position": "费用运营经理", "grade": "M3", - "organization_unit_code": "FIN-SSC", + "organization_unit_code": "FINANCE-DEPT", "manager_employee_no": "E10018", "finance_owner_name": "华东财务组", "cost_center": "CC-2108", @@ -68,7 +68,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "上海", "position": "财务分析师", "grade": "P5", - "organization_unit_code": "FIN-SSC", + "organization_unit_code": "FINANCE-DEPT", "manager_employee_no": "E10234", "finance_owner_name": "华东财务组", "cost_center": "CC-2111", @@ -90,7 +90,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "上海", "position": "财务系统专员", "grade": "P5", - "organization_unit_code": "FIN-SSC", + "organization_unit_code": "FINANCE-DEPT", "manager_employee_no": "E10234", "finance_owner_name": "华东财务组", "cost_center": "CC-2112", @@ -112,7 +112,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "上海", "position": "差旅合规专员", "grade": "P4", - "organization_unit_code": "FIN-SSC", + "organization_unit_code": "FINANCE-DEPT", "manager_employee_no": "E10234", "finance_owner_name": "华东财务组", "cost_center": "CC-2115", @@ -134,7 +134,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "杭州", "position": "组织发展主管", "grade": "P6", - "organization_unit_code": "HR-OD", + "organization_unit_code": "HR-DEPT", "manager_employee_no": "E11618", "finance_owner_name": "总部财务BP", "cost_center": "CC-3206", @@ -156,7 +156,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "杭州", "position": "人力资源经理", "grade": "M2", - "organization_unit_code": "HR-OD", + "organization_unit_code": "HR-DEPT", "manager_employee_no": "E10018", "finance_owner_name": "总部财务BP", "cost_center": "CC-3201", @@ -178,7 +178,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "杭州", "position": "HRBP", "grade": "P4", - "organization_unit_code": "HR-OD", + "organization_unit_code": "HR-DEPT", "manager_employee_no": "E11618", "finance_owner_name": "总部财务BP", "cost_center": "CC-3208", @@ -200,7 +200,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "北京", "position": "品牌市场经理", "grade": "M2", - "organization_unit_code": "MKT-BRAND", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E10018", "finance_owner_name": "市场财务BP", "cost_center": "CC-5203", @@ -222,7 +222,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "北京", "position": "品牌策划", "grade": "P4", - "organization_unit_code": "MKT-BRAND", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E11026", "finance_owner_name": "市场财务BP", "cost_center": "CC-5207", @@ -244,7 +244,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "北京", "position": "数字营销专员", "grade": "P4", - "organization_unit_code": "MKT-BRAND", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E11026", "finance_owner_name": "市场财务BP", "cost_center": "CC-5209", @@ -266,7 +266,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "深圳", "position": "区域销售经理", "grade": "M2", - "organization_unit_code": "SALES-SOUTH", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E10018", "finance_owner_name": "华南财务组", "cost_center": "CC-4102", @@ -288,7 +288,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "深圳", "position": "销售运营专家", "grade": "P5", - "organization_unit_code": "SALES-SOUTH", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E11602", "finance_owner_name": "华南财务组", "cost_center": "CC-4106", @@ -310,7 +310,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "深圳", "position": "大客户代表", "grade": "P4", - "organization_unit_code": "SALES-SOUTH", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E11602", "finance_owner_name": "华南财务组", "cost_center": "CC-4109", @@ -332,7 +332,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "深圳", "position": "销售协调专员", "grade": "P3", - "organization_unit_code": "SALES-SOUTH", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E11602", "finance_owner_name": "华南财务组", "cost_center": "CC-4112", @@ -354,7 +354,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "北京", "position": "研发平台主管", "grade": "M3", - "organization_unit_code": "RND-CENTER", + "organization_unit_code": "TECH-DEPT", "manager_employee_no": "E10018", "finance_owner_name": "研发财务BP", "cost_center": "CC-6105", @@ -376,7 +376,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "北京", "position": "产品经理", "grade": "P5", - "organization_unit_code": "RND-CENTER", + "organization_unit_code": "TECH-DEPT", "manager_employee_no": "E11745", "finance_owner_name": "研发财务BP", "cost_center": "CC-6112", @@ -398,7 +398,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "北京", "position": "后端工程师", "grade": "P5", - "organization_unit_code": "RND-CENTER", + "organization_unit_code": "TECH-DEPT", "manager_employee_no": "E11745", "finance_owner_name": "研发财务BP", "cost_center": "CC-6114", @@ -420,7 +420,7 @@ EMPLOYEE_DEFINITIONS_PART_1 = [ "location": "北京", "position": "数据工程师", "grade": "P5", - "organization_unit_code": "RND-CENTER", + "organization_unit_code": "TECH-DEPT", "manager_employee_no": "E11745", "finance_owner_name": "研发财务BP", "cost_center": "CC-6116", diff --git a/server/src/app/services/employee_seed_part2.py b/server/src/app/services/employee_seed_part2.py index 27fa80f..772dcc5 100644 --- a/server/src/app/services/employee_seed_part2.py +++ b/server/src/app/services/employee_seed_part2.py @@ -12,7 +12,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "北京", "position": "测试负责人", "grade": "P6", - "organization_unit_code": "RND-CENTER", + "organization_unit_code": "TECH-DEPT", "manager_employee_no": "E11745", "finance_owner_name": "研发财务BP", "cost_center": "CC-6119", @@ -34,7 +34,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "南京", "position": "行政采购专员", "grade": "P4", - "organization_unit_code": "OPS-ADMIN", + "organization_unit_code": "PRODUCTION-DEPT", "manager_employee_no": "E12653", "finance_owner_name": "行政财务BP", "cost_center": "CC-7204", @@ -56,7 +56,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "南京", "position": "行政运营经理", "grade": "M1", - "organization_unit_code": "OPS-ADMIN", + "organization_unit_code": "PRODUCTION-DEPT", "manager_employee_no": "E10018", "finance_owner_name": "行政财务BP", "cost_center": "CC-7201", @@ -78,7 +78,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "上海", "position": "风控审计经理", "grade": "M2", - "organization_unit_code": "AUDIT-RISK", + "organization_unit_code": "FINANCE-DEPT", "manager_employee_no": "E10018", "finance_owner_name": "集团财务", "cost_center": "CC-8102", @@ -112,7 +112,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "上海", "position": "审计专员", "grade": "P4", - "organization_unit_code": "AUDIT-RISK", + "organization_unit_code": "FINANCE-DEPT", "manager_employee_no": "E12661", "finance_owner_name": "集团财务", "cost_center": "CC-8105", @@ -134,7 +134,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "南京", "position": "采购合规分析师", "grade": "P4", - "organization_unit_code": "OPS-ADMIN", + "organization_unit_code": "PRODUCTION-DEPT", "manager_employee_no": "E12653", "finance_owner_name": "行政财务BP", "cost_center": "CC-7208", @@ -156,7 +156,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "上海", "position": "华东销售总监", "grade": "M2", - "organization_unit_code": "SALES-EAST", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E10018", "finance_owner_name": "华东财务组", "cost_center": "CC-4108", @@ -178,7 +178,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "上海", "position": "重点客户经理", "grade": "P5", - "organization_unit_code": "SALES-EAST", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E12067", "finance_owner_name": "华东财务组", "cost_center": "CC-4111", @@ -200,7 +200,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "上海", "position": "销售代表", "grade": "P3", - "organization_unit_code": "SALES-EAST", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E12067", "finance_owner_name": "华东财务组", "cost_center": "CC-4114", @@ -222,7 +222,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "北京", "position": "数据分析师", "grade": "P4", - "organization_unit_code": "RND-CENTER", + "organization_unit_code": "TECH-DEPT", "manager_employee_no": "E11745", "finance_owner_name": "研发财务BP", "cost_center": "CC-6122", @@ -244,7 +244,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "上海", "position": "费用核算专员", "grade": "P4", - "organization_unit_code": "FIN-SSC", + "organization_unit_code": "FINANCE-DEPT", "manager_employee_no": "E10234", "finance_owner_name": "华东财务组", "cost_center": "CC-2118", @@ -266,7 +266,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "上海", "position": "预算控制经理", "grade": "M1", - "organization_unit_code": "FIN-SSC", + "organization_unit_code": "FINANCE-DEPT", "manager_employee_no": "E10234", "finance_owner_name": "集团财务", "cost_center": "CC-2120", @@ -288,7 +288,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "深圳", "position": "渠道销售经理", "grade": "P5", - "organization_unit_code": "SALES-SOUTH", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E11602", "finance_owner_name": "华南财务组", "cost_center": "CC-4116", @@ -310,7 +310,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "北京", "position": "内容运营经理", "grade": "P5", - "organization_unit_code": "MKT-BRAND", + "organization_unit_code": "MARKET-DEPT", "manager_employee_no": "E11026", "finance_owner_name": "市场财务BP", "cost_center": "CC-5211", @@ -332,7 +332,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "北京", "position": "架构工程师", "grade": "P6", - "organization_unit_code": "RND-CENTER", + "organization_unit_code": "TECH-DEPT", "manager_employee_no": "E11745", "finance_owner_name": "研发财务BP", "cost_center": "CC-6125", @@ -354,7 +354,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "南京", "position": "供应商管理专员", "grade": "P4", - "organization_unit_code": "OPS-ADMIN", + "organization_unit_code": "PRODUCTION-DEPT", "manager_employee_no": "E12653", "finance_owner_name": "行政财务BP", "cost_center": "CC-7210", @@ -376,7 +376,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "上海", "position": "风控策略分析师", "grade": "P4", - "organization_unit_code": "AUDIT-RISK", + "organization_unit_code": "FINANCE-DEPT", "manager_employee_no": "E12661", "finance_owner_name": "集团财务", "cost_center": "CC-8108", @@ -398,7 +398,7 @@ EMPLOYEE_DEFINITIONS_PART_2 = [ "location": "上海", "position": "合规产品负责人", "grade": "P7", - "organization_unit_code": "RND-CENTER", + "organization_unit_code": "TECH-DEPT", "manager_employee_no": "E11745", "finance_owner_name": "研发财务BP", "cost_center": "CC-6128", diff --git a/server/src/app/services/employee_spreadsheet.py b/server/src/app/services/employee_spreadsheet.py index 364203f..f2fd135 100644 --- a/server/src/app/services/employee_spreadsheet.py +++ b/server/src/app/services/employee_spreadsheet.py @@ -103,7 +103,7 @@ def build_import_template_bytes() -> bytes: ("办公地点", "可选。"), ("岗位*", "必填。"), ("职级*", "必填,例如 P3、P5。"), - ("部门编码", "可选,须与系统组织编码一致,例如 FIN-SSC。"), + ("部门编码", "可选,须与系统组织编码一致,例如 FINANCE-DEPT。"), ("直属上级工号", "可选,须为系统中已有员工编号,或出现在本次导入表中。"), ("财务归口", "可选。"), ("成本中心", "可选。"), diff --git a/server/src/app/services/employee_time.py b/server/src/app/services/employee_time.py new file mode 100644 index 0000000..7e185a4 --- /dev/null +++ b/server/src/app/services/employee_time.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +from datetime import UTC, date, datetime +from zoneinfo import ZoneInfo + +from app.services.employee_serialization import format_history_datetime as serialize_history_datetime + +DISPLAY_TIMEZONE = ZoneInfo("Asia/Shanghai") + + +def normalize_optional_text(value: str | None) -> str | None: + if value is None: + return None + text_value = value.strip() + return text_value or None + + +def parse_date(value: str | None) -> date | None: + if not value: + return None + return datetime.strptime(value, "%Y-%m-%d").date() + + +def parse_datetime(value: str | datetime | None) -> datetime | None: + if value is None: + return None + if isinstance(value, datetime): + return value + return datetime.strptime(value, "%Y-%m-%d %H:%M") + + +def format_date(value: date | None) -> str | None: + if value is None: + return None + return value.strftime("%Y-%m-%d") + + +def to_display_datetime(value: datetime) -> datetime: + if value.tzinfo is None: + normalized = value.replace(tzinfo=UTC) + else: + normalized = value.astimezone(UTC) + return normalized.astimezone(DISPLAY_TIMEZONE) + + +def format_datetime(value: datetime | None) -> str | None: + if value is None: + return None + return to_display_datetime(value).strftime("%Y-%m-%d %H:%M") + + +def format_history_datetime(value: datetime | None) -> str: + return serialize_history_datetime(value, to_display_datetime=to_display_datetime) diff --git a/server/src/app/services/expense_claim_access_policy.py b/server/src/app/services/expense_claim_access_policy.py index 20e1377..e3039aa 100644 --- a/server/src/app/services/expense_claim_access_policy.py +++ b/server/src/app/services/expense_claim_access_policy.py @@ -17,6 +17,7 @@ ARCHIVE_CENTER_ROLE_CODES = {"finance", "executive", "auditor"} APPROVAL_VISIBLE_CLAIM_ROLE_CODES = {"manager", "approver"} CLAIM_DELETE_ROLE_CODES = {"executive"} ARCHIVED_CLAIM_STATUSES = ("approved", "completed", "paid") +APPLICATION_ARCHIVED_STAGES = ("审批完成", "申请归档", "completed") class ExpenseClaimAccessPolicy: @@ -39,9 +40,22 @@ class ExpenseClaimAccessPolicy: def build_archived_claim_condition() -> Any: normalized_status = func.lower(func.coalesce(ExpenseClaim.status, "")) stage = func.coalesce(ExpenseClaim.approval_stage, "") + normalized_type = func.lower(func.coalesce(ExpenseClaim.expense_type, "")) + claim_no = func.upper(func.coalesce(ExpenseClaim.claim_no, "")) + application_condition = or_( + claim_no.like("AP-%"), + claim_no.like("APP-%"), + normalized_type == "application", + normalized_type.like("%\\_application", escape="\\"), + ) return or_( stage == "归档入账", stage == "completed", + and_( + application_condition, + normalized_status.in_(ARCHIVED_CLAIM_STATUSES), + stage.in_(APPLICATION_ARCHIVED_STAGES), + ), and_( normalized_status.in_(ARCHIVED_CLAIM_STATUSES), or_( @@ -59,6 +73,27 @@ class ExpenseClaimAccessPolicy: return True return bool(ExpenseClaimAccessPolicy.normalize_role_codes(current_user) & CLAIM_DELETE_ROLE_CODES) + @staticmethod + def is_archived_claim(claim: ExpenseClaim) -> bool: + normalized_status = str(claim.status or "").strip().lower() + stage = str(claim.approval_stage or "").strip() + if stage in {"归档入账", "completed"}: + return True + normalized_type = str(claim.expense_type or "").strip().lower() + claim_no = str(claim.claim_no or "").strip().upper() + is_application_claim = ( + claim_no.startswith(("AP-", "APP-")) + or normalized_type == "application" + or normalized_type.endswith("_application") + ) + if ( + is_application_claim + and normalized_status in ARCHIVED_CLAIM_STATUSES + and stage in APPLICATION_ARCHIVED_STAGES + ): + return True + return normalized_status in ARCHIVED_CLAIM_STATUSES and stage in {"", "归档入账", "completed"} + def can_return_claim(self, current_user: CurrentUserContext, claim: ExpenseClaim) -> bool: if self.has_privileged_claim_access(current_user): return True @@ -338,6 +373,7 @@ class ExpenseClaimAccessPolicy: else: add_condition("employee_id", username) add_condition("employee_name", username) + add_condition("employee_name", str(current_user.name or "").strip()) return conditions @@ -422,10 +458,14 @@ class ExpenseClaimAccessPolicy: return stmt.where(or_(*conditions)) def apply_archived_claim_scope(self, stmt: Any, current_user: CurrentUserContext) -> Any: + archived_condition = self.build_archived_claim_condition() if not self.has_archive_center_access(current_user): - return stmt.where(ExpenseClaim.id == "__no_visible_claim__") + owned_conditions = self.build_personal_claim_conditions(current_user) + if not owned_conditions: + return stmt.where(ExpenseClaim.id == "__no_visible_claim__") + return stmt.where(archived_condition, or_(*owned_conditions)) - return stmt.where(self.build_archived_claim_condition()) + return stmt.where(archived_condition) @staticmethod def resolve_claim_manager_name(claim: ExpenseClaim) -> str: diff --git a/server/src/app/services/expense_claim_application_handoff.py b/server/src/app/services/expense_claim_application_handoff.py new file mode 100644 index 0000000..baab82b --- /dev/null +++ b/server/src/app/services/expense_claim_application_handoff.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +import uuid +from datetime import UTC, datetime +from decimal import Decimal +from typing import Any + +from app.models.financial_record import ExpenseClaim + + +APPLICATION_REIMBURSEMENT_TYPE_MAP = { + "travel_application": "travel", + "purchase_application": "office", + "meeting_application": "meeting", + "expense_application": "other", + "application": "other", +} + + +class ExpenseClaimApplicationHandoffMixin: + @staticmethod + def _resolve_reimbursement_type_from_application(expense_type: str | None) -> str: + normalized = str(expense_type or "").strip().lower() + if normalized in APPLICATION_REIMBURSEMENT_TYPE_MAP: + return APPLICATION_REIMBURSEMENT_TYPE_MAP[normalized] + if normalized.endswith("_application"): + return normalized.removesuffix("_application") or "other" + return normalized or "other" + + def _create_reimbursement_draft_from_application( + self, + *, + application_claim: ExpenseClaim, + approval_flag: dict[str, Any], + operator: str, + ) -> ExpenseClaim: + occurred_at = application_claim.occurred_at or datetime.now(UTC) + created_at = datetime.now(UTC) + draft_claim = ExpenseClaim( + claim_no=self._generate_claim_no(occurred_at), + employee_id=application_claim.employee_id, + employee_name=application_claim.employee_name, + department_id=application_claim.department_id, + department_name=application_claim.department_name, + project_code=application_claim.project_code, + expense_type=self._resolve_reimbursement_type_from_application(application_claim.expense_type), + reason=application_claim.reason, + location=application_claim.location, + amount=application_claim.amount or Decimal("0.00"), + currency=application_claim.currency or "CNY", + invoice_count=0, + occurred_at=occurred_at, + submitted_at=None, + status="draft", + approval_stage="待提交", + risk_flags_json=[ + { + "source": "application_handoff", + "event_type": "expense_application_to_reimbursement_draft", + "handoff_event_id": str(uuid.uuid4()), + "severity": "info", + "label": "申请转报销草稿", + "message": ( + f"费用申请 {application_claim.claim_no} 已由 {operator} 确认审核," + "系统已生成报销草稿。" + ), + "application_claim_id": application_claim.id, + "application_claim_no": application_claim.claim_no, + "application_budget_amount": str(application_claim.amount or Decimal("0.00")), + "application_approval_event_id": str(approval_flag.get("approval_event_id") or ""), + "leader_opinion": str(approval_flag.get("opinion") or "").strip(), + "created_at": created_at.isoformat(), + } + ], + ) + self.db.add(draft_claim) + self.db.flush() + + approval_flag["generated_draft_claim_id"] = draft_claim.id + approval_flag["generated_draft_claim_no"] = draft_claim.claim_no + approval_flag["handoff_event_type"] = "expense_application_to_reimbursement_draft" + approval_flag["handoff_message"] = f"已生成报销草稿 {draft_claim.claim_no}。" + return draft_claim diff --git a/server/src/app/services/expense_claim_draft_persistence.py b/server/src/app/services/expense_claim_draft_persistence.py index c05d816..326b834 100644 --- a/server/src/app/services/expense_claim_draft_persistence.py +++ b/server/src/app/services/expense_claim_draft_persistence.py @@ -33,6 +33,7 @@ from app.services.agent_asset_spreadsheet import RISK_RULES_LIBRARY from app.services.agent_foundation import AgentFoundationService from app.services.audit import AuditLogService from app.services.document_intelligence import build_document_insight +from app.services.document_numbering import generate_unique_expense_claim_no from app.services.expense_claim_access_policy import ExpenseClaimAccessPolicy from app.services.expense_claim_attachment_presentation import ExpenseClaimAttachmentPresentation from app.services.expense_claim_attachment_storage import ExpenseClaimAttachmentStorage @@ -250,23 +251,11 @@ class ExpenseClaimDraftPersistenceMixin: ) def _generate_claim_no(self, occurred_at: datetime) -> str: - month_code = occurred_at.strftime("%Y%m") - prefix = f"EXP-{month_code}-" - existing_claim_nos = list( - self.db.scalars( - select(ExpenseClaim.claim_no).where(ExpenseClaim.claim_no.like(f"{prefix}%")) - ) + return generate_unique_expense_claim_no( + self.db, + "reimbursement", + timestamp=datetime.now(UTC), ) - max_suffix = 0 - for claim_no in existing_claim_nos: - normalized = str(claim_no or "").strip() - if not normalized.startswith(prefix): - continue - suffix = normalized[len(prefix):] - if not suffix.isdigit(): - continue - max_suffix = max(max_suffix, int(suffix)) - return f"{prefix}{max_suffix + 1:03d}" @staticmethod def _resolve_claim_no_retry_count(context_json: dict[str, Any]) -> int: diff --git a/server/src/app/services/expense_claim_platform_risk.py b/server/src/app/services/expense_claim_platform_risk.py index bfde667..5682bb7 100644 --- a/server/src/app/services/expense_claim_platform_risk.py +++ b/server/src/app/services/expense_claim_platform_risk.py @@ -13,6 +13,7 @@ from app.services.agent_asset_spreadsheet import RISK_RULES_LIBRARY from app.services.expense_rule_runtime import ( RuntimeTravelPolicy, ) +from app.services.risk_rule_manifest_normalizer import normalize_risk_rule_manifest from app.services.risk_rule_template_executor import RiskRuleTemplateExecutor @@ -46,7 +47,7 @@ class ExpenseClaimPlatformRiskMixin: flags.append(flag) severity = str(flag.get("severity") or "").strip().lower() action = str(flag.get("action") or "").strip().lower() - if severity == "high" or action == "block": + if severity in {"high", "critical"} or action == "block": blocking_reasons.append(str(flag.get("message") or flag.get("label") or "").strip()) deduplicated_reasons = list(dict.fromkeys(reason for reason in blocking_reasons if reason)) @@ -100,6 +101,7 @@ class ExpenseClaimPlatformRiskMixin: except (FileNotFoundError, ValueError): continue + payload = normalize_risk_rule_manifest(payload) manifest_code = str(payload.get("rule_code") or rule_code).strip() if not manifest_code or (code_filter and manifest_code not in code_filter): continue @@ -129,6 +131,7 @@ class ExpenseClaimPlatformRiskMixin: ) except (FileNotFoundError, ValueError): continue + payload = normalize_risk_rule_manifest(payload) rule_code = str(payload.get("rule_code") or "").strip() if not rule_code or rule_code in manifests_by_code: continue @@ -612,7 +615,7 @@ class ExpenseClaimPlatformRiskMixin: outcomes = manifest.get("outcomes") if isinstance(manifest.get("outcomes"), dict) else {} fail_outcome = outcomes.get("fail") if isinstance(outcomes.get("fail"), dict) else {} severity = str(fail_outcome.get("severity") or "medium").strip().lower() or "medium" - default_action = "block" if severity == "high" else "manual_review" + default_action = "block" if severity in {"high", "critical"} else "manual_review" action = str(fail_outcome.get("action") or default_action).strip() label = str(manifest.get("name") or manifest.get("rule_code") or "风险规则命中").strip() diff --git a/server/src/app/services/expense_claims.py b/server/src/app/services/expense_claims.py index 41c6c85..3d68c5a 100644 --- a/server/src/app/services/expense_claims.py +++ b/server/src/app/services/expense_claims.py @@ -33,9 +33,11 @@ from app.services.agent_asset_spreadsheet import RISK_RULES_LIBRARY from app.services.agent_foundation import AgentFoundationService from app.services.audit import AuditLogService from app.services.document_intelligence import build_document_insight +from app.services.document_numbering import is_application_claim_no from app.services.expense_claim_access_policy import ExpenseClaimAccessPolicy from app.services.expense_claim_attachment_presentation import ExpenseClaimAttachmentPresentation from app.services.expense_claim_attachment_storage import ExpenseClaimAttachmentStorage +from app.services.expense_claim_application_handoff import ExpenseClaimApplicationHandoffMixin from app.services.expense_claim_attachment_analysis import ExpenseClaimAttachmentAnalysisMixin from app.services.expense_claim_attachment_document import ExpenseClaimAttachmentDocumentMixin from app.services.expense_claim_attachment_operations import ExpenseClaimAttachmentOperationsMixin @@ -124,6 +126,7 @@ from app.services.ocr import OcrService class ExpenseClaimService( + ExpenseClaimApplicationHandoffMixin, ExpenseClaimAttachmentOperationsMixin, ExpenseClaimReviewPreviewMixin, ExpenseClaimDraftFlowMixin, @@ -153,7 +156,7 @@ class ExpenseClaimService( or "" ).strip().lower() return ( - claim_no.startswith("APP-") + is_application_claim_no(claim_no) or expense_type == "application" or expense_type.endswith("_application") or document_type in {"application", "expense_application"} @@ -492,9 +495,28 @@ class ExpenseClaimService( def delete_claim(self, claim_id: str, current_user: CurrentUserContext) -> ExpenseClaim | None: claim = self.get_claim(claim_id, current_user) + if claim is None and ( + current_user.is_admin or self._access_policy.has_archive_center_access(current_user) + ): + candidate_claim = self.db.scalar( + select(ExpenseClaim) + .options( + selectinload(ExpenseClaim.items), + selectinload(ExpenseClaim.employee).selectinload(Employee.manager), + selectinload(ExpenseClaim.employee).selectinload(Employee.roles), + ) + .where(ExpenseClaim.id == claim_id) + ) + if candidate_claim is not None and ( + current_user.is_admin or self._access_policy.is_archived_claim(candidate_claim) + ): + claim = candidate_claim if claim is None: return None + if self._access_policy.is_archived_claim(claim) and not current_user.is_admin: + raise ValueError("已归档单据不能删除,只有高级管理员可以执行删除。") + if not self._access_policy.has_claim_delete_access(current_user): self._ensure_draft_claim(claim) if not self._access_policy.is_claim_owned_by_current_user(claim, current_user): @@ -642,7 +664,7 @@ class ExpenseClaimService( label = "领导审批通过" next_status = "approved" next_stage = "审批完成" - default_message = "{operator} 已审批通过,申请流程完成。" + default_message = "{operator} 已确认审核,申请流程完成并生成报销草稿。" else: event_type = "expense_claim_approval" label = "领导审批通过" @@ -663,9 +685,12 @@ class ExpenseClaimService( else: raise ValueError("当前节点不支持审批通过。") + approval_opinion = str(opinion or "").strip() + if previous_stage == "直属领导审批" and not approval_opinion: + raise ValueError("领导审核意见不能为空,请填写意见后再确认审核。") + before_json = self._serialize_claim(claim) operator = self._access_policy.resolve_current_user_display_name(current_user) - approval_opinion = str(opinion or "").strip() approval_flag = { "source": approval_source, "event_type": event_type, @@ -692,6 +717,12 @@ class ExpenseClaimService( claim.approval_stage = next_stage if claim.submitted_at is None: claim.submitted_at = datetime.now(UTC) + if is_application_claim and previous_stage == "直属领导审批": + generated_draft = self._create_reimbursement_draft_from_application( + application_claim=claim, + approval_flag=approval_flag, + operator=operator, + ) claim.risk_flags_json = [*list(claim.risk_flags_json or []), approval_flag] self.db.commit() diff --git a/server/src/app/services/ontology_detection.py b/server/src/app/services/ontology_detection.py index 91499e3..2017ccb 100644 --- a/server/src/app/services/ontology_detection.py +++ b/server/src/app/services/ontology_detection.py @@ -399,6 +399,10 @@ class OntologyDetectionMixin: "missing_slots 使用简短 snake_case,例如 expense_type, amount, " "customer_name, participants, attachments。" "entity_hints 只填写你比较确定的业务对象;如果不确定,可以返回空数组。" + "费用申请场景下,建议把干净的申请事由放入 type=reason," + "把出行方式放入 type=transport_mode,取值优先为飞机、火车、轮船。" + "reason 只能保留真实业务目的,例如“服务美团业务部署”," + "不要把发生时间、地点、出差天数、交通方式混进 reason。" ) user_prompt = ( "请根据以下事实输出 JSON:\n" @@ -415,6 +419,9 @@ class OntologyDetectionMixin: ' "entity_hints": [\n' ' {"type": "expense_type", "value": "交通费", ' '"normalized_value": "transport", "role": "filter", ' + '"confidence": 0.86},\n' + ' {"type": "reason", "value": "服务客户业务部署", ' + '"normalized_value": "服务客户业务部署", "role": "target", ' '"confidence": 0.86}\n' " ]\n" "}" diff --git a/server/src/app/services/ontology_extraction.py b/server/src/app/services/ontology_extraction.py index 3184282..c4792c8 100644 --- a/server/src/app/services/ontology_extraction.py +++ b/server/src/app/services/ontology_extraction.py @@ -13,6 +13,7 @@ from app.schemas.ontology import ( OntologyPermission, OntologyTimeRange, ) +from app.services.document_numbering import DOCUMENT_NUMBER_EXTRACT_PATTERN from app.services.ontology_rules import ( AMOUNT_PATTERN, DATE_RANGE_PATTERN, @@ -243,7 +244,8 @@ class OntologyExtractionMixin: for code in re.findall(r"PRJ-[A-Z]+-\d+", query, flags=re.IGNORECASE): upsert(self._make_entity("project", code, code.upper(), role="filter")) - for code in re.findall(r"EXP-\d{6}-\d{3}", query, flags=re.IGNORECASE): + for match in DOCUMENT_NUMBER_EXTRACT_PATTERN.finditer(query): + code = match.group(0) upsert(self._make_entity("expense_claim", code, code.upper())) for code in re.findall(r"AR-\d{6}-\d{3}", query, flags=re.IGNORECASE): upsert(self._make_entity("receivable", code, code.upper())) diff --git a/server/src/app/services/risk_rule_flow_diagram.py b/server/src/app/services/risk_rule_flow_diagram.py index 2f63692..3e7428e 100644 --- a/server/src/app/services/risk_rule_flow_diagram.py +++ b/server/src/app/services/risk_rule_flow_diagram.py @@ -4,6 +4,7 @@ from __future__ import annotations import html from dataclasses import dataclass +from typing import Any @dataclass(frozen=True) @@ -25,6 +26,9 @@ class RiskRuleFlowDiagramSpec: basis: str pass_text: str fail_text: str + fact_lines: tuple[str, ...] = () + condition_lines: tuple[str, ...] = () + hit_logic: str = "" @dataclass(frozen=True) @@ -66,35 +70,49 @@ class RiskRuleFlowDiagramRenderer: border="#fecaca", surface="#fef2f2", ), + "critical": RiskRuleFlowDiagramPalette( + accent="#991b1b", + accent_dark="#7f1d1d", + border="#fca5a5", + surface="#fff1f2", + ), } def render(self, spec: RiskRuleFlowDiagramSpec) -> str: title = self._truncate(spec.title, 26) palette = self._palette(spec.severity) + fact_lines = spec.fact_lines or self._field_lines(spec.fields) + condition_lines = spec.condition_lines or (spec.basis,) + hit_logic = spec.hit_logic or spec.basis - return f""" + return f""" {self._escape(title)}流程说明 - 风险规则只读流程图,展示从业务单据提交到风险复核的判断路径。 + 风险规则只读流程图,展示字段事实、集合交集、日期范围、例外说明和命中路径。 + + + - - + + RULE FLOW - {self._node("业务输入", spec.start, 48, 118, 124, 60)} - {self._node("字段取数", "读取字段证据", 214, 118, 132, 60)} - {self._diamond("判断依据", spec.decision, 392, 92, 112, 112)} - {self._node("继续流转", spec.pass_text, 562, 74, 126, 60)} - {self._node("进入复核", spec.fail_text, 562, 190, 126, 62, palette=palette)} - {self._note(spec.basis, 214, 218, 290, 36)} - - - - - - + {self._node("业务输入", spec.start, 38, 142, 120, 62)} + {self._panel("字段事实", fact_lines, 196, 64, 240, 128)} + {self._panel("判断条件", condition_lines, 196, 216, 382, 104)} + {self._diamond("命中逻辑", hit_logic, 494, 80, 122, 122)} + {self._node("继续流转", spec.pass_text, 688, 76, 122, 60)} + {self._node("进入复核", spec.fail_text, 688, 226, 122, 68, palette=palette)} + + + + + + + + """ def _node( @@ -137,6 +155,28 @@ class RiskRuleFlowDiagramRenderer: {self._text_lines(body_lines, cx, cy + 11, "middle", self._MUTED, 10.2)} """ + def _panel( + self, + title: str, + lines: tuple[str, ...], + x: int, + y: int, + width: int, + height: int, + ) -> str: + visible = [self._truncate(line, 36) for line in list(lines)[:4]] + if not visible: + visible = ["读取规则字段并归一化为判断事实"] + rows = "\n ".join( + f'{self._escape(line)}' + for index, line in enumerate(visible) + ) + return f""" + + {self._escape(title)} + {rows} + """ + def _note( self, body: str, @@ -152,6 +192,13 @@ class RiskRuleFlowDiagramRenderer: {self._text_lines(lines, x + 54, y + 22, "start", self._TEXT, 10.2)} """ + def _field_lines(self, fields: tuple[RiskRuleFlowDiagramField, ...]) -> tuple[str, ...]: + rows = [] + for index, field in enumerate(fields[:4]): + label = field.label or field.key + rows.append(f"{chr(65 + index)}={label}[{field.key}]") + return tuple(rows) + def _text_lines( self, lines: list[str], @@ -189,3 +236,130 @@ class RiskRuleFlowDiagramRenderer: @classmethod def _palette(cls, severity: str) -> RiskRuleFlowDiagramPalette: return cls._PALETTES.get(str(severity or "").strip().lower(), cls._PALETTES["medium"]) + + +def build_risk_rule_flow_diagram_details( + payload: dict[str, Any], + fields: list[RiskRuleFlowDiagramField], +) -> dict[str, tuple[str, ...] | str]: + params = payload.get("params") if isinstance(payload.get("params"), dict) else {} + rule_ir = params.get("rule_ir") if isinstance(params.get("rule_ir"), dict) else {} + facts = rule_ir.get("facts") if isinstance(rule_ir.get("facts"), list) else [] + fact_lines = _build_fact_lines(facts, fields) + condition_lines = _build_condition_lines(params, fields) + hit_logic = _format_hit_logic(params.get("hit_logic")) or str( + params.get("formula") or params.get("condition_summary") or "" + ).strip() + return { + "fact_lines": tuple(fact_lines), + "condition_lines": tuple(condition_lines), + "hit_logic": hit_logic, + } + + +def _build_fact_lines( + facts: list[Any], + fields: list[RiskRuleFlowDiagramField], +) -> list[str]: + label_by_key = {field.key: field.label or field.key for field in fields} + rows: list[str] = [] + for fact in facts[:4]: + if not isinstance(fact, dict): + continue + fact_id = str(fact.get("id") or "").strip() + label = str(fact.get("label") or fact_id or "事实").strip() + field_keys = _read_string_list(fact.get("fields")) + field_text = "∪".join(label_by_key.get(key, key) for key in field_keys[:3]) + rows.append(f"{fact_id + '=' if fact_id else ''}{label}: {field_text or '规则字段'}") + if rows: + return rows + return [ + f"{chr(65 + index)}={field.label or field.key}[{field.key}]" + for index, field in enumerate(fields[:4]) + ] + + +def _build_condition_lines( + params: dict[str, Any], + fields: list[RiskRuleFlowDiagramField], +) -> list[str]: + label_by_key = {field.key: field.label or field.key for field in fields} + rows: list[str] = [] + conditions = params.get("conditions") if isinstance(params.get("conditions"), list) else [] + for index, condition in enumerate(conditions[:4], start=1): + if not isinstance(condition, dict): + continue + rows.append(_format_condition(condition, label_by_key, index)) + if rows: + return rows + summary = str(params.get("condition_summary") or "").strip() + return [summary] if summary else [] + + +def _format_condition(condition: dict[str, Any], label_by_key: dict[str, str], index: int) -> str: + operator = str(condition.get("operator") or "").strip() + condition_id = str(condition.get("id") or f"C{index}").strip() + prefix = f"{condition_id}: " + if operator in {"not_in_scope", "not_in_set", "not_overlap"}: + left = _field_group(condition.get("left_fields"), label_by_key) + right = _field_group(condition.get("right_fields"), label_by_key) + return f"{prefix}{left} ∩ {right} = ∅" + if operator in {"in_scope", "overlap"}: + left = _field_group(condition.get("left_fields"), label_by_key) + right = _field_group(condition.get("right_fields"), label_by_key) + return f"{prefix}{left} ∩ {right} ≠ ∅" + if operator == "date_outside_range": + dates = _field_group(condition.get("date_fields"), label_by_key) + start = _field_group(condition.get("range_start_fields"), label_by_key) + end = _field_group(condition.get("range_end_fields"), label_by_key) + return f"{prefix}{dates} 不在 [{start}, {end}]" + if operator in {"contains_any", "not_contains_any"}: + fields = _field_group(condition.get("fields"), label_by_key) + keywords = "、".join(_read_string_list(condition.get("keywords"))[:4]) + verb = "不含" if operator == "not_contains_any" else "包含" + return f"{prefix}{fields} {verb} {keywords or '关键词'}" + if operator in {"exists_any", "exists_all", "all_present"}: + fields = _field_group(condition.get("fields"), label_by_key) + verb = "任一有值" if operator == "exists_any" else "全部有值" + return f"{prefix}{fields} {verb}" + left = str(condition.get("left") or "").strip() + right = str(condition.get("right") or "").strip() + if left or right: + return f"{prefix}{label_by_key.get(left, left)} {operator or 'compare'} {label_by_key.get(right, right)}" + return f"{prefix}{operator or '规则条件'}" + + +def _field_group(value: Any, label_by_key: dict[str, str]) -> str: + keys = _read_string_list(value) + if not keys: + return "字段集合" + return "∪".join(label_by_key.get(key, key) for key in keys[:3]) + + +def _format_hit_logic(value: Any) -> str: + if isinstance(value, str): + return value.strip() + if isinstance(value, list): + return " AND ".join(_format_hit_logic(item) for item in value if _format_hit_logic(item)) + if not isinstance(value, dict): + return "" + if isinstance(value.get("all"), list): + return " AND ".join(_wrap_logic_part(item) for item in value["all"]) + if isinstance(value.get("any"), list): + return " OR ".join(_wrap_logic_part(item) for item in value["any"]) + if "not" in value: + return f"NOT {_wrap_logic_part(value.get('not'))}" + return "" + + +def _wrap_logic_part(value: Any) -> str: + text = _format_hit_logic(value) + if isinstance(value, dict) and text: + return f"({text})" + return text + + +def _read_string_list(value: Any) -> list[str]: + if not isinstance(value, list): + return [] + return [str(item or "").strip() for item in value if str(item or "").strip()] diff --git a/server/src/app/services/risk_rule_generation.py b/server/src/app/services/risk_rule_generation.py index 12aa262..fb6001a 100644 --- a/server/src/app/services/risk_rule_generation.py +++ b/server/src/app/services/risk_rule_generation.py @@ -2,7 +2,6 @@ from __future__ import annotations import json import re -from dataclasses import dataclass from datetime import UTC, datetime from typing import Any @@ -14,135 +13,34 @@ from app.schemas.agent_asset import AgentAssetRiskRuleGenerateRequest from app.services.agent_asset_rule_library import AgentAssetRuleLibraryManager from app.services.agent_asset_spreadsheet import RISK_RULES_LIBRARY from app.services.audit import AuditLogService -from app.services.expense_type_keywords import EXPENSE_TYPE_LABEL_BY_CODE from app.services.risk_rule_flow_diagram import ( RiskRuleFlowDiagramField, RiskRuleFlowDiagramRenderer, RiskRuleFlowDiagramSpec, + build_risk_rule_flow_diagram_details, ) +from app.services.risk_rule_generation_ontology import ( + BUSINESS_DOMAIN_LABELS, + DOMAIN_FIELD_PREFIXES, + EXPENSE_RISK_CATEGORY_ALIASES, + EXPENSE_RISK_CATEGORY_LABELS, + FIELD_ONTOLOGY, + RISK_LEVEL_LABELS, + RiskRuleField, +) +from app.services.risk_rule_generation_prompt import build_risk_rule_compiler_messages +from app.services.risk_rule_generation_interpreter import COMPOSITE_RULE_TEMPLATE_KEY +from app.services.risk_rule_generation_markdown import build_risk_rule_version_markdown +from app.services.risk_rule_generation_semantics import ( + CITY_CONSISTENCY_SEMANTIC_TYPE, + CITY_CONSISTENCY_SEMANTIC_TYPES, + build_city_consistency_draft, + build_city_consistency_params, +) +from app.services.risk_rule_scoring import apply_risk_score_to_draft, calculate_risk_rule_score from app.services.runtime_chat import RuntimeChatService -@dataclass(frozen=True) -class RiskRuleField: - key: str - label: str - field_type: str - source: str - aliases: tuple[str, ...] - - -BUSINESS_DOMAIN_LABELS: dict[str, str] = { - AgentAssetDomain.EXPENSE.value: "报销", - AgentAssetDomain.AR.value: "应收", - AgentAssetDomain.AP.value: "应付", -} - -RISK_LEVEL_LABELS: dict[str, str] = { - "low": "低风险", - "medium": "中风险", - "high": "高风险", -} - -EXPENSE_RISK_CATEGORY_CODES: tuple[str, ...] = ( - "travel", - "hotel", - "transport", - "meal", - "meeting", - "office", - "training", - "communication", - "welfare", -) -EXPENSE_RISK_CATEGORY_LABELS: dict[str, str] = { - code: EXPENSE_TYPE_LABEL_BY_CODE[code] for code in EXPENSE_RISK_CATEGORY_CODES -} -EXPENSE_RISK_CATEGORY_ALIASES = { - "entertainment": "meal", -} - -FIELD_ONTOLOGY: tuple[RiskRuleField, ...] = ( - RiskRuleField("claim.reason", "报销事由", "text", "claim", ("事由", "说明", "理由", "用途")), - RiskRuleField( - "claim.location", - "申报地点", - "text", - "claim", - ("地点", "城市", "出差地", "申报地点", "申报目的地", "目的地"), - ), - RiskRuleField("claim.amount", "申报金额", "number", "claim", ("金额", "费用", "超额", "额度")), - RiskRuleField("claim.employee_name", "报销人", "text", "claim", ("报销人", "员工", "申请人")), - RiskRuleField("claim.department_name", "部门", "text", "claim", ("部门", "组织")), - RiskRuleField("item.item_type", "费用类型", "enum", "item", ("费用类型", "科目", "类型")), - RiskRuleField("item.item_reason", "明细事由", "text", "item", ("明细事由", "明细说明")), - RiskRuleField("item.item_location", "明细地点", "text", "item", ("明细地点", "发生地点")), - RiskRuleField( - "attachment.invoice_no", "发票号码", "text", "attachment", ("发票号", "发票号码", "票号") - ), - RiskRuleField( - "attachment.buyer_name", "购买方名称", "text", "attachment", ("抬头", "购买方", "开票单位") - ), - RiskRuleField( - "attachment.goods_name", - "商品服务名称", - "text", - "attachment", - ("品名", "商品", "服务名称", "摘要"), - ), - RiskRuleField( - "attachment.issue_date", - "开票日期", - "date", - "attachment", - ("开票日期", "发票日期", "票据日期"), - ), - RiskRuleField( - "attachment.hotel_city", - "住宿城市", - "text", - "attachment", - ("住宿城市", "酒店城市", "酒店地点", "酒店发票城市", "酒店票城市", "住宿发票城市"), - ), - RiskRuleField( - "attachment.route_cities", - "行程城市", - "list", - "attachment", - ("行程", "路线", "途经城市", "出差城市", "交通票行程", "交通票城市"), - ), - RiskRuleField( - "attachment.ocr_text", - "票据全文", - "text", - "attachment", - ("票据内容", "OCR", "全文", "关键字", "关键词"), - ), - RiskRuleField( - "receivable.aging_days", "应收账龄", "number", "receivable", ("账龄", "逾期", "应收逾期") - ), - RiskRuleField( - "receivable.amount_outstanding", - "应收未收金额", - "number", - "receivable", - ("未收金额", "欠款", "应收余额"), - ), - RiskRuleField( - "payable.vendor_name", "供应商名称", "text", "payable", ("供应商", "付款方", "往来单位") - ), - RiskRuleField( - "payable.amount_outstanding", "应付未付金额", "number", "payable", ("未付金额", "应付余额") - ), -) - -DOMAIN_FIELD_PREFIXES: dict[str, tuple[str, ...]] = { - AgentAssetDomain.EXPENSE.value: ("claim.", "item.", "attachment."), - AgentAssetDomain.AR.value: ("receivable.",), - AgentAssetDomain.AP.value: ("payable.",), -} - - class RiskRuleGenerationService: def __init__( self, @@ -172,9 +70,10 @@ class RiskRuleGenerationService: if len(natural_language) < 8: raise ValueError("请至少输入 8 个字的风险规则描述。") - risk_level = str(body.risk_level or "medium").strip().lower() - if risk_level not in RISK_LEVEL_LABELS: - raise ValueError("风险等级仅支持 low、medium、high。") + rule_title = self._clean_text(body.rule_title) + if rule_title and len(rule_title) < 2: + raise ValueError("规则标题至少需要 2 个字。") + requires_attachment = bool(body.requires_attachment) expense_category = self._normalize_expense_category(body.expense_category, domain) expense_category_label = EXPENSE_RISK_CATEGORY_LABELS.get(expense_category or "", "") @@ -186,20 +85,30 @@ class RiskRuleGenerationService: domain=domain, expense_category=expense_category, expense_category_label=expense_category_label, - risk_level=risk_level, fields=fields, ) or self._build_fallback_draft( natural_language=natural_language, domain=domain, expense_category_label=expense_category_label, - risk_level=risk_level, + risk_level="medium", fields=fields, ) draft = self._align_draft_fields( draft, natural_language=natural_language, + risk_level="medium", fields=fields, ) + risk_score = calculate_risk_rule_score( + natural_language=natural_language, + draft=draft, + fields=fields, + expense_category=expense_category, + expense_category_label=expense_category_label, + requires_attachment=requires_attachment, + ) + risk_level = str(risk_score["level"]) + draft = apply_risk_score_to_draft(draft, risk_score) payload = self._build_rule_payload( draft, natural_language=natural_language, @@ -211,6 +120,8 @@ class RiskRuleGenerationService: created_at=created_at, actor=actor, requires_attachment=requires_attachment, + rule_title=rule_title, + risk_score=risk_score, ) rule_code = str(payload["rule_code"]) file_name = f"{rule_code}.json" @@ -236,6 +147,10 @@ class RiskRuleGenerationService: working_version="v0.1.0", config_json={ "severity": risk_level, + "risk_score": risk_score["score"], + "risk_level": risk_level, + "risk_level_label": risk_score["level_label"], + "risk_score_detail": risk_score, "enabled": True, "requires_attachment": requires_attachment, "tag": "风险规则", @@ -260,7 +175,7 @@ class RiskRuleGenerationService: AgentAssetVersion( asset_id=asset.id, version="v0.1.0", - content=self._build_version_markdown(payload), + content=build_risk_rule_version_markdown(payload), content_type="markdown", change_note="通过自然语言新建风险规则草稿。", created_by=actor, @@ -275,6 +190,7 @@ class RiskRuleGenerationService: after_json={ "rule_code": rule_code, "risk_level": risk_level, + "risk_score": risk_score["score"], "domain": domain, "expense_category": expense_category, "requires_attachment": requires_attachment, @@ -291,7 +207,6 @@ class RiskRuleGenerationService: domain: str, expense_category: str | None, expense_category_label: str, - risk_level: str, fields: list[RiskRuleField], ) -> dict[str, Any] | None: field_payload = [ @@ -303,50 +218,17 @@ class RiskRuleGenerationService: } for item in fields ] - messages = [ - { - "role": "system", - "content": ( - "你是 X-Financial 风险规则编译器。只能输出 JSON 对象,不要解释。" - "必须从给定字段本体中选择字段,不允许编造字段。" - "template_key 只能是 field_required_v1、field_compare_v1、keyword_match_v1。" - ), - }, - { - "role": "user", - "content": json.dumps( - { - "business_domain": domain, - "business_domain_label": BUSINESS_DOMAIN_LABELS[domain], - "expense_category": expense_category, - "expense_category_label": expense_category_label, - "risk_level": risk_level, - "risk_level_label": RISK_LEVEL_LABELS[risk_level], - "natural_language": natural_language, - "available_fields": field_payload, - "required_json_shape": { - "name": "规则名称", - "description": "面向业务用户的说明", - "template_key": "field_required_v1", - "field_keys": ["claim.reason"], - "condition_summary": "判断依据", - "keywords": [], - "flow": { - "start": "提交业务单据", - "evidence": "读取字段", - "decision": "判断依据", - "pass": "继续流转", - "fail": "提示风险", - }, - }, - }, - ensure_ascii=False, - ), - }, - ] + messages = build_risk_rule_compiler_messages( + domain=domain, + domain_label=BUSINESS_DOMAIN_LABELS[domain], + expense_category=expense_category, + expense_category_label=expense_category_label, + natural_language=natural_language, + available_fields=field_payload, + ) answer = self.runtime_chat_service.complete( messages, - max_tokens=700, + max_tokens=1400, temperature=0.1, timeout_seconds=12, max_attempts=1, @@ -370,7 +252,12 @@ class RiskRuleGenerationService: ) -> dict[str, Any]: allowed_fields = {item.key for item in fields} template_key = str(payload.get("template_key") or "").strip() - if template_key not in {"field_required_v1", "field_compare_v1", "keyword_match_v1"}: + if template_key not in { + "field_required_v1", + "field_compare_v1", + "keyword_match_v1", + COMPOSITE_RULE_TEMPLATE_KEY, + }: template_key = "field_required_v1" raw_field_keys = payload.get("field_keys") @@ -389,14 +276,37 @@ class RiskRuleGenerationService: ) if str(item or "").strip() ] + exception_keywords = [ + str(item or "").strip() + for item in ( + payload.get("exception_keywords") + if isinstance(payload.get("exception_keywords"), list) + else [] + ) + if str(item or "").strip() + ] + unsupported_fields = [ + str(item or "").strip() + for item in ( + payload.get("unsupported_fields") + if isinstance(payload.get("unsupported_fields"), list) + else [] + ) + if str(item or "").strip() + ] flow = payload.get("flow") if isinstance(payload.get("flow"), dict) else {} - return { + rule_ir = payload.get("rule_ir") if isinstance(payload.get("rule_ir"), dict) else {} + draft = { "name": self._clean_text(payload.get("name"))[:80], "description": self._clean_text(payload.get("description")), "template_key": template_key, + "semantic_type": self._clean_text(payload.get("semantic_type")), "field_keys": field_keys, "condition_summary": self._clean_text(payload.get("condition_summary")), "keywords": keywords[:12], + "exception_keywords": exception_keywords[:12], + "unsupported_fields": unsupported_fields[:20], + "rule_ir": rule_ir, "flow": { "start": self._clean_text(flow.get("start")) or "提交业务单据", "evidence": self._clean_text(flow.get("evidence")) or "读取规则字段", @@ -405,6 +315,18 @@ class RiskRuleGenerationService: "fail": self._clean_text(flow.get("fail")) or "提示风险并进入复核", }, } + for key in ("conditions", "hit_logic", "field_groups"): + value = payload.get(key) + if isinstance(value, (list, dict)): + draft[key] = value + scoring_evidence = payload.get("risk_scoring_evidence") + if isinstance(scoring_evidence, dict): + draft["risk_scoring_evidence"] = scoring_evidence + for key in ("formula", "message_template"): + value = self._clean_text(payload.get(key)) + if value: + draft[key] = value + return draft def _build_fallback_draft( self, @@ -457,6 +379,8 @@ class RiskRuleGenerationService: created_at: datetime, actor: str, requires_attachment: bool, + rule_title: str = "", + risk_score: dict[str, Any] | None = None, ) -> dict[str, Any]: created_stamp = created_at.strftime("%Y%m%d%H%M%S%f") domain_slug = {"expense": "expense", "ar": "ar", "ap": "ap"}[domain] @@ -472,6 +396,11 @@ class RiskRuleGenerationService: self._clean_text(draft.get("condition_summary")) or "判断是否符合自然语言规则描述" ) risk_category = expense_category_label or BUSINESS_DOMAIN_LABELS[domain] + risk_score_payload = dict(risk_score or {}) + risk_score_value = int(risk_score_payload.get("score") or 0) + risk_level_label = str( + risk_score_payload.get("level_label") or RISK_LEVEL_LABELS.get(risk_level, "风险") + ) keywords = list(draft.get("keywords") or []) field_by_key = {item.key: item for item in fields} params: dict[str, Any] = { @@ -480,9 +409,23 @@ class RiskRuleGenerationService: "condition_summary": condition_summary, "natural_language": natural_language, } + semantic_type = str(draft.get("semantic_type") or "").strip() + if semantic_type: + params["semantic_type"] = semantic_type + if template_key == COMPOSITE_RULE_TEMPLATE_KEY and isinstance(draft.get("rule_ir"), dict): + params["rule_ir"] = draft["rule_ir"] + for key in ("conditions", "hit_logic", "field_groups", "formula", "message_template"): + if key in draft: + params[key] = draft[key] + for key in ("keywords", "exception_keywords", "unsupported_fields"): + values = draft.get(key) + if isinstance(values, list): + params[key] = values + if draft.get("semantic_type") == CITY_CONSISTENCY_SEMANTIC_TYPE: + params.update(build_city_consistency_params(draft)) if template_key == "field_required_v1": params["required_fields"] = field_keys - if template_key == "field_compare_v1": + if template_key == "field_compare_v1" and "conditions" not in params: params["conditions"] = self._build_compare_conditions(field_keys) if template_key == "keyword_match_v1": params["keywords"] = keywords @@ -494,7 +437,9 @@ class RiskRuleGenerationService: payload = { "schema_version": "2.0", "rule_code": rule_code, - "name": self._clean_text(draft.get("name")) or self._infer_rule_name(natural_language), + "name": rule_title + or self._clean_text(draft.get("name")) + or self._infer_rule_name(natural_language), "description": self._clean_text(draft.get("description")) or natural_language, "enabled": True, "requires_attachment": requires_attachment, @@ -503,6 +448,7 @@ class RiskRuleGenerationService: "ontology_signal": "natural_language_risk", "evaluator": "template_rule", "template_key": template_key, + "semantic_type": str(draft.get("semantic_type") or "").strip() or None, "applies_to": applies_to, "inputs": { "fields": [ @@ -521,6 +467,7 @@ class RiskRuleGenerationService: "fail": { "severity": risk_level, "action": "manual_review", + "risk_score": risk_score_value, }, }, "metadata": { @@ -530,11 +477,18 @@ class RiskRuleGenerationService: "created_at": created_at.isoformat(), "created_by": actor, "requires_attachment": requires_attachment, + "risk_score": risk_score_value, + "risk_level": risk_level, + "risk_level_label": risk_level_label, + "risk_score_model": risk_score_payload.get("model"), + "risk_score_detail": risk_score_payload, + "rule_title": rule_title, "expense_category": expense_category, "expense_category_label": expense_category_label, "natural_language": natural_language, "business_explanation": self._clean_text(draft.get("description")), "condition_summary": condition_summary, + "rule_ir": draft.get("rule_ir") if isinstance(draft.get("rule_ir"), dict) else {}, "flow": draft.get("flow") if isinstance(draft.get("flow"), dict) else {}, }, } @@ -559,15 +513,17 @@ class RiskRuleGenerationService: metadata = payload.get("metadata") if isinstance(payload.get("metadata"), dict) else {} flow = metadata.get("flow") if isinstance(metadata.get("flow"), dict) else {} condition_summary = self._clean_text(metadata.get("condition_summary")) + diagram_fields = [ + RiskRuleFlowDiagramField(key=field.key, label=field.label) for field in fields + ] + details = build_risk_rule_flow_diagram_details(payload, diagram_fields) return self.flow_diagram_renderer.render( RiskRuleFlowDiagramSpec( title=self._clean_text(payload.get("name")) or "风险规则判断流程", domain_label=domain_label or BUSINESS_DOMAIN_LABELS.get(domain, "业务"), severity=risk_level, severity_label=RISK_LEVEL_LABELS.get(risk_level, "中风险"), - fields=tuple( - RiskRuleFlowDiagramField(key=field.key, label=field.label) for field in fields - ), + fields=tuple(diagram_fields), start=self._clean_text(flow.get("start")) or "业务单据提交", evidence=self._clean_text(flow.get("evidence")) or "读取规则字段", decision=self._clean_text(flow.get("decision")) @@ -581,6 +537,9 @@ class RiskRuleGenerationService: pass_text=self._clean_text(flow.get("pass")) or "未命中风险,继续流转", fail_text=self._clean_text(flow.get("fail")) or f"命中{RISK_LEVEL_LABELS.get(risk_level, '风险')},进入人工复核", + fact_lines=details["fact_lines"], + condition_lines=details["condition_lines"], + hit_logic=str(details["hit_logic"] or ""), ) ) @@ -615,7 +574,19 @@ class RiskRuleGenerationService: (10, field) for field in candidates if field.key - in {"claim.location", "attachment.hotel_city", "attachment.route_cities"} + in { + "claim.reason", + "claim.location", + "employee.location", + "item.item_date", + "item.item_reason", + "item.item_location", + "attachment.hotel_city", + "attachment.route_cities", + "attachment.issue_date", + "attachment.stay_start_date", + "attachment.stay_end_date", + } ) if any(keyword in text for keyword in ("发票", "票据", "品名", "抬头", "开票")): matched.extend( @@ -639,7 +610,7 @@ class RiskRuleGenerationService: seen.add(field.key) deduped.append(field) if deduped: - return deduped[:8] + return deduped[:10] return candidates[:4] @staticmethod @@ -657,6 +628,14 @@ class RiskRuleGenerationService: term in text for term in ("行程", "交通票", "路线", "途经") ): score += 10 + if field.key in { + "claim.trip_start_date", + "claim.trip_end_date", + "item.item_date", + "attachment.stay_start_date", + "attachment.stay_end_date", + } and any(term in text for term in ("日期", "时间", "出差开始", "出差结束", "入住", "离店")): + score += 10 if field.key == "claim.location" and any( term in text for term in ("申报目的地", "申报地点", "目的地", "出差地") ): @@ -670,14 +649,26 @@ class RiskRuleGenerationService: draft: dict[str, Any], *, natural_language: str, + risk_level: str, fields: list[RiskRuleField], ) -> dict[str, Any]: + if str(draft.get("semantic_type") or "").strip() in CITY_CONSISTENCY_SEMANTIC_TYPES: + return build_city_consistency_draft( + draft, + natural_language=natural_language, + fields=fields, + risk_level=risk_level, + ) + field_by_key = {field.key: field for field in fields} original_keys = [ str(item or "").strip() for item in list(draft.get("field_keys") or []) if str(item or "").strip() in field_by_key ] + if draft.get("template_key") == COMPOSITE_RULE_TEMPLATE_KEY: + return {**draft, "field_keys": original_keys or [field.key for field in fields[:8]]} + preferred_keys: list[str] = [] def add_preferred(key: str, *terms: str) -> None: @@ -783,40 +774,3 @@ class RiskRuleGenerationService: if start < 0 or end <= start: raise ValueError("JSON object not found.") return normalized[start : end + 1] - - @staticmethod - def _build_version_markdown(payload: dict[str, Any]) -> str: - metadata = payload.get("metadata") if isinstance(payload.get("metadata"), dict) else {} - fields = ( - payload.get("inputs", {}).get("fields") - if isinstance(payload.get("inputs"), dict) - else [] - ) - field_labels = [ - str(item.get("label") or item.get("key") or "").strip() - for item in fields - if isinstance(item, dict) and str(item.get("label") or item.get("key") or "").strip() - ] - return "\n".join( - [ - f"# {payload.get('name')}", - "", - "## 业务说明", - "", - str(payload.get("description") or ""), - "", - "## 自然语言原文", - "", - str(metadata.get("natural_language") or ""), - "", - "## 使用字段", - "", - "、".join(field_labels) or "未识别字段", - "", - "## 运行时 JSON", - "", - "```json", - json.dumps(payload, ensure_ascii=False, indent=2), - "```", - ] - ) diff --git a/server/src/app/services/risk_rule_generation_interpreter.py b/server/src/app/services/risk_rule_generation_interpreter.py new file mode 100644 index 0000000..5b2f89d --- /dev/null +++ b/server/src/app/services/risk_rule_generation_interpreter.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +COMPOSITE_RULE_TEMPLATE_KEY = "composite_rule_v1" + +COMPOSITE_RULE_OPERATORS = { + "exists_any", + "exists_all", + "all_present", + "in_scope", + "not_in_scope", + "not_in_set", + "overlap", + "not_overlap", + "date_outside_range", + "contains_any", + "not_contains_any", +} diff --git a/server/src/app/services/risk_rule_generation_jobs.py b/server/src/app/services/risk_rule_generation_jobs.py new file mode 100644 index 0000000..88de06a --- /dev/null +++ b/server/src/app/services/risk_rule_generation_jobs.py @@ -0,0 +1,336 @@ +from __future__ import annotations + +from datetime import UTC, datetime +from typing import Any + +from sqlalchemy.orm import Session + +from app.core.agent_enums import AgentAssetStatus, AgentAssetType +from app.models.agent_asset import AgentAsset, AgentAssetVersion +from app.schemas.agent_asset import AgentAssetRiskRuleGenerateRequest +from app.services.agent_asset_spreadsheet import RISK_RULES_LIBRARY +from app.services.audit import AuditLogService +from app.services.risk_rule_generation import ( + BUSINESS_DOMAIN_LABELS, + EXPENSE_RISK_CATEGORY_LABELS, + RiskRuleGenerationService, +) +from app.services.risk_rule_generation_markdown import build_risk_rule_version_markdown +from app.services.risk_rule_scoring import apply_risk_score_to_draft, calculate_risk_rule_score +from app.services.runtime_chat import RuntimeChatService + + +class RiskRuleGenerationJobService: + """把自然语言风险规则生成拆成可追踪的后台任务。""" + + def __init__( + self, + db: Session, + *, + rule_library_manager: Any | None = None, + runtime_chat_service: RuntimeChatService | None = None, + ) -> None: + self.db = db + self.generator = RiskRuleGenerationService( + db, + rule_library_manager=rule_library_manager, + runtime_chat_service=runtime_chat_service, + ) + self.audit_service = AuditLogService(db) + + def enqueue_rule_asset_generation( + self, + body: AgentAssetRiskRuleGenerateRequest, + *, + actor: str, + request_id: str | None = None, + ) -> str: + domain = self._validate_domain(body) + natural_language = self._validate_natural_language(body) + rule_title = self._validate_rule_title(body) + requires_attachment = bool(body.requires_attachment) + expense_category = self.generator._normalize_expense_category(body.expense_category, domain) + expense_category_label = EXPENSE_RISK_CATEGORY_LABELS.get(expense_category or "", "") + + created_at = datetime.now(UTC) + rule_code = self._build_placeholder_rule_code( + domain=domain, + expense_category=expense_category, + created_at=created_at, + ) + category_label = expense_category_label or BUSINESS_DOMAIN_LABELS[domain] + display_name = rule_title or self.generator._infer_rule_name(natural_language) + file_name = f"{rule_code}.json" + + asset = AgentAsset( + asset_type=AgentAssetType.RULE.value, + code=rule_code, + name=display_name, + description=natural_language, + domain=domain, + scenario_json=[category_label], + owner=actor, + reviewer=None, + status=AgentAssetStatus.GENERATING.value, + current_version=None, + published_version=None, + working_version=None, + config_json={ + "severity": "medium", + "risk_score_status": "pending", + "enabled": True, + "requires_attachment": requires_attachment, + "tag": "风险规则", + "detail_mode": "json_risk", + "expense_category": expense_category, + "expense_category_label": expense_category_label, + "risk_category": category_label, + "rule_library": RISK_RULES_LIBRARY, + "rule_document": { + "file_name": file_name, + "storage_key": f"rules/{RISK_RULES_LIBRARY}/{file_name}", + }, + "generated_by": "natural_language", + "generation_status": AgentAssetStatus.GENERATING.value, + "generation_started_at": created_at.isoformat(), + "generation_request": self._dump_generation_request(body), + }, + ) + self.db.add(asset) + self.db.flush() + self.audit_service.log_action( + actor=actor, + action="enqueue_agent_asset_risk_rule_generation", + resource_type=AgentAssetType.RULE.value, + resource_id=asset.id, + before_json=None, + after_json={ + "rule_code": rule_code, + "domain": domain, + "expense_category": expense_category, + }, + request_id=request_id, + ) + self.db.refresh(asset) + return asset.id + + def complete_rule_asset_generation( + self, + asset_id: str, + body: AgentAssetRiskRuleGenerateRequest, + *, + actor: str, + request_id: str | None = None, + ) -> None: + try: + asset = self.db.get(AgentAsset, asset_id) + if asset is None or asset.status != AgentAssetStatus.GENERATING.value: + return + self._complete_rule_asset(asset, body, actor=actor, request_id=request_id) + except Exception as exc: # noqa: BLE001 - 后台任务必须把失败写回资产状态 + self.mark_generation_failed( + asset_id, + error_message=str(exc) or exc.__class__.__name__, + actor=actor, + request_id=request_id, + ) + + def mark_generation_failed( + self, + asset_id: str, + *, + error_message: str, + actor: str, + request_id: str | None = None, + ) -> None: + asset = self.db.get(AgentAsset, asset_id) + if asset is None: + return + + config_json = dict(asset.config_json or {}) + config_json.update( + { + "generation_status": AgentAssetStatus.FAILED.value, + "generation_error": error_message[:1000], + "generation_failed_at": datetime.now(UTC).isoformat(), + } + ) + asset.status = AgentAssetStatus.FAILED.value + asset.config_json = config_json + self.db.add(asset) + self.db.flush() + self.audit_service.log_action( + actor=actor, + action="fail_agent_asset_risk_rule_generation", + resource_type=AgentAssetType.RULE.value, + resource_id=asset.id, + before_json=None, + after_json={"generation_error": error_message[:1000]}, + request_id=request_id, + ) + + def _complete_rule_asset( + self, + asset: AgentAsset, + body: AgentAssetRiskRuleGenerateRequest, + *, + actor: str, + request_id: str | None, + ) -> None: + domain = self._validate_domain(body) + natural_language = self._validate_natural_language(body) + rule_title = self._validate_rule_title(body) + requires_attachment = bool(body.requires_attachment) + expense_category = self.generator._normalize_expense_category(body.expense_category, domain) + expense_category_label = EXPENSE_RISK_CATEGORY_LABELS.get(expense_category or "", "") + created_at = asset.created_at or datetime.now(UTC) + fields = self.generator._resolve_fields(natural_language, domain=domain) + + draft = self.generator._compile_with_model( + natural_language=natural_language, + domain=domain, + expense_category=expense_category, + expense_category_label=expense_category_label, + fields=fields, + ) or self.generator._build_fallback_draft( + natural_language=natural_language, + domain=domain, + expense_category_label=expense_category_label, + risk_level="medium", + fields=fields, + ) + draft = self.generator._align_draft_fields( + draft, + natural_language=natural_language, + risk_level="medium", + fields=fields, + ) + risk_score = calculate_risk_rule_score( + natural_language=natural_language, + draft=draft, + fields=fields, + expense_category=expense_category, + expense_category_label=expense_category_label, + requires_attachment=requires_attachment, + ) + risk_level = str(risk_score["level"]) + draft = apply_risk_score_to_draft(draft, risk_score) + payload = self.generator._build_rule_payload( + draft, + natural_language=natural_language, + domain=domain, + expense_category=expense_category, + expense_category_label=expense_category_label, + risk_level=risk_level, + fields=fields, + created_at=created_at, + actor=actor, + requires_attachment=requires_attachment, + rule_title=rule_title, + risk_score=risk_score, + ) + rule_code = str(payload["rule_code"]) + file_name = f"{rule_code}.json" + self.generator.rule_library_manager.write_rule_library_json( + library=RISK_RULES_LIBRARY, + file_name=file_name, + payload=payload, + ) + + config_json = { + "severity": risk_level, + "risk_score": risk_score["score"], + "risk_level": risk_level, + "risk_level_label": risk_score["level_label"], + "risk_score_detail": risk_score, + "enabled": True, + "requires_attachment": requires_attachment, + "tag": "风险规则", + "detail_mode": "json_risk", + "expense_category": expense_category, + "expense_category_label": expense_category_label, + "risk_category": payload.get("risk_category"), + "rule_library": RISK_RULES_LIBRARY, + "rule_document": { + "file_name": file_name, + "storage_key": f"rules/{RISK_RULES_LIBRARY}/{file_name}", + }, + "ontology_signal": payload.get("ontology_signal"), + "evaluator": payload.get("evaluator"), + "generated_by": "natural_language", + "source_ref": "自然语言风险规则", + "generation_status": "completed", + "generation_completed_at": datetime.now(UTC).isoformat(), + } + + asset.code = rule_code + asset.name = str(payload["name"]) + asset.description = str(payload["description"]) + asset.domain = domain + asset.scenario_json = [str(payload.get("risk_category") or BUSINESS_DOMAIN_LABELS[domain])] + asset.status = AgentAssetStatus.DRAFT.value + asset.current_version = "v0.1.0" + asset.published_version = None + asset.working_version = "v0.1.0" + asset.config_json = config_json + self.db.add(asset) + self.db.add( + AgentAssetVersion( + asset_id=asset.id, + version="v0.1.0", + content=build_risk_rule_version_markdown(payload), + content_type="markdown", + change_note="通过自然语言新建风险规则草稿。", + created_by=actor, + ) + ) + self.db.flush() + self.audit_service.log_action( + actor=actor, + action="complete_agent_asset_risk_rule_generation", + resource_type=AgentAssetType.RULE.value, + resource_id=asset.id, + before_json=None, + after_json={ + "rule_code": rule_code, + "risk_level": risk_level, + "risk_score": risk_score["score"], + "domain": domain, + "expense_category": expense_category, + "requires_attachment": requires_attachment, + }, + request_id=request_id, + ) + + def _validate_domain(self, body: AgentAssetRiskRuleGenerateRequest) -> str: + domain = body.business_domain.value + if domain not in BUSINESS_DOMAIN_LABELS: + raise ValueError("当前仅支持报销、应收、应付业务域的新建风险规则。") + return domain + + def _validate_natural_language(self, body: AgentAssetRiskRuleGenerateRequest) -> str: + natural_language = self.generator._clean_text(body.natural_language) + if len(natural_language) < 8: + raise ValueError("请至少输入 8 个字的风险规则描述。") + return natural_language + + def _validate_rule_title(self, body: AgentAssetRiskRuleGenerateRequest) -> str: + rule_title = self.generator._clean_text(body.rule_title) + if rule_title and len(rule_title) < 2: + raise ValueError("规则标题至少需要 2 个字。") + return rule_title + + @staticmethod + def _build_placeholder_rule_code( + *, + domain: str, + expense_category: str | None, + created_at: datetime, + ) -> str: + domain_slug = {"expense": "expense", "ar": "ar", "ap": "ap"}[domain] + category_slug = f".{expense_category}" if expense_category else "" + return f"risk.{domain_slug}{category_slug}.generating_{created_at.strftime('%Y%m%d%H%M%S%f')}" + + @staticmethod + def _dump_generation_request(body: AgentAssetRiskRuleGenerateRequest) -> dict[str, Any]: + return body.model_dump(mode="json") diff --git a/server/src/app/services/risk_rule_generation_markdown.py b/server/src/app/services/risk_rule_generation_markdown.py new file mode 100644 index 0000000..b0ef7db --- /dev/null +++ b/server/src/app/services/risk_rule_generation_markdown.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +import json +from typing import Any + + +def build_risk_rule_version_markdown(payload: dict[str, Any]) -> str: + metadata = payload.get("metadata") if isinstance(payload.get("metadata"), dict) else {} + fields = ( + payload.get("inputs", {}).get("fields") if isinstance(payload.get("inputs"), dict) else [] + ) + field_labels = [ + str(item.get("label") or item.get("key") or "").strip() + for item in fields + if isinstance(item, dict) and str(item.get("label") or item.get("key") or "").strip() + ] + return "\n".join( + [ + f"# {payload.get('name')}", + "", + "## 业务说明", + "", + str(payload.get("description") or ""), + "", + "## 自然语言原文", + "", + str(metadata.get("natural_language") or ""), + "", + "## 使用字段", + "", + "、".join(field_labels) or "未识别字段", + "", + "## 运行时 JSON", + "", + "```json", + json.dumps(payload, ensure_ascii=False, indent=2), + "```", + ] + ) diff --git a/server/src/app/services/risk_rule_generation_ontology.py b/server/src/app/services/risk_rule_generation_ontology.py new file mode 100644 index 0000000..992cd41 --- /dev/null +++ b/server/src/app/services/risk_rule_generation_ontology.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from app.core.agent_enums import AgentAssetDomain +from app.services.expense_type_keywords import EXPENSE_TYPE_LABEL_BY_CODE + + +@dataclass(frozen=True) +class RiskRuleField: + key: str + label: str + field_type: str + source: str + aliases: tuple[str, ...] + + +BUSINESS_DOMAIN_LABELS: dict[str, str] = { + AgentAssetDomain.EXPENSE.value: "报销", + AgentAssetDomain.AR.value: "应收", + AgentAssetDomain.AP.value: "应付", +} + +RISK_LEVEL_LABELS: dict[str, str] = { + "low": "低风险", + "medium": "中风险", + "high": "高风险", + "critical": "极高风险", +} + +EXPENSE_RISK_CATEGORY_CODES: tuple[str, ...] = ( + "travel", + "hotel", + "transport", + "meal", + "meeting", + "office", + "training", + "communication", + "welfare", +) +EXPENSE_RISK_CATEGORY_LABELS: dict[str, str] = { + code: EXPENSE_TYPE_LABEL_BY_CODE[code] for code in EXPENSE_RISK_CATEGORY_CODES +} +EXPENSE_RISK_CATEGORY_ALIASES = { + "entertainment": "meal", +} + +FIELD_ONTOLOGY: tuple[RiskRuleField, ...] = ( + RiskRuleField("claim.reason", "报销事由", "text", "claim", ("事由", "说明", "理由", "用途")), + RiskRuleField( + "claim.location", + "申报地点", + "text", + "claim", + ("地点", "城市", "出差地", "申报地点", "申报目的地", "目的地"), + ), + RiskRuleField( + "claim.trip_start_date", + "出差开始日期", + "date", + "claim", + ("出差开始", "行程开始", "开始日期", "出差起始", "出发日期"), + ), + RiskRuleField( + "claim.trip_end_date", + "出差结束日期", + "date", + "claim", + ("出差结束", "行程结束", "结束日期", "返程日期", "返回日期"), + ), + RiskRuleField("claim.amount", "申报金额", "number", "claim", ("金额", "费用", "超额", "额度")), + RiskRuleField("claim.employee_name", "报销人", "text", "claim", ("报销人", "员工", "申请人")), + RiskRuleField("claim.department_name", "部门", "text", "claim", ("部门", "组织")), + RiskRuleField( + "employee.location", + "员工常驻地", + "text", + "employee", + ("常驻地", "办公地", "员工所在地", "出发地", "所在城市"), + ), + RiskRuleField("item.item_type", "费用类型", "enum", "item", ("费用类型", "科目", "类型")), + RiskRuleField("item.item_reason", "明细事由", "text", "item", ("明细事由", "明细说明")), + RiskRuleField("item.item_location", "明细地点", "text", "item", ("明细地点", "发生地点")), + RiskRuleField("item.item_date", "明细发生日期", "date", "item", ("明细日期", "发生日期", "费用日期")), + RiskRuleField( + "attachment.invoice_no", "发票号码", "text", "attachment", ("发票号", "发票号码", "票号") + ), + RiskRuleField( + "attachment.buyer_name", "购买方名称", "text", "attachment", ("抬头", "购买方", "开票单位") + ), + RiskRuleField( + "attachment.goods_name", + "商品服务名称", + "text", + "attachment", + ("品名", "商品", "服务名称", "摘要"), + ), + RiskRuleField( + "attachment.issue_date", + "开票日期", + "date", + "attachment", + ("开票日期", "发票日期", "票据日期"), + ), + RiskRuleField( + "attachment.stay_start_date", + "住宿开始日期", + "date", + "attachment", + ("入住日期", "住宿开始", "入住时间", "住宿开始日期"), + ), + RiskRuleField( + "attachment.stay_end_date", + "住宿结束日期", + "date", + "attachment", + ("离店日期", "退房日期", "住宿结束", "住宿结束日期"), + ), + RiskRuleField( + "attachment.hotel_city", + "住宿城市", + "text", + "attachment", + ("住宿城市", "酒店城市", "酒店地点", "酒店发票城市", "酒店票城市", "住宿发票城市"), + ), + RiskRuleField( + "attachment.route_cities", + "行程城市", + "list", + "attachment", + ("行程", "路线", "途经城市", "出差城市", "交通票行程", "交通票城市"), + ), + RiskRuleField( + "attachment.ocr_text", + "票据全文", + "text", + "attachment", + ("票据内容", "OCR", "全文", "关键字", "关键词"), + ), + RiskRuleField( + "receivable.aging_days", "应收账龄", "number", "receivable", ("账龄", "逾期", "应收逾期") + ), + RiskRuleField( + "receivable.amount_outstanding", + "应收未收金额", + "number", + "receivable", + ("未收金额", "欠款", "应收余额"), + ), + RiskRuleField( + "payable.vendor_name", "供应商名称", "text", "payable", ("供应商", "付款方", "往来单位") + ), + RiskRuleField( + "payable.amount_outstanding", "应付未付金额", "number", "payable", ("未付金额", "应付余额") + ), +) + +DOMAIN_FIELD_PREFIXES: dict[str, tuple[str, ...]] = { + AgentAssetDomain.EXPENSE.value: ("claim.", "item.", "attachment.", "employee."), + AgentAssetDomain.AR.value: ("receivable.",), + AgentAssetDomain.AP.value: ("payable.",), +} diff --git a/server/src/app/services/risk_rule_generation_prompt.py b/server/src/app/services/risk_rule_generation_prompt.py new file mode 100644 index 0000000..37147e7 --- /dev/null +++ b/server/src/app/services/risk_rule_generation_prompt.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +import json +from typing import Any + + +def build_risk_rule_compiler_messages( + *, + domain: str, + domain_label: str, + expense_category: str | None, + expense_category_label: str, + natural_language: str, + available_fields: list[dict[str, Any]], +) -> list[dict[str, str]]: + """构造自然语言规则编译提示词。 + + 大模型只负责把业务语言拆成“语义计划”,后端会校验字段、操作符和模板。 + """ + + schema = { + "name": "规则名称,短句", + "description": "面向业务和审核人员的说明,不要写实现细节", + "template_key": "field_required_v1 | field_compare_v1 | keyword_match_v1 | composite_rule_v1", + "semantic_type": ( + "可选。可用稳定英文短语描述语义类型;" + "已知差旅票据城市/路线一致性可使用 travel_route_city_consistency,其他规则按业务含义命名" + ), + "field_keys": ["只能选择 available_fields.key"], + "condition_summary": "用公式化语言描述判断依据,不要写'是否出现风险关键词'", + "rule_ir": { + "facts": "事实变量数组,例如 A=票据事实、B=业务申报事实、E=例外说明", + "conditions": "条件数组,必须能被人解释", + "hit_logic": "命中逻辑,例如 D AND ((A NOT_IN B) OR DATE_OUTSIDE(T,R)) AND NOT EXCEPTION(E)", + }, + "conditions": [ + { + "id": "稳定英文标识", + "operator": ( + "exists_any | exists_all | in_scope | not_in_scope | overlap | " + "not_overlap | date_outside_range | contains_any | not_contains_any" + ), + "fields": ["exists/contains 类操作使用"], + "left_fields": ["集合比较左侧字段"], + "right_fields": ["集合比较右侧字段"], + "date_fields": ["日期字段"], + "range_start_fields": ["日期范围开始字段"], + "range_end_fields": ["日期范围结束字段"], + "keywords": ["例外或风险词"], + } + ], + "hit_logic": {"all": ["condition_id", {"any": ["condition_id"]}]}, + "formula": "可执行逻辑公式,字段使用事实变量表达", + "message_template": "命中后的业务提示", + "unsupported_fields": ["用户规则提到但 available_fields 中暂时没有的字段"], + "keywords": "仅 keyword_match_v1 使用,且必须是真正风险词,不得把例外说明词当风险词", + "exception_keywords": "例外说明词,例如绕行、跨城办事、临时改签", + "risk_scoring_evidence": { + "impact_level": "low | medium | high | critical", + "violation_certainty": "low | medium | high | critical", + "evidence_strength": "low | medium | high | critical", + "exception_dependence": "low | medium | high | critical", + "control_action": "remind | supplement | manual_review | return | block", + "business_sensitivity": "low | medium | high | critical", + "reason": "用一句话说明这些评分证据来自哪些业务语义", + }, + "flow": { + "start": "流程起点", + "evidence": "读取哪些事实", + "decision": "判断公式或分支条件", + "pass": "未命中时说明", + "fail": "命中时说明", + }, + } + guardrails = [ + "只能输出 JSON 对象,不能输出 Markdown 或解释。", + "字段必须来自 available_fields,不能编造字段。", + "多步骤规则要使用 composite_rule_v1:先抽取事实变量,再写 conditions 和 hit_logic,不要压扁成单个关键词判断。", + "城市/地点/路线一致性必须用 field_compare_v1 或 semantic_type=travel_route_city_consistency。", + "涉及多个字段、日期范围、金额范围、集合关系、例外说明的规则必须使用 composite_rule_v1。", + "日期字段必须区分事实日期、票据日期和业务期间;如果只能拿到替代字段,要在 rule_ir 中说明这是 fallback evidence。", + "composite_rule_v1 只能使用受控 operator:exists_any、exists_all、in_scope、not_in_scope、overlap、not_overlap、date_outside_range、contains_any、not_contains_any。", + "差旅路线规则中,交通票行程城市和住宿发票城市属于附件城市集合。", + "申报目的地和明细发生地点属于申报行程城市集合。", + "员工常驻地/出发地如可用,属于合理起终点集合,不等同于申报目的地。", + "绕行、跨城办事、临时改签是例外说明证据,不是风险命中关键词。", + "如果票据路线出现申报目的地和常驻地之外的额外城市,应描述为中途周转/绕行异常。", + "keyword_match_v1 只用于品名、摘要、票据全文中出现明确风险词的规则。", + "不要直接指定 risk_level 或 risk_score;只输出 risk_scoring_evidence,后端会按固定评分模型计算 0-100 分和风险等级。", + "评分证据必须围绕六个指标:业务影响、违规确定性、证据强度、例外/规避空间、处置强度、场景敏感度。", + ] + examples = [ + { + "user_rule": ( + "差旅报销时,交通票或住宿票据中的城市均无法与申报目的地、" + "明细地点形成一致关系,且事由未说明绕行或改签原因,则高风险。" + ), + "expected": { + "template_key": "field_compare_v1", + "semantic_type": "travel_route_city_consistency", + "field_keys": [ + "attachment.route_cities", + "attachment.hotel_city", + "claim.location", + "item.item_location", + "employee.location", + "claim.reason", + "item.item_reason", + ], + "condition_summary": ( + "A=交通票行程城市∪住宿发票城市,B=申报目的地∪明细发生地点," + "C=员工常驻地/合理起终点;A与B无交集且无合理说明,或A中出现B∪C之外城市时命中。" + ), + "keywords": [], + "exception_keywords": ["绕行", "跨城办事", "临时改签"], + }, + } + ] + return [ + { + "role": "system", + "content": "\n".join( + [ + "你是 X-Financial 风险规则语义编译器。", + "你的任务是把自然语言规则转换成可校验 JSON 语义计划。", + "后端执行器只接受受控模板和受控字段,所以你必须严格遵守以下约束:", + *[f"- {item}" for item in guardrails], + ] + ), + }, + { + "role": "user", + "content": json.dumps( + { + "business_domain": domain, + "business_domain_label": domain_label, + "expense_category": expense_category, + "expense_category_label": expense_category_label, + "natural_language": natural_language, + "available_fields": available_fields, + "required_json_shape": schema, + "examples": examples, + }, + ensure_ascii=False, + ), + }, + ] diff --git a/server/src/app/services/risk_rule_generation_semantics.py b/server/src/app/services/risk_rule_generation_semantics.py new file mode 100644 index 0000000..981dd71 --- /dev/null +++ b/server/src/app/services/risk_rule_generation_semantics.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from typing import Any + +TRAVEL_ROUTE_CITY_SEMANTIC_TYPE = "travel_route_city_consistency" +LEGACY_CITY_CONSISTENCY_SEMANTIC_TYPE = "travel_city_consistency" +CITY_CONSISTENCY_SEMANTIC_TYPES = { + TRAVEL_ROUTE_CITY_SEMANTIC_TYPE, + LEGACY_CITY_CONSISTENCY_SEMANTIC_TYPE, +} +CITY_CONSISTENCY_SEMANTIC_TYPE = TRAVEL_ROUTE_CITY_SEMANTIC_TYPE + +RISK_LEVEL_LABELS = { + "low": "低风险", + "medium": "中风险", + "high": "高风险", + "critical": "极高风险", +} + +CITY_ATTACHMENT_FIELDS = ("attachment.route_cities", "attachment.hotel_city") +CITY_REFERENCE_FIELDS = ("claim.location", "item.item_location") +CITY_HOME_FIELDS = ("employee.location",) +CITY_EXCEPTION_FIELDS = ("claim.reason", "item.item_reason") +CITY_EXCEPTION_KEYWORDS = ("绕行", "跨城办事", "跨城", "临时改签", "改签", "变更") + + +def is_city_consistency_rule(text: str) -> bool: + normalized = str(text or "") + has_city_subject = any( + term in normalized + for term in ("交通票", "住宿票", "住宿发票", "票据", "附件", "行程城市", "住宿城市") + ) + has_reference = any( + term in normalized + for term in ("申报目的地", "申报地点", "明细地点", "发生地点", "意图城市", "目的地") + ) + has_relation = any( + term in normalized + for term in ("一致", "不一致", "形成一致关系", "匹配", "无法与", "对应") + ) + has_route_anomaly = any(term in normalized for term in ("绕行", "跨城", "中转", "周转", "改签")) + return has_city_subject and has_reference and (has_relation or has_route_anomaly) + + +def build_city_consistency_draft( + draft: dict[str, Any], + *, + natural_language: str, + fields: list[Any], + risk_level: str, +) -> dict[str, Any]: + del natural_language + field_by_key = {field.key: field for field in fields} + field_keys = [ + key + for key in ( + *CITY_ATTACHMENT_FIELDS, + *CITY_REFERENCE_FIELDS, + *CITY_HOME_FIELDS, + *CITY_EXCEPTION_FIELDS, + ) + if key in field_by_key + ] + risk_label = RISK_LEVEL_LABELS.get(risk_level, "风险") + condition_summary = ( + "判断公式:A=交通票行程城市∪住宿发票城市,B=申报目的地∪明细发生地点," + "C=员工常驻地/合理出发地。若A或B为空则要求补充识别;若A与B无交集且无合理说明," + "或票据路线中存在不属于B∪C的额外城市,则命中目的地不一致/中途周转异常风险。" + ) + flow = draft.get("flow") if isinstance(draft.get("flow"), dict) else {} + return { + **draft, + "template_key": "field_compare_v1", + "field_keys": field_keys, + "semantic_type": TRAVEL_ROUTE_CITY_SEMANTIC_TYPE, + "condition_summary": condition_summary, + "keywords": [], + "exception_keywords": list(CITY_EXCEPTION_KEYWORDS), + "flow": { + **flow, + "start": "差旅报销单据提交,并上传交通票据、住宿票据或其他可识别城市的附件", + "evidence": "读取员工常驻地、申报目的地、明细发生地点、交通票行程城市、住宿发票城市和报销事由", + "decision": "附件城市是否覆盖申报行程,且票据路线是否出现申报目的地和常驻地之外的中转城市", + "pass": "票据城市覆盖申报行程,且未出现申报目的地和常驻地之外的额外城市", + "fail": f"票据路线存在目的地不一致或额外中转城市,命中{risk_label}并要求补充说明或退回修改", + }, + } + + +def build_city_consistency_params(draft: dict[str, Any]) -> dict[str, Any]: + exception_keywords = list(draft.get("exception_keywords") or CITY_EXCEPTION_KEYWORDS) + return { + "semantic_type": TRAVEL_ROUTE_CITY_SEMANTIC_TYPE, + "attachment_city_fields": list(CITY_ATTACHMENT_FIELDS), + "reference_city_fields": list(CITY_REFERENCE_FIELDS), + "home_city_fields": list(CITY_HOME_FIELDS), + "exception_fields": list(CITY_EXCEPTION_FIELDS), + "exception_keywords": exception_keywords, + "keywords": [], + "route_anomaly_policy": "flag_unexpected_intermediate_cities", + "exception_handling": "exception_text_is_evidence_not_auto_pass_for_route_anomaly", + "formula": ( + "A=UNION(attachment.route_cities, attachment.hotel_city); " + "B=UNION(claim.location, item.item_location); " + "C=UNION(employee.location); " + "HIT WHEN (A∩B=∅ AND NOT CONTAINS_ANY(exception_fields, exception_keywords)) " + "OR EXISTS(city IN A WHERE city NOT IN B∪C)" + ), + "conditions": [ + { + "left_group": list(CITY_ATTACHMENT_FIELDS), + "operator": "route_city_consistency", + "right_group": list(CITY_REFERENCE_FIELDS), + "home_group": list(CITY_HOME_FIELDS), + "exception_fields": list(CITY_EXCEPTION_FIELDS), + "exception_keywords": exception_keywords, + } + ], + } diff --git a/server/src/app/services/risk_rule_manifest_normalizer.py b/server/src/app/services/risk_rule_manifest_normalizer.py new file mode 100644 index 0000000..53a6c30 --- /dev/null +++ b/server/src/app/services/risk_rule_manifest_normalizer.py @@ -0,0 +1,199 @@ +from __future__ import annotations + +from typing import Any + +from app.services.risk_rule_flow_diagram import ( + RiskRuleFlowDiagramField, + RiskRuleFlowDiagramRenderer, + RiskRuleFlowDiagramSpec, +) +from app.services.risk_rule_generation_semantics import ( + CITY_ATTACHMENT_FIELDS, + CITY_CONSISTENCY_SEMANTIC_TYPE, + CITY_CONSISTENCY_SEMANTIC_TYPES, + CITY_EXCEPTION_FIELDS, + CITY_EXCEPTION_KEYWORDS, + CITY_HOME_FIELDS, + CITY_REFERENCE_FIELDS, + build_city_consistency_params, + is_city_consistency_rule, +) + +RISK_LEVEL_LABELS = { + "low": "低风险", + "medium": "中风险", + "high": "高风险", + "critical": "极高风险", +} + +CITY_ROUTE_CONDITION_SUMMARY = ( + "判断公式:A=交通票行程城市∪住宿发票城市,B=申报目的地∪明细发生地点," + "C=员工常驻地/合理出发地。若A或B为空则要求补充识别;若A与B无交集且无合理说明," + "或票据路线中存在不属于B∪C的额外城市,则命中目的地不一致/中途周转异常风险。" +) +CITY_ROUTE_FLOW_DECISION = ( + "附件城市是否覆盖申报行程,且票据路线是否出现申报目的地和常驻地之外的中转城市" +) +CITY_ROUTE_FLOW_EVIDENCE = ( + "读取员工常驻地、申报目的地、明细发生地点、交通票行程城市、住宿发票城市和报销事由" +) + + +def normalize_risk_rule_manifest(manifest: dict[str, Any]) -> dict[str, Any]: + """把历史误编译的城市一致性规则规范为受控语义 DSL。""" + + if not isinstance(manifest, dict) or not _looks_like_city_consistency_manifest(manifest): + return manifest + + payload = dict(manifest) + metadata = dict(payload.get("metadata") if isinstance(payload.get("metadata"), dict) else {}) + params = dict(payload.get("params") if isinstance(payload.get("params"), dict) else {}) + exception_keywords = _read_string_list( + payload.get("exception_keywords") or params.get("exception_keywords") + ) or list(CITY_EXCEPTION_KEYWORDS) + field_keys = _resolve_city_field_keys(payload, params) + severity = _resolve_severity(payload) + severity_label = RISK_LEVEL_LABELS.get(severity, "中风险") + + payload["template_key"] = "field_compare_v1" + payload["semantic_type"] = CITY_CONSISTENCY_SEMANTIC_TYPE + payload["keywords"] = [] + payload["exception_keywords"] = exception_keywords + + params.update( + build_city_consistency_params( + { + "exception_keywords": exception_keywords, + } + ) + ) + params["template_key"] = "field_compare_v1" + params["field_keys"] = field_keys + params["condition_summary"] = CITY_ROUTE_CONDITION_SUMMARY + payload["params"] = params + + payload["condition_summary"] = CITY_ROUTE_CONDITION_SUMMARY + flow = dict(metadata.get("flow") if isinstance(metadata.get("flow"), dict) else {}) + flow["evidence"] = CITY_ROUTE_FLOW_EVIDENCE + flow["decision"] = CITY_ROUTE_FLOW_DECISION + flow.setdefault( + "start", + "差旅报销单据提交,并上传交通票据、住宿票据或其他可识别城市的附件", + ) + flow.setdefault( + "pass", + "票据城市覆盖申报行程,且未出现申报目的地和常驻地之外的额外城市", + ) + flow["fail"] = ( + f"票据路线存在目的地不一致或额外中转城市,命中{severity_label}并要求补充说明或退回修改" + ) + metadata["condition_summary"] = CITY_ROUTE_CONDITION_SUMMARY + metadata["flow"] = flow + payload["metadata"] = metadata + payload["flow_diagram_svg"] = _build_city_flow_svg(payload, field_keys, severity, severity_label) + return payload + + +def _looks_like_city_consistency_manifest(manifest: dict[str, Any]) -> bool: + params = manifest.get("params") if isinstance(manifest.get("params"), dict) else {} + semantic_type = str(manifest.get("semantic_type") or params.get("semantic_type") or "").strip() + if semantic_type in CITY_CONSISTENCY_SEMANTIC_TYPES: + return True + + metadata = manifest.get("metadata") if isinstance(manifest.get("metadata"), dict) else {} + text = "\n".join( + str(value or "") + for value in ( + metadata.get("natural_language"), + params.get("natural_language"), + manifest.get("description"), + metadata.get("condition_summary"), + params.get("condition_summary"), + ) + ) + if is_city_consistency_rule(text): + return True + + field_keys = set(_resolve_city_field_keys(manifest, params)) + has_attachment_city = bool(field_keys & set(CITY_ATTACHMENT_FIELDS)) + has_reference_city = bool(field_keys & set(CITY_REFERENCE_FIELDS)) + return has_attachment_city and has_reference_city and "风险关键词" in text + + +def _resolve_city_field_keys(manifest: dict[str, Any], params: dict[str, Any]) -> list[str]: + inputs = manifest.get("inputs") if isinstance(manifest.get("inputs"), dict) else {} + input_fields = inputs.get("fields") if isinstance(inputs.get("fields"), list) else [] + known = { + str(item.get("key") or "").strip() + for item in input_fields + if isinstance(item, dict) and str(item.get("key") or "").strip() + } + candidates = [ + *_read_string_list(manifest.get("field_keys")), + *_read_string_list(params.get("field_keys") or params.get("search_fields")), + *CITY_ATTACHMENT_FIELDS, + *CITY_REFERENCE_FIELDS, + *CITY_HOME_FIELDS, + *CITY_EXCEPTION_FIELDS, + ] + resolved: list[str] = [] + for key in candidates: + if known and key not in known and key not in { + *CITY_ATTACHMENT_FIELDS, + *CITY_REFERENCE_FIELDS, + *CITY_HOME_FIELDS, + *CITY_EXCEPTION_FIELDS, + }: + continue + if key not in resolved: + resolved.append(key) + return resolved + + +def _build_city_flow_svg( + payload: dict[str, Any], + field_keys: list[str], + severity: str, + severity_label: str, +) -> str: + inputs = payload.get("inputs") if isinstance(payload.get("inputs"), dict) else {} + input_fields = inputs.get("fields") if isinstance(inputs.get("fields"), list) else [] + label_by_key = { + str(item.get("key") or "").strip(): str(item.get("label") or item.get("key") or "").strip() + for item in input_fields + if isinstance(item, dict) and str(item.get("key") or "").strip() + } + fields = tuple( + RiskRuleFlowDiagramField(key=key, label=label_by_key.get(key) or key) + for key in field_keys[:8] + ) + metadata = payload.get("metadata") if isinstance(payload.get("metadata"), dict) else {} + flow = metadata.get("flow") if isinstance(metadata.get("flow"), dict) else {} + return RiskRuleFlowDiagramRenderer().render( + RiskRuleFlowDiagramSpec( + title=str(payload.get("name") or "风险规则判断流程").strip(), + domain_label=str(payload.get("risk_category") or "差旅费").strip(), + severity=severity, + severity_label=severity_label, + fields=fields, + start=str(flow.get("start") or "差旅报销单据提交").strip(), + evidence=CITY_ROUTE_FLOW_EVIDENCE, + decision=CITY_ROUTE_FLOW_DECISION, + basis=CITY_ROUTE_CONDITION_SUMMARY, + pass_text=str(flow.get("pass") or "未命中风险,继续流转").strip(), + fail_text=str(flow.get("fail") or f"命中{severity_label},进入人工复核").strip(), + ) + ) + + +def _resolve_severity(payload: dict[str, Any]) -> str: + outcomes = payload.get("outcomes") if isinstance(payload.get("outcomes"), dict) else {} + fail = outcomes.get("fail") if isinstance(outcomes.get("fail"), dict) else {} + severity = str(fail.get("severity") or payload.get("severity") or "").strip().lower() + return severity if severity in RISK_LEVEL_LABELS else "medium" + + +def _read_string_list(value: Any) -> list[str]: + if not isinstance(value, list): + return [] + return [str(item or "").strip() for item in value if str(item or "").strip()] diff --git a/server/src/app/services/risk_rule_scoring.py b/server/src/app/services/risk_rule_scoring.py new file mode 100644 index 0000000..6f62868 --- /dev/null +++ b/server/src/app/services/risk_rule_scoring.py @@ -0,0 +1,322 @@ +from __future__ import annotations + +import re +from typing import Any + + +RISK_LEVEL_LABELS: dict[str, str] = { + "low": "低风险", + "medium": "中风险", + "high": "高风险", + "critical": "极高风险", +} + +RISK_SCORE_MODEL_VERSION = "risk_score_v1" + +RISK_SCORE_WEIGHTS: dict[str, float] = { + "impact": 0.35, + "certainty": 0.25, + "evidence": 0.15, + "exception": 0.10, + "action": 0.10, + "sensitivity": 0.05, +} + +LEVEL_SCORE_MAP: dict[str, int] = { + "none": 0, + "very_low": 12, + "low": 25, + "medium": 55, + "high": 78, + "critical": 94, +} + +LEVEL_ALIASES: dict[str, str] = { + "极低": "very_low", + "很低": "very_low", + "低": "low", + "低风险": "low", + "中": "medium", + "中等": "medium", + "中风险": "medium", + "高": "high", + "高风险": "high", + "极高": "critical", + "严重": "critical", + "重大": "critical", + "极高风险": "critical", + "very_low": "very_low", + "low": "low", + "medium": "medium", + "high": "high", + "critical": "critical", + "extreme": "critical", +} + +ACTION_SCORE_MAP: dict[str, int] = { + "observe": 20, + "remind": 35, + "supplement": 48, + "manual_review": 65, + "return": 78, + "block": 94, +} + +ACTION_ALIASES: dict[str, str] = { + "提示": "remind", + "提醒": "remind", + "补充": "supplement", + "补充说明": "supplement", + "人工复核": "manual_review", + "复核": "manual_review", + "审核": "manual_review", + "退回": "return", + "驳回": "return", + "阻断": "block", + "禁止": "block", + "禁止提交": "block", + "observe": "observe", + "remind": "remind", + "supplement": "supplement", + "manual_review": "manual_review", + "review": "manual_review", + "return": "return", + "reject": "return", + "block": "block", +} + +SENSITIVE_CATEGORY_SCORES: dict[str, int] = { + "travel": 70, + "hotel": 76, + "transport": 68, + "meal": 72, + "meeting": 58, + "training": 48, + "communication": 36, + "office": 30, + "welfare": 42, +} + + +def calculate_risk_rule_score( + *, + natural_language: str, + draft: dict[str, Any], + fields: list[Any], + expense_category: str | None, + expense_category_label: str, + requires_attachment: bool, +) -> dict[str, Any]: + evidence = _read_scoring_evidence(draft) + text = _join_text( + natural_language, + draft.get("description"), + draft.get("condition_summary"), + draft.get("formula"), + draft.get("message_template"), + ) + template_key = str(draft.get("template_key") or "").strip() + field_keys = _read_string_list(draft.get("field_keys")) + condition_count = len(draft.get("conditions") if isinstance(draft.get("conditions"), list) else []) + + components = { + "impact": _component_score( + evidence.get("impact_level"), + _infer_impact_score(text, template_key=template_key), + ), + "certainty": _component_score( + evidence.get("violation_certainty"), + _infer_certainty_score(template_key=template_key, condition_count=condition_count), + ), + "evidence": _component_score( + evidence.get("evidence_strength"), + _infer_evidence_score(field_keys, fields=fields, requires_attachment=requires_attachment), + ), + "exception": _component_score( + evidence.get("exception_dependence"), + _infer_exception_score(text, draft), + ), + "action": _action_score( + evidence.get("control_action"), + _infer_action_score(text, draft), + ), + "sensitivity": _component_score( + evidence.get("business_sensitivity"), + _infer_sensitivity_score(text, expense_category=expense_category), + ), + } + score = _clamp_score( + round(sum(components[key] * RISK_SCORE_WEIGHTS[key] for key in RISK_SCORE_WEIGHTS)) + ) + level = risk_level_from_score(score) + return { + "score": score, + "level": level, + "level_label": RISK_LEVEL_LABELS[level], + "model": RISK_SCORE_MODEL_VERSION, + "weights": RISK_SCORE_WEIGHTS, + "components": components, + "ai_evidence": evidence, + "basis": { + "template_key": template_key, + "field_count": len(field_keys), + "condition_count": condition_count, + "expense_category": expense_category, + "expense_category_label": expense_category_label, + "requires_attachment": requires_attachment, + }, + } + + +def apply_risk_score_to_draft(draft: dict[str, Any], score_result: dict[str, Any]) -> dict[str, Any]: + level_label = str(score_result.get("level_label") or RISK_LEVEL_LABELS["medium"]).strip() + updated = dict(draft) + flow = dict(updated.get("flow") if isinstance(updated.get("flow"), dict) else {}) + flow["fail"] = _replace_or_append_risk_label( + str(flow.get("fail") or "命中风险,进入人工复核"), + level_label, + ) + updated["flow"] = flow + description = str(updated.get("description") or "").strip() + if description: + updated["description"] = _replace_or_append_risk_label(description, level_label) + updated["risk_scoring_evidence"] = score_result.get("ai_evidence") or {} + return updated + + +def risk_level_from_score(score: int | float) -> str: + normalized = _clamp_score(round(float(score or 0))) + if normalized <= 30: + return "low" + if normalized <= 60: + return "medium" + if normalized <= 80: + return "high" + return "critical" + + +def _read_scoring_evidence(draft: dict[str, Any]) -> dict[str, Any]: + value = draft.get("risk_scoring_evidence") + return dict(value) if isinstance(value, dict) else {} + + +def _component_score(value: Any, fallback: int) -> int: + normalized = LEVEL_ALIASES.get(str(value or "").strip().lower()) + if normalized: + return LEVEL_SCORE_MAP[normalized] + normalized = LEVEL_ALIASES.get(str(value or "").strip()) + if normalized: + return LEVEL_SCORE_MAP[normalized] + return _clamp_score(fallback) + + +def _action_score(value: Any, fallback: int) -> int: + normalized = ACTION_ALIASES.get(str(value or "").strip().lower()) + if normalized: + return ACTION_SCORE_MAP[normalized] + normalized = ACTION_ALIASES.get(str(value or "").strip()) + if normalized: + return ACTION_SCORE_MAP[normalized] + return _clamp_score(fallback) + + +def _infer_impact_score(text: str, *, template_key: str) -> int: + if _contains_any(text, "伪造", "虚假", "重复报销", "骗取", "套取", "假票", "发票重复"): + return 94 + if _contains_any(text, "禁止", "阻断", "退回", "超预算", "超标准", "高风险", "不一致"): + return 78 + if template_key in {"field_compare_v1", "composite_rule_v1"}: + return 70 + if _contains_any(text, "缺少", "未上传", "不完整", "补充"): + return 48 + return 42 + + +def _infer_certainty_score(*, template_key: str, condition_count: int) -> int: + if template_key == "composite_rule_v1": + return 84 if condition_count >= 2 else 72 + if template_key == "field_compare_v1": + return 80 + if template_key == "field_required_v1": + return 86 + if template_key == "keyword_match_v1": + return 58 + return 55 + + +def _infer_evidence_score( + field_keys: list[str], + *, + fields: list[Any], + requires_attachment: bool, +) -> int: + keys = set(field_keys) + if any(key.startswith("attachment.") for key in keys) or requires_attachment: + return 82 + field_by_key = {str(getattr(field, "key", "") or "").strip(): field for field in fields} + if any(str(getattr(field_by_key.get(key), "source", "") or "") == "claim" for key in keys): + return 62 + return 50 + + +def _infer_exception_score(text: str, draft: dict[str, Any]) -> int: + exception_keywords = _read_string_list(draft.get("exception_keywords")) + if exception_keywords or _contains_any(text, "无说明", "没有说明", "未说明", "补充说明", "合理说明"): + return 66 + if _contains_any(text, "人工判断", "疑似", "可能"): + return 74 + return 35 + + +def _infer_action_score(text: str, draft: dict[str, Any]) -> int: + flow = draft.get("flow") if isinstance(draft.get("flow"), dict) else {} + corpus = _join_text(text, flow.get("fail"), draft.get("message_template")) + if _contains_any(corpus, "禁止提交", "阻断", "不允许提交", "拦截"): + return 94 + if _contains_any(corpus, "退回", "驳回", "重新提交"): + return 78 + if _contains_any(corpus, "人工复核", "复核", "审核"): + return 65 + if _contains_any(corpus, "补充", "说明"): + return 48 + return 35 + + +def _infer_sensitivity_score(text: str, *, expense_category: str | None) -> int: + if _contains_any(text, "发票", "票据", "虚假", "重复报销", "伪造"): + return 88 + if expense_category in SENSITIVE_CATEGORY_SCORES: + return SENSITIVE_CATEGORY_SCORES[expense_category or ""] + if _contains_any(text, "差旅", "住宿", "招待", "交通"): + return 72 + return 45 + + +def _replace_or_append_risk_label(value: str, level_label: str) -> str: + normalized = str(value or "").strip() + if not normalized: + return f"命中{level_label},进入人工复核" + replaced = re.sub(r"(低风险|中风险|高风险|极高风险)", level_label, normalized) + if replaced != normalized: + return replaced + if "风险" in normalized: + return normalized + return f"{normalized}({level_label})" + + +def _read_string_list(value: Any) -> list[str]: + if not isinstance(value, list): + return [] + return [str(item or "").strip() for item in value if str(item or "").strip()] + + +def _contains_any(text: str, *keywords: str) -> bool: + return any(keyword and keyword in text for keyword in keywords) + + +def _join_text(*parts: Any) -> str: + return "\n".join(str(part or "") for part in parts if str(part or "").strip()) + + +def _clamp_score(value: int | float) -> int: + return max(0, min(100, int(round(float(value))))) diff --git a/server/src/app/services/risk_rule_template_executor.py b/server/src/app/services/risk_rule_template_executor.py index bb29569..bd2d345 100644 --- a/server/src/app/services/risk_rule_template_executor.py +++ b/server/src/app/services/risk_rule_template_executor.py @@ -1,9 +1,16 @@ from __future__ import annotations import re +from datetime import date, datetime, timedelta from typing import Any from app.models.financial_record import ExpenseClaim +from app.services.risk_rule_generation_interpreter import COMPOSITE_RULE_TEMPLATE_KEY + +CITY_CONSISTENCY_SEMANTIC_TYPES = { + "travel_city_consistency", + "travel_route_city_consistency", +} class RiskRuleTemplateExecutor: @@ -20,9 +27,17 @@ class RiskRuleTemplateExecutor: if template_key == "field_required_v1": return self._evaluate_required_fields(params, claim=claim, contexts=contexts) if template_key == "field_compare_v1": + if str(params.get("semantic_type") or "").strip() in CITY_CONSISTENCY_SEMANTIC_TYPES: + return self._evaluate_city_consistency_rule( + params, + claim=claim, + contexts=contexts, + ) return self._evaluate_compare_conditions(params, claim=claim, contexts=contexts) if template_key == "keyword_match_v1": return self._evaluate_keyword_match(params, claim=claim, contexts=contexts) + if template_key == COMPOSITE_RULE_TEMPLATE_KEY: + return self._evaluate_composite_rule(params, claim=claim, contexts=contexts) return None def _evaluate_required_fields( @@ -105,6 +120,13 @@ class RiskRuleTemplateExecutor: claim: ExpenseClaim, contexts: list[dict[str, Any]], ) -> dict[str, Any] | None: + if self._looks_like_city_consistency_rule(params): + return self._evaluate_city_consistency_rule( + params, + claim=claim, + contexts=contexts, + ) + keywords = self._read_string_list(params.get("keywords")) search_fields = self._read_string_list( params.get("search_fields") or params.get("field_keys") @@ -140,6 +162,331 @@ class RiskRuleTemplateExecutor: }, } + def _evaluate_city_consistency_rule( + self, + params: dict[str, Any], + *, + claim: ExpenseClaim, + contexts: list[dict[str, Any]], + ) -> dict[str, Any] | None: + field_keys = self._read_string_list(params.get("search_fields") or params.get("field_keys")) + reference_keys = self._read_string_list(params.get("reference_city_fields")) or [ + key for key in field_keys if key in {"claim.location", "item.item_location"} + ] or ["claim.location", "item.item_location"] + attachment_keys = self._read_string_list(params.get("attachment_city_fields")) or [ + key + for key in field_keys + if key in {"attachment.route_cities", "attachment.hotel_city"} + ] or ["attachment.route_cities", "attachment.hotel_city"] + home_keys = self._read_string_list(params.get("home_city_fields")) or ["employee.location"] + + reference_values: list[str] = [] + attachment_values: list[str] = [] + home_values: list[str] = [] + route_values: list[str] = [] + for key in reference_keys: + reference_values.extend(self._resolve_values(key, claim=claim, contexts=contexts)) + for key in attachment_keys: + resolved = self._resolve_values(key, claim=claim, contexts=contexts) + attachment_values.extend(resolved) + if key == "attachment.route_cities": + route_values.extend(resolved) + for key in home_keys: + home_values.extend(self._resolve_values(key, claim=claim, contexts=contexts)) + + reference_values = self._dedupe_values(reference_values) + attachment_values = self._dedupe_values(attachment_values) + home_values = self._dedupe_values(home_values) + route_values = self._dedupe_values(route_values) + if not reference_values or not attachment_values: + return None + + explanation_keywords = self._read_string_list( + params.get("exception_keywords") or params.get("keywords") + ) + exception_fields = self._read_string_list(params.get("exception_fields")) or [ + "claim.reason", + "item.item_reason", + ] + explanation_corpus = "\n".join( + value + for key in exception_fields + for value in self._resolve_values(key, claim=claim, contexts=contexts) + ) + keyword_hits = [ + keyword + for keyword in explanation_keywords + if keyword and keyword in explanation_corpus + ] + unexpected_route_cities = self._resolve_unexpected_route_cities( + route_values, + reference_values=reference_values, + home_values=home_values, + ) + has_destination_overlap = self._condition_passes( + "overlap", + attachment_values, + reference_values, + ) + if not unexpected_route_cities and (has_destination_overlap or keyword_hits): + return None + + reason = ( + "票据路线包含申报行程和常驻地之外的中转城市。" + if unexpected_route_cities + else "票据城市与申报目的地或明细地点不一致,且未说明绕行、跨城或改签原因。" + ) + return { + "message": self._resolve_message( + params, + fallback=reason, + ), + "evidence": { + "failed_conditions": [ + { + "left": "attachment.city", + "operator": "overlap", + "right": "claim.location", + "left_values": attachment_values[:5], + "right_values": reference_values[:5], + } + ], + "condition_summary": params.get("condition_summary"), + "formula": params.get("formula"), + "city_consistency": { + "attachment_values": attachment_values[:8], + "reference_values": reference_values[:8], + "home_values": home_values[:8], + "route_values": route_values[:8], + "unexpected_route_cities": unexpected_route_cities[:8], + "explanation_keywords": explanation_keywords[:8], + "explanation_hits": keyword_hits[:8], + }, + }, + } + + def _evaluate_composite_rule( + self, + params: dict[str, Any], + *, + claim: ExpenseClaim, + contexts: list[dict[str, Any]], + ) -> dict[str, Any] | None: + conditions = params.get("conditions") if isinstance(params.get("conditions"), list) else [] + condition_evidence: list[dict[str, Any]] = [] + condition_results: dict[str, bool] = {} + for index, condition in enumerate(conditions): + if not isinstance(condition, dict): + continue + condition_id = str(condition.get("id") or f"condition_{index + 1}").strip() + passed, evidence = self._evaluate_composite_condition( + condition, + claim=claim, + contexts=contexts, + ) + condition_results[condition_id] = passed + condition_evidence.append({"id": condition_id, **evidence, "passed": passed}) + + hit_logic = params.get("hit_logic") + hit = ( + self._evaluate_logic_node(hit_logic, condition_results) + if isinstance(hit_logic, (dict, list, str)) + else bool(condition_results) and all(condition_results.values()) + ) + if not hit: + return None + + return { + "message": self._resolve_message( + params, + fallback=str(params.get("condition_summary") or "复合规则条件命中,进入人工复核。"), + ), + "evidence": { + "condition_summary": params.get("condition_summary"), + "formula": params.get("formula"), + "semantic_type": params.get("semantic_type"), + "conditions": condition_evidence, + "condition_results": condition_results, + "hit_logic": hit_logic, + "rule_ir": params.get("rule_ir") if isinstance(params.get("rule_ir"), dict) else {}, + }, + } + + def _evaluate_composite_condition( + self, + condition: dict[str, Any], + *, + claim: ExpenseClaim, + contexts: list[dict[str, Any]], + ) -> tuple[bool, dict[str, Any]]: + operator = str(condition.get("operator") or "").strip() + fields = self._read_string_list(condition.get("fields")) + left_fields = self._read_string_list(condition.get("left_fields")) + right_fields = self._read_string_list(condition.get("right_fields")) + if operator == "exists_any": + values = self._resolve_group_values(fields, claim=claim, contexts=contexts) + return bool(values), {"operator": operator, "fields": fields, "values": values[:8]} + if operator in {"exists_all", "all_present"}: + missing = [ + key for key in fields if not self._resolve_values(key, claim=claim, contexts=contexts) + ] + return not missing, {"operator": operator, "fields": fields, "missing_fields": missing} + if operator in {"not_in_scope", "not_in_set", "not_overlap"}: + left_values = self._resolve_group_values(left_fields, claim=claim, contexts=contexts) + right_values = self._resolve_group_values(right_fields, claim=claim, contexts=contexts) + matched = self._values_overlap(left_values, right_values) + return bool(left_values and right_values and not matched), { + "operator": operator, + "left_fields": left_fields, + "right_fields": right_fields, + "left_values": left_values[:8], + "right_values": right_values[:8], + } + if operator in {"in_scope", "overlap"}: + left_values = self._resolve_group_values(left_fields, claim=claim, contexts=contexts) + right_values = self._resolve_group_values(right_fields, claim=claim, contexts=contexts) + return self._values_overlap(left_values, right_values), { + "operator": operator, + "left_fields": left_fields, + "right_fields": right_fields, + "left_values": left_values[:8], + "right_values": right_values[:8], + } + if operator == "date_outside_range": + return self._evaluate_date_outside_range(condition, claim=claim, contexts=contexts) + if operator in {"not_contains_any", "contains_any"}: + keywords = self._read_string_list(condition.get("keywords")) + values = self._resolve_group_values(fields, claim=claim, contexts=contexts) + corpus = "\n".join(values) + hits = [keyword for keyword in keywords if keyword and keyword in corpus] + passed = not hits if operator == "not_contains_any" else bool(hits) + return passed, { + "operator": operator, + "fields": fields, + "keyword_hits": hits[:8], + "values": values[:8], + } + left = str(condition.get("left") or "").strip() + right = str(condition.get("right") or "").strip() + if left: + left_values = self._resolve_values(left, claim=claim, contexts=contexts) + right_values = self._resolve_values(right, claim=claim, contexts=contexts) if right else [] + passed = self._condition_passes(operator or "overlap", left_values, right_values) + return passed, { + "operator": operator or "overlap", + "left": left, + "right": right, + "left_values": left_values[:8], + "right_values": right_values[:8], + } + return False, {"operator": operator or "unknown"} + + def _evaluate_date_outside_range( + self, + condition: dict[str, Any], + *, + claim: ExpenseClaim, + contexts: list[dict[str, Any]], + ) -> tuple[bool, dict[str, Any]]: + date_fields = self._read_string_list(condition.get("date_fields")) + start_fields = self._read_string_list(condition.get("range_start_fields")) + end_fields = self._read_string_list(condition.get("range_end_fields")) + tolerance_days = int(condition.get("tolerance_days") or 0) + dates = self._resolve_group_dates(date_fields, claim=claim, contexts=contexts) + starts = self._resolve_group_dates(start_fields, claim=claim, contexts=contexts) + ends = self._resolve_group_dates(end_fields, claim=claim, contexts=contexts) + if not dates or not (starts or ends): + return False, { + "operator": "date_outside_range", + "date_fields": date_fields, + "range_start_fields": start_fields, + "range_end_fields": end_fields, + "dates": [item.isoformat() for item in dates], + "range_start": None, + "range_end": None, + } + start = min(starts or ends) - timedelta(days=tolerance_days) + end = max(ends or starts) + timedelta(days=tolerance_days) + outside = [item for item in dates if item < start or item > end] + return bool(outside), { + "operator": "date_outside_range", + "date_fields": date_fields, + "range_start_fields": start_fields, + "range_end_fields": end_fields, + "dates": [item.isoformat() for item in dates], + "range_start": start.isoformat(), + "range_end": end.isoformat(), + "outside_dates": [item.isoformat() for item in outside], + } + + def _resolve_group_values( + self, + field_keys: list[str], + *, + claim: ExpenseClaim, + contexts: list[dict[str, Any]], + ) -> list[str]: + values: list[str] = [] + for key in field_keys: + values.extend(self._resolve_values(key, claim=claim, contexts=contexts)) + return self._dedupe_values(values) + + def _resolve_group_dates( + self, + field_keys: list[str], + *, + claim: ExpenseClaim, + contexts: list[dict[str, Any]], + ) -> list[date]: + values: list[date] = [] + for key in field_keys: + for value in self._resolve_values(key, claim=claim, contexts=contexts): + parsed = self._parse_date_value(value) + if parsed and parsed not in values: + values.append(parsed) + return values + + @staticmethod + def _evaluate_logic_node(node: Any, condition_results: dict[str, bool]) -> bool: + if isinstance(node, str): + return bool(condition_results.get(node)) + if isinstance(node, list): + return all(RiskRuleTemplateExecutor._evaluate_logic_node(item, condition_results) for item in node) + if not isinstance(node, dict): + return bool(node) + if "all" in node: + values = node.get("all") if isinstance(node.get("all"), list) else [] + return all(RiskRuleTemplateExecutor._evaluate_logic_node(item, condition_results) for item in values) + if "any" in node: + values = node.get("any") if isinstance(node.get("any"), list) else [] + return any(RiskRuleTemplateExecutor._evaluate_logic_node(item, condition_results) for item in values) + if "not" in node: + return not RiskRuleTemplateExecutor._evaluate_logic_node(node.get("not"), condition_results) + return False + + @staticmethod + def _looks_like_city_consistency_rule(params: dict[str, Any]) -> bool: + field_keys = RiskRuleTemplateExecutor._read_string_list( + params.get("search_fields") or params.get("field_keys") + ) + if str(params.get("semantic_type") or "").strip() in CITY_CONSISTENCY_SEMANTIC_TYPES: + return True + has_reference = any(key in {"claim.location", "item.item_location"} for key in field_keys) + has_attachment_city = any( + key in {"attachment.route_cities", "attachment.hotel_city"} for key in field_keys + ) + if not (has_reference and has_attachment_city): + return False + text = "\n".join( + str(params.get(key) or "") + for key in ("natural_language", "condition_summary", "message_template") + ) + consistency_terms = ("一致", "不一致", "匹配", "不符", "对应", "出现在") + city_terms = ("城市", "地点", "目的地", "行程", "票据", "发票") + return any(term in text for term in consistency_terms) and any( + term in text for term in city_terms + ) + def _resolve_values( self, field_key: str, @@ -150,6 +497,12 @@ class RiskRuleTemplateExecutor: normalized = str(field_key or "").strip() if not normalized: return [] + if normalized == "claim.trip_start_date": + explicit = getattr(claim, "trip_start_date", None) + return self._normalize_values([explicit or self._claim_trip_date(claim, start=True)]) + if normalized == "claim.trip_end_date": + explicit = getattr(claim, "trip_end_date", None) + return self._normalize_values([explicit or self._claim_trip_date(claim, start=False)]) if normalized.startswith("claim."): return self._normalize_values([getattr(claim, normalized.removeprefix("claim."), "")]) if normalized.startswith("item."): @@ -157,10 +510,39 @@ class RiskRuleTemplateExecutor: return self._normalize_values( [getattr(item, attr, "") for item in list(claim.items or [])] ) + if normalized.startswith("employee."): + employee = getattr(claim, "employee", None) + if employee is None: + return [] + return self._normalize_values( + [getattr(employee, normalized.removeprefix("employee."), "")] + ) if normalized.startswith("attachment."): return self._resolve_attachment_values(normalized.removeprefix("attachment."), contexts) return [] + @staticmethod + def _resolve_unexpected_route_cities( + route_values: list[str], + *, + reference_values: list[str], + home_values: list[str], + ) -> list[str]: + if len(route_values) < 2: + return [] + allowed = {value.lower() for value in [*reference_values, *home_values] if value} + if not allowed: + return [] + candidates = route_values if home_values else route_values[1:-1] + unexpected: list[str] = [] + for city in candidates: + normalized = city.lower() + if normalized in allowed: + continue + if city not in unexpected: + unexpected.append(city) + return unexpected + def _resolve_attachment_values( self, field_key: str, contexts: list[dict[str, Any]] ) -> list[str]: @@ -171,13 +553,15 @@ class RiskRuleTemplateExecutor: document_info = {} if field_key == "ocr_text": values.extend([context.get("ocr_text"), context.get("ocr_summary")]) - if field_key in {"hotel_city", "route_cities"}: + if field_key == "hotel_city": specific_values = self._scan_document_values(document_info, field_key) values.extend( specific_values if specific_values else self._scan_document_values(document_info, "city") ) + elif field_key == "route_cities": + values.extend(self._scan_document_values(document_info, field_key)) else: values.extend(self._scan_document_values(document_info, field_key)) return self._normalize_values(values) @@ -207,6 +591,8 @@ class RiskRuleTemplateExecutor: "buyer_name": ("购买方", "抬头", "买方"), "goods_name": ("品名", "商品", "服务名称"), "issue_date": ("日期", "开票日期", "发票日期"), + "stay_start_date": ("入住日期", "住宿开始", "入住时间", "开始日期"), + "stay_end_date": ("离店日期", "退房日期", "住宿结束", "结束日期"), "hotel_city": ("住宿城市", "酒店城市", "酒店地点", "住宿", "酒店"), "route_cities": ("行程", "路线", "目的地", "出差城市"), "city": ("城市", "地点"), @@ -222,6 +608,17 @@ class RiskRuleTemplateExecutor: ) -> bool: return bool(self._resolve_values(field_key, claim=claim, contexts=contexts)) + @staticmethod + def _claim_trip_date(claim: ExpenseClaim, *, start: bool) -> date | datetime | None: + item_dates = [ + item.item_date + for item in list(claim.items or []) + if getattr(item, "item_date", None) is not None + ] + if item_dates: + return min(item_dates) if start else max(item_dates) + return getattr(claim, "occurred_at", None) + @staticmethod def _condition_passes(operator: str, left_values: list[str], right_values: list[str]) -> bool: if operator == "is_empty": @@ -239,6 +636,43 @@ class RiskRuleTemplateExecutor: return any(any(right in left for right in right_set) for left in left_set) return bool(left_set & right_set) + @staticmethod + def _values_overlap(left_values: list[str], right_values: list[str]) -> bool: + left_set = [RiskRuleTemplateExecutor._normalize_match_value(value) for value in left_values] + right_set = [RiskRuleTemplateExecutor._normalize_match_value(value) for value in right_values] + for left in left_set: + for right in right_set: + if left and right and (left == right or left in right or right in left): + return True + return False + + @staticmethod + def _normalize_match_value(value: str) -> str: + return re.sub(r"[省市区县\s]+$", "", str(value or "").strip().lower()) + + @staticmethod + def _parse_date_value(value: Any) -> date | None: + if isinstance(value, datetime): + return value.date() + if isinstance(value, date): + return value + text = str(value or "").strip() + if not text: + return None + iso_match = re.search(r"(\d{4})[-/.](\d{1,2})[-/.](\d{1,2})", text) + cn_match = re.search(r"(\d{4})年(\d{1,2})月(\d{1,2})日", text) + match = iso_match or cn_match + if match: + year, month, day = (int(part) for part in match.groups()) + try: + return date(year, month, day) + except ValueError: + return None + try: + return date.fromisoformat(text[:10]) + except ValueError: + return None + @staticmethod def _normalize_values(values: list[Any]) -> list[str]: normalized: list[str] = [] @@ -251,6 +685,15 @@ class RiskRuleTemplateExecutor: normalized.append(text) return normalized + @staticmethod + def _dedupe_values(values: list[str]) -> list[str]: + deduped: list[str] = [] + for value in values: + text = str(value or "").strip() + if text and text not in deduped: + deduped.append(text) + return deduped + @staticmethod def _read_string_list(value: Any) -> list[str]: if not isinstance(value, list): diff --git a/server/src/app/services/user_agent_application.py b/server/src/app/services/user_agent_application.py index 1b5b53f..778fc45 100644 --- a/server/src/app/services/user_agent_application.py +++ b/server/src/app/services/user_agent_application.py @@ -1,6 +1,5 @@ from __future__ import annotations -import hashlib import re from datetime import UTC, datetime, timedelta from decimal import Decimal, InvalidOperation @@ -16,6 +15,11 @@ from app.schemas.user_agent import ( UserAgentSuggestedAction, ) from app.services.expense_claim_access_policy import ExpenseClaimAccessPolicy +from app.services.document_numbering import ( + build_document_number, + generate_unique_expense_claim_no, +) +from app.services.user_agent_application_locations import normalize_application_location APPLICATION_CONTEXT_VALUES = { "application", @@ -31,35 +35,6 @@ APPLICATION_TRANSPORT_KEYWORDS = { "火车": ("火车", "高铁", "动车", "铁路", "列车"), "轮船": ("轮船", "船", "客轮", "邮轮", "坐船"), } -APPLICATION_DESTINATION_PREFIXES = ( - "上海", - "北京", - "广州", - "深圳", - "杭州", - "南京", - "苏州", - "成都", - "重庆", - "武汉", - "西安", - "天津", - "宁波", - "青岛", - "长沙", - "郑州", - "济南", - "合肥", - "福州", - "厦门", - "昆明", - "南昌", - "沈阳", - "大连", - "无锡", - "佛山", - "东莞", -) APPLICATION_REASON_VERBS = ( "支撑", "支持", @@ -189,7 +164,7 @@ class UserAgentApplicationMixin: f"申请单号:{application_no}", "申请信息:\n" + self._build_application_summary_table(facts), f"当前状态:{manager_name}审核中。", - "预算处理:预计总费用已作为预算占用参考,等待领导审核确认。", + "预算处理:用户预估费用已作为预算占用参考,等待领导审核确认。", ] ) @@ -210,6 +185,14 @@ class UserAgentApplicationMixin: "transport_mode": "", "amount": "", "application_type": "", + "grade": "", + "lodging_daily_cap": "", + "subsidy_daily_cap": "", + "transport_policy": "", + "policy_estimate": "", + "matched_city": "", + "rule_name": "", + "rule_version": "", } for message, is_current in self._iter_application_user_messages(payload): partial = { @@ -225,6 +208,10 @@ class UserAgentApplicationMixin: if value: facts[key] = value + for key, value in self._resolve_application_preview_facts(payload.context_json or {}).items(): + if value: + facts[key] = value + if not facts["application_type"]: facts["application_type"] = self._infer_application_type(facts) facts["time"] = self._expand_application_time_with_days( @@ -233,6 +220,40 @@ class UserAgentApplicationMixin: ) return facts + @staticmethod + def _resolve_application_preview_facts(context_json: dict[str, object]) -> dict[str, str]: + preview = context_json.get("application_preview") + if not isinstance(preview, dict): + return {} + fields = preview.get("fields") + if not isinstance(fields, dict): + return {} + + def pick(*keys: str) -> str: + for key in keys: + value = str(fields.get(key) or "").strip() + if value: + return value + return "" + + return { + "application_type": pick("applicationType", "application_type"), + "time": pick("time", "timeRange", "time_range"), + "location": pick("location"), + "reason": pick("reason"), + "days": pick("days"), + "transport_mode": pick("transportMode", "transport_mode"), + "amount": pick("amount"), + "grade": pick("grade"), + "lodging_daily_cap": pick("lodgingDailyCap", "lodging_daily_cap"), + "subsidy_daily_cap": pick("subsidyDailyCap", "subsidy_daily_cap"), + "transport_policy": pick("transportPolicy", "transport_policy"), + "policy_estimate": pick("policyEstimate", "policy_estimate"), + "matched_city": pick("matchedCity", "matched_city"), + "rule_name": pick("ruleName", "rule_name"), + "rule_version": pick("ruleVersion", "rule_version"), + } + def _resolve_expense_application_step( self, payload: UserAgentRequest, @@ -335,23 +356,6 @@ class UserAgentApplicationMixin: ) return match.group("value").strip() if match else "" - def _resolve_application_entity_or_label( - self, - payload: UserAgentRequest, - entity_type: str, - labels: tuple[str, ...], - ) -> str: - entity_value = next( - ( - str(item.normalized_value or item.value or "").strip() - for item in payload.ontology.entities - if item.type == entity_type - and str(item.normalized_value or item.value or "").strip() - ), - "", - ) - return entity_value or self._resolve_application_labeled_value(payload.message, labels) - def _resolve_application_location( self, payload: UserAgentRequest, @@ -359,12 +363,24 @@ class UserAgentApplicationMixin: message: str, use_entities: bool, ) -> str: - entity_or_labeled = ( - self._resolve_application_entity_or_label(payload, "location", ("地点", "业务地点", "发生地点")) - if use_entities - else self._resolve_application_labeled_value(message, ("地点", "业务地点", "发生地点")) - ) - return entity_or_labeled or self._resolve_application_location_from_text(message) + labeled = self._resolve_application_labeled_value(message, ("地点", "业务地点", "发生地点")) + if labeled: + return normalize_application_location(labeled) + + if use_entities: + entity_value = next( + ( + str(item.normalized_value or item.value or "").strip() + for item in payload.ontology.entities + if item.type == "location" + and str(item.normalized_value or item.value or "").strip() + ), + "", + ) + if entity_value: + return normalize_application_location(entity_value) + + return self._resolve_application_location_from_text(message) @staticmethod def _resolve_application_location_from_text(message: str) -> str: @@ -380,30 +396,11 @@ class UserAgentApplicationMixin: if not match: continue target = str(match.group("target") or "").strip() - location = UserAgentApplicationMixin._normalize_application_location_target(target) + location = normalize_application_location(target) if location: return location return "" - @staticmethod - def _normalize_application_location_target(target: str) -> str: - text = str(target or "").strip("::,,。;;") - if not text: - return "" - known = next((item for item in APPLICATION_DESTINATION_PREFIXES if text.startswith(item)), "") - if known: - return known - - verb_indexes = [ - index - for keyword in APPLICATION_REASON_VERBS - for index in [text.find(keyword)] - if index > 0 - ] - if verb_indexes: - return text[: min(verb_indexes)] - return text[:12] - @staticmethod def _resolve_application_days(message: str) -> str: labeled = UserAgentApplicationMixin._resolve_application_labeled_value( @@ -445,7 +442,7 @@ class UserAgentApplicationMixin: return "" text = re.sub( - r"^(?:发生时间|业务发生时间|申请时间|时间|地点|业务地点|发生地点|天数|出差天数|申请天数|出行方式|交通方式|交通工具|预计总费用|预计费用|预计金额|申请金额|预算|金额)[::]\s*", + r"^(?:发生时间|业务发生时间|申请时间|时间|地点|业务地点|发生地点|天数|出差天数|申请天数|出行方式|交通方式|交通工具|用户预估费用|预估费用|预计总费用|预计费用|预计金额|申请金额|预算|金额)[::]\s*", "", text, ) @@ -569,7 +566,7 @@ class UserAgentApplicationMixin: def _resolve_application_amount_from_text(message: str) -> str: labeled = UserAgentApplicationMixin._resolve_application_labeled_value( message, - ("预计总费用", "预计费用", "预计金额", "申请金额", "预算", "费用", "金额"), + ("用户预估费用", "预估费用", "预计总费用", "预计费用", "预计金额", "申请金额", "预算", "费用", "金额"), ) if labeled: return UserAgentApplicationMixin._normalize_application_amount(labeled) @@ -625,7 +622,7 @@ class UserAgentApplicationMixin: def _display_application_slot_label(slot: str) -> str: return { "expense_type": "申请类型", - "amount": "预计金额/预算", + "amount": "用户预估费用", "time_range": "发生时间", "time": "发生时间", "location": "地点", @@ -670,7 +667,7 @@ class UserAgentApplicationMixin: "reason": ("补充申请事由", "事由:"), "days": ("补充天数", "天数:"), "transport_mode": ("补充出行方式", "出行方式:"), - "amount": ("补充预计总费用", "预计总费用:"), + "amount": ("补充预估费用", "用户预估费用:"), } return config.get(field, ("补充申请信息", "")) @@ -718,7 +715,12 @@ class UserAgentApplicationMixin: ("事由", facts.get("reason", "")), ("天数", facts.get("days", "")), ("出行方式", facts.get("transport_mode", "")), - ("预计总费用", facts.get("amount", "")), + ("职级", facts.get("grade", "")), + ("住宿上限/天", facts.get("lodging_daily_cap", "")), + ("补贴标准/天", facts.get("subsidy_daily_cap", "")), + ("交通费用口径", facts.get("transport_policy", "")), + ("规则测算参考", facts.get("policy_estimate", "")), + ("用户预估费用", facts.get("amount", "")), ) ) @@ -735,7 +737,12 @@ class UserAgentApplicationMixin: ("事由", facts.get("reason", "")), ("天数", facts.get("days", "")), ("出行方式", facts.get("transport_mode", "")), - ("预计总费用", facts.get("amount", "")), + ("职级", facts.get("grade", "")), + ("住宿上限/天", facts.get("lodging_daily_cap", "")), + ("补贴标准/天", facts.get("subsidy_daily_cap", "")), + ("交通费用口径", facts.get("transport_policy", "")), + ("规则测算参考", facts.get("policy_estimate", "")), + ("用户预估费用", facts.get("amount", "")), ] visible_rows = rows if include_empty else [(label, value) for label, value in rows if str(value or "").strip()] if not visible_rows: @@ -790,13 +797,38 @@ class UserAgentApplicationMixin: submitted_at=datetime.now(UTC), status="submitted", approval_stage="直属领导审批", - risk_flags_json=[], + risk_flags_json=[self._build_application_detail_flag(facts)], ) self.db.add(claim) self.db.commit() self.db.refresh(claim) return claim + @staticmethod + def _build_application_detail_flag(facts: dict[str, str]) -> dict[str, object]: + return { + "source": "application_detail", + "severity": "info", + "label": "申请详情", + "application_detail": { + "application_type": str(facts.get("application_type") or "").strip(), + "time": str(facts.get("time") or "").strip(), + "location": str(facts.get("location") or "").strip(), + "reason": str(facts.get("reason") or "").strip(), + "days": str(facts.get("days") or "").strip(), + "transport_mode": str(facts.get("transport_mode") or "").strip(), + "amount": str(facts.get("amount") or "").strip(), + "grade": str(facts.get("grade") or "").strip(), + "lodging_daily_cap": str(facts.get("lodging_daily_cap") or "").strip(), + "subsidy_daily_cap": str(facts.get("subsidy_daily_cap") or "").strip(), + "transport_policy": str(facts.get("transport_policy") or "").strip(), + "policy_estimate": str(facts.get("policy_estimate") or "").strip(), + "matched_city": str(facts.get("matched_city") or "").strip(), + "rule_name": str(facts.get("rule_name") or "").strip(), + "rule_version": str(facts.get("rule_version") or "").strip(), + }, + } + def _resolve_application_manager_name( self, payload: UserAgentRequest, @@ -930,29 +962,15 @@ class UserAgentApplicationMixin: *, fallback_seed: str = "", ) -> str: - raw_date = str(facts.get("time") or "") - match = re.search(r"20\d{2}[-/.年]\d{1,2}[-/.月]\d{1,2}", raw_date) - date_text = match.group(0) if match else datetime.now().strftime("%Y-%m-%d") - digits = re.sub(r"\D", "", date_text)[:8].ljust(8, "0") - seed = re.sub(r"[^A-Za-z0-9]", "", fallback_seed)[-6:] or "SIM001" - return f"APP-{digits}-{seed.upper()}" + return build_document_number("application", timestamp=datetime.now(UTC)) def _build_application_claim_no( self, payload: UserAgentRequest, facts: dict[str, str], ) -> str: - context_json = payload.context_json or {} - seed_source = "|".join( - str(item or "").strip() - for item in ( - context_json.get("conversation_id"), - payload.user_id, - facts.get("time"), - facts.get("location"), - facts.get("reason"), - facts.get("amount"), - ) + return generate_unique_expense_claim_no( + self.db, + "application", + timestamp=datetime.now(UTC), ) - digest = hashlib.sha1(seed_source.encode("utf-8")).hexdigest()[:6] - return self._build_simulated_application_no_from_facts(facts, fallback_seed=digest) diff --git a/server/src/app/services/user_agent_application_locations.py b/server/src/app/services/user_agent_application_locations.py new file mode 100644 index 0000000..1f6b10d --- /dev/null +++ b/server/src/app/services/user_agent_application_locations.py @@ -0,0 +1,148 @@ +from __future__ import annotations + +import re + + +DIRECT_MUNICIPALITY_DISPLAY = { + "北京": "北京市", + "北京市": "北京市", + "上海": "上海市", + "上海市": "上海市", + "天津": "天津市", + "天津市": "天津市", + "重庆": "重庆市", + "重庆市": "重庆市", +} + +PROVINCE_ALIASES = { + "新疆维吾尔自治区": "新疆", + "新疆": "新疆", + "广东省": "广东", + "广东": "广东", + "浙江省": "浙江", + "浙江": "浙江", + "江苏省": "江苏", + "江苏": "江苏", + "四川省": "四川", + "四川": "四川", + "湖北省": "湖北", + "湖北": "湖北", + "陕西省": "陕西", + "陕西": "陕西", + "山东省": "山东", + "山东": "山东", + "湖南省": "湖南", + "湖南": "湖南", + "河南省": "河南", + "河南": "河南", + "安徽省": "安徽", + "安徽": "安徽", + "福建省": "福建", + "福建": "福建", + "云南省": "云南", + "云南": "云南", + "江西省": "江西", + "江西": "江西", + "辽宁省": "辽宁", + "辽宁": "辽宁", +} + +CITY_TO_PROVINCE = { + "伊犁": "新疆", + "伊犁哈萨克自治州": "新疆", + "乌鲁木齐": "新疆", + "克拉玛依": "新疆", + "喀什": "新疆", + "广州": "广东", + "深圳": "广东", + "佛山": "广东", + "东莞": "广东", + "杭州": "浙江", + "宁波": "浙江", + "南京": "江苏", + "苏州": "江苏", + "无锡": "江苏", + "成都": "四川", + "武汉": "湖北", + "西安": "陕西", + "青岛": "山东", + "济南": "山东", + "长沙": "湖南", + "郑州": "河南", + "合肥": "安徽", + "福州": "福建", + "厦门": "福建", + "昆明": "云南", + "南昌": "江西", + "沈阳": "辽宁", + "大连": "辽宁", +} + +LOCATION_NOISE_PATTERN = re.compile( + r"(?:出差|驻场|现场|支撑|支持|部署|上线|实施|拜访|验收|会议|采购|培训|协助|处理|办理|参加|进行).*$" +) + + +def normalize_application_location(value: str) -> str: + text = _cleanup_location_text(value) + if not text: + return "" + + direct = _resolve_direct_municipality(text) + if direct: + return direct + + province_city = _resolve_province_city(text) + if province_city: + return province_city + + return text[:12] + + +def _cleanup_location_text(value: str) -> str: + text = re.sub(r"\s+", "", str(value or "")) + text = text.strip("::,,。;;、") + text = re.sub(r"^(?:地点|业务地点|发生地点)[::]", "", text) + text = re.sub(r"^(?:去|到|赴|前往)", "", text) + text = LOCATION_NOISE_PATTERN.sub("", text) + return text.strip("::,,。;;、") + + +def _resolve_direct_municipality(text: str) -> str: + for key, display in DIRECT_MUNICIPALITY_DISPLAY.items(): + if text.startswith(key): + return display + return "" + + +def _resolve_province_city(text: str) -> str: + for province_alias, province_display in PROVINCE_ALIASES.items(): + if not text.startswith(province_alias): + continue + remainder = text[len(province_alias) :].strip("省市地区自治州盟,,、") + if not remainder: + return province_display + city = _resolve_city_name(remainder) + return f"{province_display},{city}" if city else province_display + + city = _resolve_city_name(text) + if city: + province = CITY_TO_PROVINCE.get(city) + return f"{province},{city}" if province else city + return "" + + +def _resolve_city_name(text: str) -> str: + normalized = text.strip(",,、") + if not normalized: + return "" + for city in sorted(CITY_TO_PROVINCE, key=len, reverse=True): + if normalized.startswith(city): + return _display_city_name(city) + return "" + + +def _display_city_name(city: str) -> str: + if city == "伊犁哈萨克自治州": + return "伊犁" + return city.removesuffix("市") diff --git a/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.pdf b/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.pdf deleted file mode 100644 index b2207b8ad799b9ba771c6d9d2c21f7c2c9be6221..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24995 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 Vfn*pX6GKxA;EfEbuKsSkTmb6uqc#8l diff --git a/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.pdf.meta.json b/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.pdf.meta.json deleted file mode 100644 index fbfb425..0000000 --- a/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.pdf.meta.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "file_name": "2月20_武汉-上海.pdf", - "storage_key": "635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.pdf", - "media_type": "application/pdf", - "size_bytes": 24995, - "uploaded_at": "2026-05-22T05:00:39.043901+00:00", - "previewable": true, - "preview_kind": "image", - "preview_storage_key": "635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.preview.png", - "preview_media_type": "image/png", - "preview_file_name": "2月20_武汉-上海.preview.png", - "analysis": { - "severity": "pass", - "label": "AI提示符合条件", - "headline": "AI提示:附件符合基础校验条件", - "summary": "已识别到票据类型和关键字段,且符合当前费用场景的附件要求。", - "points": [ - "票据类型:已识别为火车/高铁票。", - "附件类型要求:当前费用项目为火车票,已识别为火车/高铁票。", - "金额字段:已识别到与当前明细接近的金额 354.00 元。" - ], - "rule_basis": [], - "suggestion": "建议继续核对报销分类、费用说明和业务场景是否一致。" - }, - "document_info": { - "document_type": "train_ticket", - "document_type_label": "火车/高铁票", - "scene_code": "travel", - "scene_label": "差旅票据", - "fields": [ - { - "key": "amount", - "label": "金额", - "value": "354元" - }, - { - "key": "date", - "label": "列车出发时间", - "value": "2026-02-20 07:55" - }, - { - "key": "merchant_name", - "label": "商户", - "value": "中国铁路" - }, - { - "key": "invoice_number", - "label": "票据号码", - "value": "26429165800002785705" - }, - { - "key": "route", - "label": "行程", - "value": "武汉-上海" - } - ] - }, - "requirement_check": { - "matches": true, - "current_expense_type": "train_ticket", - "current_expense_type_label": "火车票", - "allowed_scene_labels": [], - "allowed_document_type_labels": [], - "recognized_scene_code": "travel", - "recognized_scene_label": "差旅票据", - "recognized_document_type": "train_ticket", - "recognized_document_type_label": "火车/高铁票", - "mismatch_severity": "high", - "rule_code": "rule.expense.scene_submission_standard", - "rule_name": "报销场景提交与附件标准", - "message": "当前费用项目为火车票,已识别为火车/高铁票。" - }, - "ocr_status": "recognized", - "ocr_error": "", - "ocr_text": "电子发票\n(铁路电子客票)\n州\n国家税务总局\n发票号码:26429165800002785705\n湖北省税务局\n开票日期:2026年05月18日\n武汉站\n上海虹桥站\nG458\nWuhan\nShanghaihongqiao\n2026年02月20日\n07:55开\n06车01B号\n二等座\n票价:¥354.00\n4201061987****1615\n曹笑竹\n电子客票号:6580061086021391007342026\n购买方名称:曹笑竹\n统一社会信用代码:\n买票请到12306发货请到95306\n中国铁路祝您旅途愉快", - "ocr_summary": "电子发票;(铁路电子客票);州", - "ocr_avg_score": 0.9580968717734019, - "ocr_line_count": 24, - "ocr_classification_source": "rule", - "ocr_classification_confidence": 0.88, - "ocr_classification_evidence": [ - "铁路电子客票", - "电子客票", - "铁路", - "二等座" - ], - "ocr_warnings": [] -} \ No newline at end of file diff --git a/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.preview.png b/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/0df374d2-0eeb-4c54-be94-53589dbf0d65/2月20_武汉-上海.preview.png deleted file mode 100644 index 0bdfb91ed399259b563c3e3570ff1bb0bffb6348..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136681 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 zл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 diff --git a/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/2014d004-4ef9-4655-8ef0-8f3ad64df2f7/2月23_上海-武汉.pdf.meta.json b/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/2014d004-4ef9-4655-8ef0-8f3ad64df2f7/2月23_上海-武汉.pdf.meta.json deleted file mode 100644 index ecceb77..0000000 --- a/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/2014d004-4ef9-4655-8ef0-8f3ad64df2f7/2月23_上海-武汉.pdf.meta.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "file_name": "2月23_上海-武汉.pdf", - "storage_key": "635e6e0f-d4ae-4913-a046-c2340643ea1c/2014d004-4ef9-4655-8ef0-8f3ad64df2f7/2月23_上海-武汉.pdf", - "media_type": "application/pdf", - "size_bytes": 24940, - "uploaded_at": "2026-05-22T05:01:02.605504+00:00", - "previewable": true, - "preview_kind": "image", - "preview_storage_key": "635e6e0f-d4ae-4913-a046-c2340643ea1c/2014d004-4ef9-4655-8ef0-8f3ad64df2f7/2月23_上海-武汉.preview.png", - "preview_media_type": "image/png", - "preview_file_name": "2月23_上海-武汉.preview.png", - "analysis": { - "severity": "pass", - "label": "AI提示符合条件", - "headline": "AI提示:附件符合基础校验条件", - "summary": "已识别到票据类型和关键字段,且符合当前费用场景的附件要求。", - "points": [ - "票据类型:已识别为火车/高铁票。", - "附件类型要求:当前费用项目为火车票,已识别为火车/高铁票。", - "金额字段:已识别到与当前明细接近的金额 354.00 元。" - ], - "rule_basis": [], - "suggestion": "建议继续核对报销分类、费用说明和业务场景是否一致。" - }, - "document_info": { - "document_type": "train_ticket", - "document_type_label": "火车/高铁票", - "scene_code": "travel", - "scene_label": "差旅票据", - "fields": [ - { - "key": "amount", - "label": "金额", - "value": "354元" - }, - { - "key": "date", - "label": "列车出发时间", - "value": "2026-02-23 13:54" - }, - { - "key": "merchant_name", - "label": "商户", - "value": "中国铁路" - }, - { - "key": "invoice_number", - "label": "票据号码", - "value": "26319166100006175398" - }, - { - "key": "route", - "label": "行程", - "value": "上海-武汉" - } - ] - }, - "requirement_check": { - "matches": true, - "current_expense_type": "train_ticket", - "current_expense_type_label": "火车票", - "allowed_scene_labels": [], - "allowed_document_type_labels": [], - "recognized_scene_code": "travel", - "recognized_scene_label": "差旅票据", - "recognized_document_type": "train_ticket", - "recognized_document_type_label": "火车/高铁票", - "mismatch_severity": "high", - "rule_code": "rule.expense.scene_submission_standard", - "rule_name": "报销场景提交与附件标准", - "message": "当前费用项目为火车票,已识别为火车/高铁票。" - }, - "ocr_status": "recognized", - "ocr_error": "", - "ocr_text": "电子发票\n(铁路电子客票)\n州\n国家税务总局\n发票号码:26319166100006175398\n开票日期:2026年05月18日\n上海市税务局\n上海虹桥站\n武汉站\nG456\nShanghaihongqiao\nWuhan\n2026年02月23日\n13:54开\n12车08B号\n二等座\n票价:¥354.00\n4201061987****1615\n曹笑竹\n电子客票号:6610061086021394837402026\n购买方名称:曹笑竹\n统一社会信用代码:\n买票请到12306发货请到95306\n中国铁路祝您旅途愉快", - "ocr_summary": "电子发票;(铁路电子客票);州", - "ocr_avg_score": 0.9620026834309101, - "ocr_line_count": 24, - "ocr_classification_source": "rule", - "ocr_classification_confidence": 0.88, - "ocr_classification_evidence": [ - "铁路电子客票", - "电子客票", - "铁路", - "二等座" - ], - "ocr_warnings": [] -} \ No newline at end of file diff --git a/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/2014d004-4ef9-4655-8ef0-8f3ad64df2f7/2月23_上海-武汉.preview.png b/server/storage/expense_claims/635e6e0f-d4ae-4913-a046-c2340643ea1c/2014d004-4ef9-4655-8ef0-8f3ad64df2f7/2月23_上海-武汉.preview.png deleted file mode 100644 index 099413e4ce18f4f2918bf8b613e4a7f695e2aee4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 137722 zcmZ_0RahKdw>4ToAPEwJ2MCbhP6!&@f?IHR3GVK}AwaO;5ZoOacXxMpcWIncdEf6l zdtdDTf~LBur>V7StvSaWW6TPcl@>uk#6tuC07XnxNDcttX#fDu_7yDn2|UbeC-B#6 zYf)7@0C<=5_XkGI!8Zl~J^*4u{0h$Vhf5ABn3_1iX{8k15-}yY9t9;erK7|g;t&5| zc)gdin(tqZ!4_teCAs5v)(1vEL->0lx@SHFWE+(WTEFBytxSiBx zMflV5z53|)Rseh%J?YH)*6WP(&l~V1ofjan{`XJdyZ`%8_eJ=BPyfFUKmKe*`}^g8 z&-!17erW9f{q+BF_z70zzZdqu4&Mr|{`b@W$6@=c_x~Bs+yDDe;GGZ2e@FGd4*%}~ z?moZ9vVs9GRRRB(T}2-)E0D+KDOB{$m*v0wonXLAyR&-%Yidf(!X0*ZMT4eaxLhYV)c>%= zFi>qdTv>s>zh^h^w5qBe${Y`igyhcl&y5HW%hdU2rXRI7lg&+y$hklAKkmo!Vwfw_ z&@xmXh<9#2xL%d#{~h|jqrS_1^ zM$qQ}zk*#Td?#$RI-4o$snG%_I9*=|Ec`e)ghFhAx0Cr7g=fWld{R>I0P!9N-5Jy4 z#>=~fnxB5o$X$O|)IWd);Q)9$Z~5rxy_1#CdipHH zB%XKTb<;zYY*t<5|1;D)*Sx;IL3xTx*qI0W$)WuP8R|u{`KsV<wZ1pgWPc;6G>E6l!oqY!30+ zg8$#8@oWeQqqVE}45O3P(;2G{c@8X9AVm-E(nItocA|+JurMiNKo17~G+FG(f15EJ zaF-Ad;j(kxZ-h7aeEWRMB(K^4)$kq?vVL=l@Ta-4HA&^-YHlKx`w0L0cVsFmRhU0# z`k4Lu6A{K1L#4{v{FT=sw>&*ico;tA9<4Ux4y+!F57=;FO7e`iWyhLYUJokDQWjK6 zN4Fc(Kl&1tY!PV>eQSM+BW=p+mt9$L4_e#MBXz>qpJb3}22itE8+aFvL zJmJr<*tO5n?2N#82Pe$c?Q-4)2;X#p`$*_Tc%9r^fnB2On_~i*xHYMw;-qSG?)iy< zg0BN1>Mi7+v<~!w|r}49Y zY@&JJyTP@lpP%9bu)zMnz`fLPpHV7zq06SMtN<6_PEQpKt780J;gdQ0!dT=4BqjOK z4A{%hj~fn9W3^oTVH6+cPp3x4Wk9^;rrYAwY)zgrOUskvKV#+d#bx9e$9(nV3y z&ON=H%cU$Bh78X;m^uU!bko80%g&?Gzgc%75J5miMUNfYC1E^*++z1Uljc7aAYMj@Gc>K(G4orM*&}7sEo~72LKyWG(P$H@qI4^J9A5<+zv+@s*k(7)3`uam&eSrr8 z9gjxGr?0hE@4e1>Op{-aL7|03L=HTy)&L;@RIxFiH@IJ^X9@!Vtj~NkWz*IGwQv%+ zH~^-~#f=_-PeR0al=*~O`|;x5sq=>-l3`%4X)1g7@g8NKHW9FUR$8|uHRnEn(67z% zIF!WUdlxC8lI(c&S6M{vuBlG<&(wlkpah!f6ND3%sDuMM)4ME?#P4V_@4k4m$wK z9EPaj^z~M*C5*`dgYL z6}A>U+;4NbZm4)g1vv;n8gC=TG_%E+K!RP4$HSi{H;P=3mrJn&K05=1dO%{r@Gu&iejN`W7PDYnETUe{(K*ttsp@>Om`%2bAofX8(3pjKr)z+AJ*_D^(WPU8s0 ze->_@oWbV=EkQWEE@HH_k+Mv4JZ!i*1;IZ z`Bwe7#-oidtQ|D=CNTIMBm{h#uqt(28shk`RPyCGl2>AS=h0T@C z2_xvSniX|~LTmJcUTIu(GA>OjX!!;dKu^vRRqbjQ0S`FJh}LL%`424F6?U`OWojd> z)>%D58C^-q(Zvorypa(@I&B)h@OET-)1OfZhv%CItaEDC*^>vi9(HKNonM?^*g9EhoHTF%EkbYqfz_kDs{*?TR}zo0w8=?QRoVhKd-7x7lw%gn=G|8! z2{8(3r<;`1{E8a2TQV*uCsi@JySZIxgK?G2ms#7F1pAieXs7GsDM)h_pgoaILOu~z z109XOi}PYp8x-Z0S8&kxH%IA6=%gqyhtm!n4h$RTz34TuzfdeVXEdzmSOL9P7f{dJ zD6d;$V3Mhr>3Nj@itTl0j-GLxKxyB5`~@P|*aoM9g>6+0h0FAZcyWji5-sg`$1vJJ zy_piDR?8qV4=gD1~Hp6}4fx?C%;S%;}fGK08UPi5DgS~0H}#hwH3jzp`=3(`=d zhI*@RA)$%95z($g?sv#m%1NCL5HH9qG@h#1vby_ZY-r%_3cXItbCRSh#%&H&qDy`W zh(M1CS;ht$Jv|)tV+$oFsI`^KRvH+2)XOMvFe8hGe%I%R5Q?5r&9R`>RVf zf>)4(G5K2(KLSo_6Q`?0ZV?FILnyl#@#Sxe1uCAz^zy!Cd1DPXXwl4{wVcP7v9DWr zo4la=x0U@6PfzXX0gkn;P&2uI35kgE{kuxgIa~OkI>O=uGnWP4b0Qw08H%VyBXmqf=zkhm)AU(PG1SBSf2gH<#;*qmP9J z7*csYI)024E!EdwX>@-L%X4*5W5viQRDSsLr-X6GCSiJ}#g1!F1Z;$r+_G|l`V&2k z>z%BEf+LaT(jUytN`yD)g?XD4WF<{rsLou#u3&DdN@JD5=-WN+uJ!y(k+%`HceAto zdjgW#VA`7?rbnmRn$u0shlSbPR5)1DMe;wqyqs5(@hXpk9FN+pB$V|-nq`7y9!}Fu zLb0)bqj(bEx(M~|xkQsc(Zt$s3XNp)yG3E}Ufe!}4aAxA+P-fY`*+d5y_1hXpFIRW zB`p5rBhQJy0D@7xq(o}3xAbuhUia8~bzc`CDTnp_zw>Vf8eA42ii8vO4oMhsrE1mR z^Eb2jp_WS;8x4Q@^=9aWv>IehhKrrfN`r-@5ZmLm!&>2CAHOz-7!R(ivB~f{1jOr@ zACy&4PQKCklm4wck#E}D$Ci8)9Wg8bIq`RNN@OGqK%bY0zItijLj9KHZqzs2JX1P0?L` zUz8QHwYBYB$B~_z1$h4sm+nk?=`c9tH6iFXFDqukJpUuMu&L+g3vZgWos~vcDo!%O z|AgY|nJ5Q*#YqJ$%vTzl>T(*$071hn3huYANuE$+X$m~jXYX9p<9k<`+r)TERVEY2 zNHW<#-c)je$w>DVZhn5JZAA0qO0y@K`u5tJA$}e$z<;H=iG^c>J}G`rwRi ze7yfEwav`P!^BJ0`PLTzoKdXz$4F*!l;k)qH7lc3cJngE9euPvhUHWn<6KPkWr(}# zh2q-xojU?70I*tRrhRxwJU$Afi$1EwbZt8uWM;gY z8X}^(YIRglD?YPH274tNK%2rXuaYWt2kjdkRB5pnyV*Nwi*k9qgW+^^+Mh4359GkE zasuZ%inj*7Xc{%Vj5gv6UDLSG#ovkHYz}4b!DzTo|yxMy@3O)*e|k z%i-+U;{pWW6KN58xy^3^Dn>`6MWf z>-JM7tX6bLbOs8Qx5n6geP)0yk(aA{Et-+1Vl@>qh+{J#(SK)aX8Ij7HU?VXtm^9XHe9-$wo&@3D)rl)4&mZ-(ri%Lp9HpG6zh*mI3e$99-7sE(HcS+meqt*-fEPbb-pgZ+@Be> z6F|&%o_q&#SyCy1-&)9bzBKS{c^vMYl~(&+e{ZB52w`CUbhCNP=-yrR&@KRch!)*B zn0!r3TOS#jnv_y)vQ=S-&x%1dN2|1&QPH<%F*(IKbVensIK@?_omHas*_<8tg@hy( zh=Lv6Ms;U(%0y(a(Qqfwbo$#D0T$yy{>)2@ZiYzZ%WT2x2aqFCD`W1YSEiNioDQSe_K$zuwZ|9IT3ioX|NkL z#}qU22}+Z-jr*j$TZEFwZc~E^LiO%8L6Ck4nHvLnCvAimp6}(bc{wllDC`*W^fIQT zKTtY4t}&>vpR7J@nJHOu+aAihx`sH|4T+%YMp5L-=Ms;IRWOK8j~|Yep#eexsZ>a! z$(pf^@8@?$gpW6Rv$Lt#EMEtw_rk)I+UOT|wCPdDPBg=z`QYGSxw50gI&s>s?Co6&vv(`C#r%0Y9*=%16#}GCL4R zA~%H(-=9hgyJcAIKeD|pbvYdYpVqZ=7k@_LdwQXA($S6+(-&xle!fferhv+;WdFC5ihLwJ>%G4F8CkLQ=!9Bqmgg0P5v5g zvUSwBPj07t!;ARJ<|{H*tjmDXbg_0R|3;200)jn)uW+VVEUwN%Wc*2pa3=Rta#8KX zlUE2hBAcckr<^k*yk5(&f8=Obql^zx>2Z(kIhp-#zGK>Vt5av&+Wsmpuk4lZ+X1>A#zm=G&-=7|NWF5DL%dHcXiD zj);mTQ;rDwiBHcxZ$JwAuSk%-(!S)@Ey?>F;%n~JRZAt0Wgg~sC-iyAekL{iS$VA{ ze`2)X=mGm3UaW&T_N$-d(mDbD0{++5fHMJ(M2z`NpAtkfQbU9GQw;!^y1M2Iq5`8o zo2QsaHkN+w^{1^4JNR@8EJ(AW7~#Dd+Of66V>NL#=oFQ+wH;4FjTs!IiXhTlgxor4XxQkc zLubF`Yexs4qa-n>R9J{aLRf=@IFi_~29x#K%>wSadbys-x=2K#*|S}mL#v$33l}Du zoonr`!!;4TY-H`Gt!VLh+w6CBG8mIJmeUiLmIy6-dY6Ko>gU;bs-m zf9ChmaDPL#>@T0GL|>;x%Ep#+UH^;uQ?j)?bDBawk^6HUeJLq z9#3Y6n54;mr!BE|k`X^qV)fvxk+ficrpk6508$#yYroz2Cy-pkZmDHLJ14n8&w*Hx)9(a0TSDT7GHEe{1W|uxo@drHE6`9Te%&9$7DaI7Zr(S zxcGg;CX?w65yl(*=4{1-kD3;$WMolFt_~<+OPo%s)n=MpwhCakWfZxa2mcUOGhb_-#;4IC2IWUevv_+x-$Jrz3uJu z{x&8KmL>;|Fzv+NW$Gis!?E&Ue{ZeLo}QsPub1=lZ4d4+b^p+?T$xq%F2i{-?E|R3 zWTl|Iq*gpqi^&hgK>L-u7({M#L7Y0bi%=x(u8b|H#%fCH_BQq`e3S};}Arma@CC*P-maOdjx4U<-9SoMj`@um%6y*uGF*G(t)@^up$0h%qx;mx-% z8-p;U`uB>|15aOx&kWJ^qD8Z&k2nF~?ej|j$T3pw3A}UERCn4Hr8T=c7W8^7a9;E? zF`I!)Kn8Goq!a6r*{E9>h^ev z&un_g&W#*3YQvF6?x`jO*Fq;FzwJ?(j@Pa zjZeupxClxbzl*@v++|VKF;QhUt?I6;?8@xHmQ2$f%fQWc7q=F7ge+d*uCwX|Exf8r zs(t^a{7=EBnmL(h$?R)_u-~4CS;NCen;98gk(8IcYa#d~((m!_Z@yXHz+2wG5ul?R z`X!IIGhBXmIRs4@uo}k{&H7=o%%NU`89@+nRg!V!W9C?bR5bhjt0o=DAj#$_4>^|!e~0}ePTdP&SAQ9u^OEBbgCw?Ls5(>P@#Q~z zMa4AamXIPZ4;Az4aLWovjXqyIJTQ%qF9xDKT^Ebc+6^f)w4PFZ zVS~izNqH$i5J(RXmlb*0$u}fQF<@`|ba^t__6>u(R5Qpu?JX)$%xQl)#;psMhW~m2 z)G=lu(P0KO^70p>x4m^*ghCzX;5tlpb4+9PTKj;b4DJmXqD2Nb#LOJ*TDuW|Vn_Q+ z^t`+n8R_GLL`djq1MghwG+a}W|q##l4@0cQr3%V>Di?_-&I#x199;-s8dPd5Gu>}-MpDHQ&YqHGPL>NE+fGkr^0{2U%l1`` zuXLQ8pRLJNsz<8OVz3#%4V@Sk`AzB(S)^RrC?!Keoc#2pc|DKvRV%vJ#ic5XL4bfj zT#G(Ph>v`S?$rOZ!llvQ)wQAl1eqyZwJjCCxug_mNTsQ~Nb?o>6?!0qE1dOE{_wUa z`2-FO91gjIDbEvbo`FO(^@9Bv#A8y+wFG z0bv36?PlMY#rGJ>#*U@Ss<9C=&E*CznO~TgqysD5pp1#kk!N=-EHq4ZUZ2c%Kjph} zG)gT9+LZrFOXD<5oKz?0hkF~-WJXrq7`2a&cf@Pwxb0U?xXiRkt|tppm3E&=ooB*P zByf;kz^2RA>RYsiSHncXg1pjoGS_3;VOFsgp^rq=ASl27V`<3Je9a5}6UEpq&LN*} zqUHiZRca!i(5`isW~407U7EVgtPR&f$0Lx4SAIeUGZfKEQ}G#oNt;1`4r1Kb;Xc;F ztjL!A(}uj>NtbvPP3}j7pgVt2tQP+$DdFaekLT#U%N2-@0$q^!jWq0w3$nO(=sFPR z!Gi_czRKir2V3=Ry$B7e4;uE0u>E;AfN3dr4y5^*1nex&1W_ueqFx-@ov!KTG>wat@ zW}y+N*L+8mRwJ0zozv5Y+TaF8^g*f%mctndnJRQ3BZe5!{;8mHc>hC3`ec_6iM;{Iujj(tOrfR<=YNQWKgDbLwO&Ql2Gn6 z(Y>CLLB%a#j=gAGBA3#%DZnX*60>UFV#Pp z&CJ|vy-S+@ronflhRH;$V0n|`$od{29mY#`)j;C8Qfp0@An zR2y@t8}meBgH2B1Qam#=Jf54Go$&44F}9c!rlQPa`lD^S*>!ltDVCkxx`ii|CXS9s zjnp1I=?cm*QD@hNFpbSVJ(swd z+!Q9C5?(WCzp$9Y#PUq-&7?;U&M4IiZg%w-BCb9%n5cXPl1i3})gVR3g9cuOQT@*f zS-BWUEVDy||M5~?iZppV?r-{>+%~~F9!lRP9`0~s0pW*pES(m94u#XUPJ;tOs+v6Z zi&d6OujQKx3FPo_pF6c$f|8>xcDbb-n_5RHH&#FDtuyw`o)ER4Twa25w%vU7$b6Hs zTMuh7+`U|hMlQICe%fxF%ue%3&~Q?`1jZwX41p|vk!f7!R_pngB5rgghGz+SgJo)mgN z*D|lp5!I>(tjx$Njs2Dy>7d4U=XG|Zyg@s3>V1@H1NFa|RM@pGpD(IT zMI)Pix)a`Jz$;Q;;elpC7s>leMyI~da-eLMvXCEF`g(B4KOo=%0R^{ITXmk3D*qP&`-+ane1!s!wB}*`Zika45lb`3S6>7($VqaG`&Km)8h9p&X}N(JRnp_` z@R5j;I~>?m7RW@&6*x;?=d(y&15#^K>)&&VDTFCJ~O_9vGDQF(dR$s$z{ zqwm<%Ma75oY2+(xbd?8-QoWA+7UjI#6UWUunPxjwEb(PD2Ib-&hYm*h%W%( zJoN{t-}z4D25qSed~EbmhOAxDI6vx-AyQ3`PmGV#Fqb{Ao{@YV(Ze33bF@XD3TGTA zT4_$;YX1r;Ru@ngaVjfZcv}+*vY5ax)j6`bgZ0tMP{EPK!$UJ1V)`^*8fY&UJsSy2 z^~&hlUvm(7$-@$JW!W!DWYF;Yk8SqO`29)?!{x579%xB{KU@R_1*r!UU6U^hUVZgs zPrzH(!&jRAMieaR;D_VDp6c-~6N(f9rguNN5fxC*?&=a7_H0bamvlxG^& zR2&f?Qn>!$)7jqTdq_BG5IwdD>5=+i5v8u51UaYRoTa$+sC9+hQl4DU6f{~)K{PFThQ`pp$-*b8F54VW# z^~0rvz0K|zBwET{WAUJNHu>E0gC~p+h(xRNqLhyLC6UW@7M5V1ul^fBeg-ocQ5`dQ ze4lxv_v65*SZx7%oVlqBk{dTxOM&33EK&QUe7*zfq1hZ?8{9^m?^d6RY2sj3JD)tL zsSBD5;egftTRRD(k(kaS^*4m$Y|M8Q^s1l*uI_Opcx~klY5QB{XZQf&Jqkh0_gTGW z=)ogQxLd!m9h*D5s?9ZO_(rd%kn}mYeS6@%YfYkyK{hdQ_3R}*H3g0QIBuIoV?$&< z6`Bvx3en~l{okNEdcp&9FkMXW5?MiZb%!HraBMPbvL=hn1H>@mS?TGXd$43|rLz`( z@dCO5{y#*kn*M;5?>BBT9Bk8*1tfqB4&gjebpoB2X(V9oK_vheTMgtXAT1r*jK*i< zAU#!>R{#N1WnW35RLwEnJS21M`$iygQRFcD6+TPio-(T*F9{tT4?XMNMXoN1$TLQ4 zoV?oIWgEBN+;IBK|Isf4pb!3%bae3W!|%UH3au8;S0`0E=bwx4xSaA~m>oD)Z_G66 zeM4L0Nj;8&$pC|k%WyKCVt_!pIj_5U`zZeiArIi~%L&N`Z}gD?)69UcC`7XJsI%6F zEbQ|1aC^3)-k8QjD4Xfs!W)9e-~$%MLL^w9=9aX>oWrXm(CcyO=X~^s8~BAyNTsB# z>|WJwG9mUpJikz0?FL@DN4j+bSLU6>I#PW@81fg#7r+~QT+|1WH8$XIxi=fK71w*x_3?GqShuTaFt7f6%|13 zEAnTUTn7W8`AlL^E7qiZ*c$?{PFfxB8oBoco#w)%=uEWs417r5^jn6=?nYj2r?spa zwiDZQ?I(*?22({9szduM-@h-_J2;@?+p&a@F^wgb=jWH_n-mvj#Wr}J!6tRVf(6Gi z{9l9kKV>PfI+hY|$Wu8#HyfU|zht#rrskHqmMe4TA``p$>tM8Qpuc!w>$gE(-XYR! z{JB!-y`M8MTSxy(JR9+aVI|+twKg$+4UN%MMz&(;Z(BAp3i9P9o2HY|^a{HDCW6_URomU`@yw{*J`j zV5H>rMC0>V-5=GC0AopO2DhaBDTxN_1dt+H-xty=2X&^9*rYf0ol{{A4ooB;m#uT0 zw%a;X7n~S71gu~r;mhFS;a8DcMT3-GF3Wy1`zo`Z0By}qX=(BAie&CtgRyMmr?$ll zTIZ&&>5ua$%a3v}>Bze6~;G0<-^g!6tjeOXk z*DMo9j*=Tj_|YYn8mwV$yrp{9-e^8H=5F^M=xPy~A_XDujO%ukWAJ5zu1a0fPB^}j z!4Jmp=Z_Ca+!P6K@H^SqM%X>YALplhe9Z6?W2$M67b@xyR@iycp3jc(^J4|lN1f&| z*R^mH;w4i!(^Po%e!N7>h^d%!W1Vc39ESDzz(aq0nySgv9ri@p7k(|WPesNA_W~C9 zV8c#MqDggoYqEwTv^9qq6Y05+41DSk$nWdZbGQPxkS%C%AmmeV!G%kkyNTFbDb0!v zjRByv|s{^V{-#oKQk8lvwdWvaH~ifXf9qM z=}3Ki`64Y;M`_*0Y*@!xGA)18O+iD! ze!Z1P5B8{RL&UAx0;|z1aIuvSjH+|78VB3&@zv2ozg|j@%o5R-Y)>RK8q$P_gyl0i z|GAzX5{@P}dO?&b$MaI9GhqTG@mbIxQ@^;*mP>_Jhi30AF4xh2f^czOW_UY9ks56)FkbKD=fgJX;lCDGLR-8)+cnJJEH3Q z!gNV4Ni)H}v$orEC4*l_TU_I<9hMgC&hxB3j-xl%*lcLgkO!J>?w!nu?C8WRx?Kiq zHG5>Jq{VxvsZ^V+o9#x9Z2gV6#I#%Si5I zn$y}n-Sr$C&K#&-_B+=y$%<4-1gkaH-L+#viIf%uOC4x9AXG0nwsA$pdw)ebz2t^) zHt}!~2*n6^lgK*<(kb4Qd@@1Cy;auYc&)+9dvi%OKN?xN#nteRI^4P_el^)htO?1a zN1_40M68Gr>3+UY{O>TjvWV8X$ z=zVZ~7|rn}O!z_*gVN>aXG1>;UC-vj0h=$$emI6GSFahg>`li1UM*M%#yoD~C&VK= zI;dBAfEwCZ?16!Q{SqzL8-WgeCaBE3&1-XDO z@;?v!!d<#|pfv_P)c^VJu~=LU7bHt{_w@AYPJX5Li#+{Tq-6T}Q!2d6=C+4e6t8cIWgQMJsBF#Ua@Ze_o2`D(b9s#F;v$skE2N~Sc0DU9lHmu@sfM}*gYmFplO813Q%&G`N2rbjCzd(}w2DO^ctKnx)#l|7f7^1t zhgDb@$KDWRNRA$-E`!UkK3O6RTvuQ8a;P{vkDp>)k335QB-s2{D=LTIN?*ju0+oR#xTL z(prq}$=7Z{_nLQfk}V82u<8p=BO93DxPuP~PZD!EM2X@#WX@wg7(m*^=NSiB68uROn06Mw^QHP!rhy8vaSSGRQ@p$B^V zdqLs!kY~zGStZY0*AZ2O@+2jdcR>UKJZ>RPtO?c?s~K7qIZ9W<(~l$?jnKg>Xo4KicYrxFPjr;#tIC$GF$A=z=|Qtn7bh&%@k~My7n)G z*2limhB-(JnGW2C0dN6UBQ*esNnl2dCSK*h#nU2dqMa3;t%;CymcB`h!_i z*uSeS?NJ3@J-UGXptFWZA1}3|Pl+w#J!aCRnH<;86}OUtc^bvyH@Axl3WSY6^f{d-e?@2%A9%@9OH;t{`2jfT73V+;B~c^eFW0bEwwDk!YB7_mK7 z^7wdk+skW>U7GXGF8J^%SQnTAjN-`_{uU)3E zg5=HH0g&yKiQg~2Nrswhw75(`B@YWsO)-oI&y&BXM`<5TUF;y`ID62apv&Y=upOh8 z*p4@V@e@ZRa0H8ETt<7B&Fq_MjaEYr3e&{I0h{?`vt8Vq6i%nZ@#^rpx~R0|GeHEN zpx~0}z~Vyv($&f}mWwv5;B&e`4lJBUd+sK!(q?^^b%fv%6V-~jn>>@#Vy(omB-I(l8t05M%|htv#})>!hLA+H-EtDpq*GKo z-r76sa>!nDb{w+72)cJbeT8I+LhCgzj^iQa*?q&h)n%zRdW!J#X~&Y;?~%$tRNh80 z0O)~%dxV=A2!KjT4T06dmXQR+d{HGKbbq=KdK=#$FI?a3@n|X@7;a`b-c^o(5^%n$ z4z_P$by&^kMj+T3f!6!}MWq#rT_Cfj>3o!Ll+8Ug<u2ZnftiQRd4COtzo33s7!*1|d1&(((t} zz8V_EYE?zYNMBQ5C(vNLj``}#JkMu5Lw3NX^hI~J6m;;qmZcP{&?+g)gRLeuze1BX zynT4lq)92r1JZb0X!iS);uwNh?64V9SiRB}%0W6G)LD@6zto#;RU*37u`5b0NPl8w zS#o%=@A|kKQm$H5OA#S*bKjSvd#}(7>+>f)6&oOJawRD%1AwUpC+6Wa!&DAcW>?AO zj4onL?qcqScMMtv@JBC!i))Jt$kD2Od8R>@dX?!D*%!s4PhniZEC2RjX~E!8WeE|A z^~v)GP@yYox@#q%|B>Z=N;ZP|84kjnCHtv5Y4MKeqE#O{`a+Rf>kv|y@C@H@=^h(cHz1+i)h!z_j z!k922rH{}a`H}kTL!NpZ>DyNh4#BPkB$)Q@XYF`sXlu0Ppk++x`A4Yzs4GBNVV< zTRol`@ozPJSvX5Oecj3eDo~Q-@+Qn!SXe9$C?0}>@^*HG+jQ>~{askrZ<;@ViwTq{ z%r>{qZS4q!M8Fhn|I|Nae>Z|09#)6#aL{iEenBpi!qgPX&!6~gX4Cb8J^O^9O@ZYT zOANjtn&v1->WC2ypr@J~E$Nq8YxT#PC7(kKF?QCvN-W7rEGMm2293*M#{TOCu$ZSH z&j!M}xm+|zXqg{1{GuX%%10c7O-3VL-qB2BKB{dL{IuAG16l72cR_#ydh9Ss%at#K zHZP3H0v%~K^i;m@*OUUQqq$ZaM$;igLty_OB5;Xj$@WrZbANfXPi2zCFVKL|+a^{i<ujsX$=0vto%OBzU5j~9w#y#u6fstR9R!r zbeS5}l)^fBW&rjU)D6~bW#7gV=P9mR?H{t;^~0k@(K ziYBo8%mv#s*9&&lPi?>A;&&wb+xidk`Y(VYo=4A{t`--3rcLIJTRTA3<5E_~ocL2= zW&^#f)s=W`t;!?(#bn9X{H6M?5^2GV)Ml7~l@_?*p=~x3Q+79Umc39@N#un#{wqi3 zhK8p7$NOZ6#gx^uj?;;>*S(Tysa!(|(CU1P4u~HS7@&fiVkF=R8@UBdKot8Q_QsUK z!9^YgBeNOomIYe(4CHO1M?15u$1$4}2VnS)wo#^krO&B5k+w*_H8sVSzRd8HVom)d zEtz?a=~yr9r3luU_{73-v4;TcM!i zuWqQQ5^|z*MfAs&69fs#kRyPPj$G}y0Trl1lz0F8c!z(V?9fk)S5Je!e<1NYG)2?E z2@UUE#e26sOBIp(%krvd6d-S@vCWb4z_A2Rvx$Cpmkq73(2m0iZL*kVd^*SDuD8-u zIKpelY!U3Km8mk9uc(yZT!=-+&A8i;oX6Y)HhmK?Auc*#?3R$6c^L2V3H*Paon7x7YBF4wQ(`95p`D}HAttOErCr9(b#MxE84S&>SUx1#T8k>oQ_0VH; zo@H+0+wmz9C^;K^i0r0PGXCx&GA?gq8vU>3h6OV98YDP^b=Nc{UX`xKc^|W--pR-~ zVltqco1^-Mr$`$Joa~NvOu=Mmt5b5OWKm#1g(-_*`vXO93+$f7I%+~^#m}OVO1b4~ z+Iq)qIn_sGqMyM=2I%VQVBzOa{1QKKv;23Q{F^wH<4D?ao*xj6)tGeK2Xv~XALgex zToZe!%0AN<$V;w>Qd4%ip_VW+o(5ayiO})z;o8CSJ3{w_?!wHdd*O9>~yB_8k8Ey8r2zvCsZ7f!*v4x>$9N z`hX#wp~#SWX1d}xK*k>Ox*MqsUEbzgBYAv9IIQra|Hme~xuU&JSG;-pm=1Mc8<-~c zvZRX3F_=D3%eLHt_!C^m*GZs=!V6(gWpeKS9lHiSy=?8X-VN^jmFei@TJ=FccYy{= zS6g-cMGoWEkN!v#*BZ>ZVkqE8BS&P-H!5~pwOi^EM#f-AQ%B9Z#1<7YdN#}9+m8*X zv(4jF3iJN5{o`!^zf(p~W=v;|&Deq26|;AA>2WU|iR(kj)Q}i{X`gUfBbS6&wj2Re zWZ&U~_ZV}cA;W>0`>f=lp8)#&_HU$tr7A5;-{{U*nTUB_e34!r03`a4!|P@KKt z1R5aalOtnOb=4sX?Tvrt5!I$;W{IC}7xLKGXxfvbr&n!qReL2tQXM3GazkM!G<_5O z-xG^i8L9*iUV3o!0jf}e!`))C@dEfNBL`+BR2ReZ)zeA}D(;u+^*om6DD6!_8`tI~ z(FCX$bWM%wdD+AT%%PB=sRcvEE-6)XSbt0!&~Nn+0lyJw#MfJo)7DIF^C2mH^E(4O zOAwQ0e?8pUhldw2?8A_~f%Wgy-)L>l;NBU}sBqyHr8|^d+awd&TBWgmzzm~uZz|sV z99U2=g^cfVlBdE{$|gaZQ;>fMTOQk@qOU1tW_I7QjylWj+OF3=;*Rh>vhtMZzx`xN zAcR}kW1~-ydEd#apqG|F`o;W#0bbG;=*B+|tn5^9lGwu~SY(%CvG>Q`I4ixd zSzxf8Rvo4W%dr=OEHM4$3?!#`S@DX&oZz6wfOl-T^mL5X;{IZSG7 zw7_4G+l-G>|9uuDELfbR*ybr_dL^p4X)5W#)nSjOTCsLuI_ALT1S)3=5zXB^-jcS9 zijt}Y;%W@0y+fN-TgGyU=~-fP@=0yml4qjk#X~iPH#i#9jfU>85fS8W&*gVQ@^gq^ z>;?jkf(FBOV_6puj8av2V zf8+7(vtE|XBj%@Rxvi(ud1y-aYx z%?Awz7>5T}bac-1FyYuBU7xv4rBWcj3okO6Y|leOqx(m+hcTs4?$Bx_hQOtK!c#Q+ z?)~TV0&j_JRx8pwtI-zc*p~JgQ6E`Wu*w2#Mbi{PlQA>u*1fR)=mJK6sGL&HgvUQy zVGaqSAEWYAUvX^R&_qR^obkd_C$Q=h0rFE*fFG+}jKv7uO%cs+ zfq)$hhGfnb{W-jkX=SMPYYT>g=1KJC*?ZC<$Qyd?!kZ2VyY~YhI;b>KMwkmD`l1Yb z`uCvA)mnU5cTC>%zSe0}5QH5p_HBtKEjfcyv4#ahY&m`5&&b0v+#jZLVXPxpd!QF} zBsYWxagP2OdKD)0v4MaHjIc;PrFab> z`O_pD6C(dkR@{%2bUiwVm7G{qq+-o>J%sR^i;JnLtBdOz=N@ke&*~>=7>(N#MFpzg z*2lW@;<6v_uF71Uaw0;VY)?gaWhrS+unOg-rxX`zA;j{1Gz$N2l{$C8030}x^whUU zr@?k!JV5QJ9soS|iX5ZwE5mV04}j;o23Rq;GW+*#{&7jFQm0ffRrBT0WC88 zlRNrs{;$x)(4Ia@o7Eq-R{HA--O~XTxk86aDpYqz__^2T&UGaGM*6TcqWgqXQe0x>euz*i#f zv^GfNbNS=0F28?I?o0axm*d}K>)$*2@3(1is*8US5o=f-z|v0d`JTnbiFcpB_p5PC zmXrzdK?W23gujyiQtJzE*+!29(vJ+u6D%_;@?zys2>G83-t@koP+b^cK~w z@`Sl!Y-x*p2c>4;o`Ybz!{EsV&&b-^+QF@2dV=<$DT~0kXCPIJV89j|voK1bSH%aS z2qsNlm3;S1Y<9|IQHqFH#-@YUeo)c>*$a%%F)>9R+ZB~QWtRq}mugk-iHVzwLN}LK zRUqV&BR2v!4Fn}53d`v)8Qg=ZKK6dl(*S#iv>>%-XG^J`CVhC9Dt>JV>|?zk1LF59 zTk;}8P@Z;Q^gF?E=<8(s&uj1*g?9SFr|xF%kZ8a4&oCA3N7KvXQ5bI3gHFHXdj!9e z*=qk`XJf;2B^^y!w*Af*kd;MGHf#4B2!5lI7QT~d=?R@8C|K1%K2vdVFOgw>^l;t} zzuzI5(#Jbi+E&%&BH@gzm2bYkZ+gDB%UdcE;YrXL%ZJ73(Ez+11V4 zoF~NtNED}z6&nLpKB7N+AhT-eDxa(81D^2FK1`k;GZpIWZ*Bse^K%)nc=L08^K)}# z6mJoyFw~HR!-Kh2&!rKzMn8C2!HESQ(D3j7y#7JXaeom77t;tC<&kLK-<)sR)^ia) z3P)2(^x;o@hc_geI=#9@mF59vW_JC_Rin!;F(#w1Ji6zrTK>nhJyufM_1~j~RW)iW zoewTS;=Uus?xbR5=J|mzA&J3Sr}?+z2v#7$78kc)7fMtF8NF_|Hro6eJsvKd_h5p& zw(m9~!!p);dtbfVW9`6dCJDLd%RN+}lL+Pf!%e3?fCL8(siKv}|CZZb@Sztb z3l|?L6vq48UoROM#rg6%-PRujAx!#TldRZrZRh9VfNbQmgYVyKeO`PfPh!s7p?E*r zWfL55GixT5brN9zTiKm{jCTL?x&bG^#`x{qUr=Rz(;a^NAj9oKRaTea(sq7?#UY~- znNzlUWWtl#_QR~L$bQ|Y$AiJNL+e_DBfGZN_(k+E#>U%WxcO##@FzSvdK*DqPEOWj z-rNq&5q?HYT-4;-009?~>pmbN)uJSJH>}p0C#>^ESYqP4xk*Nc%f=WO7~`OVX6WAy ziI*Tbd%SLU&0$#xiaVb8QrLWuz<>!e`uw7~R9Lomm$P%7#>knXJSn}dq;^lwt$ia# z+N?EVSuxLKVB3}7HfP&0op!T#^@ z-$E!Es1*KOWBLMdTU#G<^t39p^GnL3bUwUq^BF_X6kkyJw*kOFa2gUWJf*6jPjx&q zNhc*P%AP<@s+8L2m&KwVZRV#7OO24$pSm^uCFQv@QPVf6c8e!Q~L(Tj>4 z6UGB>pN}w2oQQ?kqqXXG&zocd^=iU|-=#leBjFdZ>i-=aRtwoj5<|#9<_Ltnmt)EF z*zJ?SLxKYoHogTiZvmXz2ZP zhs>Mmn9hDQg*^n#4xf&(2R#4UBN)JJsR|Jg))lE%Gpo`H;AOz_!A-@*#qPX6#l#7y z@+{NwQZ{n&Sjse{u;W$0#d_bTNi%%w!X)KQ9VD2q7#JA%;DJ$3iH~1PpZZQ|tRsBt zBjAQ{EiA45JaJG%D;65*#%Y%vRb6t(K3-Ped^i&O2Togd@R~q6c43N1TnsaR9q9Zu zePmJMiq9E*+iSm`8Fh>FBJpz~E_G-Ne};H7UHELiQeVal_#Hj z$y&!7`_c4)6JPs!Y7#Z^?oG;{ki?}jmFZ(=g~sxNUC@sxS^z3i>z|UIy$A81TzzFwFdbaTZ$SWKzcb@URF1dMz`l&1y zjjw-kcQ3l5n~ZyVscbiG9$~*YTIqtHsJPY>eKfdAL`PDyelUY+k@_~-=QUPTwe@Ij z@!FYxYG&(u^{Ap~CH?KSzdjK|!QTEvAfRwGmDgWsTZAj)_4B>Ck-E6CMoMMTekCeN zSz^V^y{7bxyx!g8Lk;raV7^EeH#b*ap0Sf?WU8fc*(lWj8PF;v?RLRZR8$$Wu#{8u z`!}y!Xw|{UMy}Xaa!DQmTzRlp$Z0ri;lgFgj7ij z66(@onKaJj5d%JdbNpND?zG>vGiy<{OqNLfr8@VFXR{HeB%*f~S+>^Z1(RlHA3q90 zJ|{Wo%#r>SPa#~}+uNRKHiPE**8BJY?+Dy!WPN?gYRaH%2*%jh`_JHR1V%(y#2XD1 zy5V2%A8-T>=<)HvNQ4*5PMF?gP2av?(IS8O5AM(xed>`o4pKaw$BiN=G&du;w^tN5<^5S#4WC{w!;U0OHHpx-;ireKVdi zLaouI!;-{;0v88vpq7OdV|({kS}G0>K*U6Q-Ls|>p|(uWVFwS~!$8L?w{ORh*0Vr$ z>VrKu&gA0%?wvS44rI^mn(TJ7SLm%eca-y+Uy2|zxQme0w&oX`PhXNzUD%sRXd4A4 zEhnI1GHBcen)B3pA8kFkf)(3W5V?AMuH23m56w4lI%8k-1`GONLn4p#czydERMq5K zczmDyQ>rkxcVJhpU;zjLfoPhfM%R+!s>zpbPC%VwbYROqCO8p8DoCGyJz$y5wYDLb zEF0qW&StIgbpn(1Su{AvL$_^33k^q8v#rcknmMh`YxQx>0FndIgiLA?LA7<@=mfW$NNDapKZDIK@v9uGZ1dOh`olpn~7Cva$YD zQns?T@kvUK!NVMO$fSq6W5)XNGFJGhBlDr-o%F`3Tr`}J2|)OhZ0 zm?|R!na3vgZPSheHXr~mx4C+1Z0h^MlpqmGVucRBOAawX^jl22(_XWdkj@F0c+oV2_+si31_&(3k2W`RPTVA0a4EhBU_QOvxo zR~zs>U145hVo1pRFKdJIb4#oFG}O$&YP%{sJwZfY`w{tEP1ist zG3JL(=^Kt2OH;rT#IMIh3L)kAT!{37Ukh`t_GMt+gQ=jVgnxhD_1$>5<*lq7)^93j zJbrBKH0hfq4Y0lEq?LW3qeBPw(sgeFA;j=}`IE{KB=g7*ljK;5yv`6lLvm?hPHKRI za{+UA=hV^3gw5lQ7&-`A&3dKPVj9DQVIB-St_w@T!9lxwM(Z6rHVw|)K&uA<0VXyC zfP1%a#U-KUOeN9cF6I8wWyPWa;bE8miNM2lwR0 zA+vtWu_s$M#q`s2pER5u9>&EcTt$0Ds$Y(b(9JwqW`MQZRBXEs)4x(Mt|o^{US^tx76KRr~dwK$$oSLMS1vO13JdbL1>v_$?#}% z8Vum^(7>Q4%tQW)e4)m4{(EL7ITTYQ=%M5grvOZ-f7KSf1LT9=1EJwzt4Qx^Gx=tx z+%6MZ_@?(pb~yZeVz*&~dN;|CC^DMnQ*4dYlauxywy6+b+C7$bNR~-+3reV2$sTkJ z1aYfoSK0lxAeKYRV3$&1e-{40dD^O?2RE#Rq=UAyk&TI{p*L@bpI>ToA2i8U$`z)a zQ&f&TWkqICf2)^*g6fh;E>&sCK@NHC;lVSrl|C1V!~_|i+fj`aoiLh27}Ui0W|ORgSLF9{uh=yxMT)RoAKk6mwSsDD^DzBexqVSj&eu=t zy|Fcz_YV(?W=#`=QffKff552`Hhb6I>20$L2~e4*jNT%d$xvaZjC$VTFSWDsQ*d+A z&*@*rhcJ9zL@vt9G4Sm+2lZY61r;Nwv~YfE#%XU!1*ojoh}1n{xC@m~Uz1n!)E_Q@ zkxE*&S-ib^k@3Al%6_qQJvMU5(`j=*(z!cQ+1a^UsbFS4Uq?n&m@KTaZPz&QR{RWz zi%Vo*E}wX zVu_8Ybt|+}8&EA0J21O;sR2e)LJ;P4{(#{OK54Hrs~eWL=TtG6W%kbnS8;LMY>&M zFV7|$w6AJ+ZIKJE2-$eSJoKseBeM^!RtkiVSY=opR(Ww^!ZKkHPC$mBwmKIW93Q$mr=zT1iq8Wj*B3lkY3huhV_nudUM{X>*P%fB7)2v@W*a5%fT~ zc=QP!LtTbh@XgvZ^`{rZ!lJ_+kDji8j8N|H#>SEyJb-Iy`RAqqpuhARY-okXeDx4g zKKQrmzg~cPTNppJ@hoiV%&BDvu~%l;2L%;fB!EFEfa((hQpj@s8_^xJ(?YF+lGgIY zRKnSznxi2}Kb93EDnkrm&93jxd_ez}%wFnx)9EcARaJg*SZ-UO*!G@D%}8jnf}Ykg z#TJ--Li$yg;5?BVtJGb_eVi$SGW_cc0qzZPKSE$c$1D=VB;N-BRx7YMfc;2(QxlsD zTFT1y`d~z;H<7rstiZ-VXoHO5@j)9)>kKOIwWKJM*HaDGTXKg{d(ddY{@0(3;t45c7 zSZHEyQKerl4I*Oov|>ZKx@5j$ex9+a?HZF)PiW%qZYLZc2ssGd-6bU@&Ny8li&IeW zBm2hpWs1N5T{y)@=H-oXZ9Zsqe_?r2qKXGYNs;Ns$gJA|!`TTKUn}%#E1Rno)rEUp zhU>qPh=TZhrQM;ppu}P|C#dTJ-pmX2E@BV$@g=(JzPHr+!cDOArRCrM^Y9p;qRh#H zBd8NRs4iJi1#`V5$OPn;nuKu|0a4fxlP8)<6-s5BmlH{?;Z^jkVzMWs+ip;Lpzptcg zz^>pikK=P+GLd3!j6dOJv>$nqlI8T0{_Zqq>wZaDg>{3YFMqFVvc8VyW~ZR z^Elp5$aDaKkSFV{;I5GnR=!Rh&kgHSN;x9YWdXiut`kv`XZA|?cj>3|dfdTh!bB#q1J|0C*&*K&{P?52kLhc>6+sC55dtu8k^?7`h*_}Wdjf@-s z{&-V+*yC$7V?YFQB_-R#5^0kaRJ z=X)wZy{yu{ed$D4--^qzZ^iwp5Q~kKO`F?uXz~U@KQ+Gt z0&ny$NQT4_G-%D)Qi6ak(1;{u%&JB=P^Y{D-vJH}x~9eqA-pbW75MsX*#ybxs{pDL zRMduT4++C~F|(<|kNLFM)3UOB5q32yDhNNY?{fA=u>&(xb93_q!jDY-nT`)j0U@+i zbwY11>2Hiwx2Dbmb+fY~K`?;4)~F>>!%#I-kDkMEPZ%KoT#Q1smkcj0qf}E=`3poH z!w^5$oy6n`xhGKozFi|Izp;T>;GBu{elXxEUqnQ(TTsYJOr2Ku`?ikfU;CEQ(&3X= zAv`2CFr;%G1yR2|ejq3q_EO*Ov1!)TcfoT@Ls=Ikwb7`$zIMKQ&?{6BKD?Vl+!j;# zMm*H1m703Y{(E?QG~3CA8pJx-{9Q{X=yx0wKV=FWql3oCdbrNit89cqJM=6aYu?FUN`SDYdK*1~&*4&92nC&>-g4A} zDNI>e{jc3r3h&c=7HyM+L?|5KE%aw?F6L7QlDEHG^q2mSd)<*b&075fhuceMZWoxR z2N3r6i-k~dekkU%cBNwD;RkXR%(_p-yc(2lG(oilHW z^gt0|g7uCNw*+nuuG}R!KE8G^vDD@D70s6SI&-t(w+j{f&jD}k>nUU?$b__kW8{VPuK68WR^>Ew2<`fVRkgmDj3jm$gjFio-FQ+a- zLNk~;=4IAZ?GPB4FBQQc)&yA}Fel4I7E)Fo8Bg-QfB`I4%fBJmz3(6#aG*oncj5}@ z(q9jRemNm($W2rHxQ4e@b@TY(14g_N=9JU%>GKWK@|LSd)jVez94`5YX}F7ZNKD^9 zd_8rJrZG(uAD3AHFb6&1rYrmf0N`$JUjyqBJ}|Rozyl7K2w;M$wycGuj}y5IZf~&4 zy_DqCE%ZWFLgl1$ZuHgl>=!FDzt?$(r)gN(YSmTwQUF{{-Z9R@crp~dX|hynMaydY zu|=Ij!r(FI@kzDZJno^zq%QDPx+kLb_wOJY)uExm;hO=eBTLI<3I%^;@0fLvx&}|s zW}nyN$(p?~YgNZ&cf}&0lcDns*Cu9-<3*@&oQrdTaHaV{NO20xs8Bit`Omn4%Dcwg zu5GG+tCWbIYF3mK;oZAhR4pOGr@Pu{vA`0HJ8&4kH%W9@rLc_iqSYjm=HITwl2a_!; zBWMuh2@LLyhmMaI@ZPSFKt3D!YMc{Op-xtfYgYOhPyc>MB!2Y)bL+t5eRF2h-c~ON zzyb${7Qq@s7eXP?AlvSC^SpQq72-C7v}0_1xnQ$?+c4k1y#Ym?0A`}J?Ygk97k5kZ zThtUJ_yCx;Rx!Zij()jm+ghAld%cxu-Iy^?HaV#HI!)jy5xpF!sVWi>-rKsl@|<{s zEBr@DBuB3_1uKc1e7b}Gjuw`H_!A+;kyrI@D)$MdI5SA(Q>!Kd6P0iJAzg0mk6cl& zdqyONwC4oDzA%N+cqPVHlcg2KT^n{S^Go*WWe=-uXm8XAbi&Esz=ubKV$U#b*I#sS z*&r6v<~{VJn!KIrW!(%ak4ioaxfsoG}(93AgJuiZA*iX8gf!1-Z*tK#y>g1b9~h0JyA?fLIgtO zvV@GbNk5|kp9KS$*E$H<9A*w>(o`Oc)#4LZnsyU^#=cHW3#sS?@Cv3?hEjr*v9KaF z9myZV-}f#|*eAWqf$WwT^nhOyu^eGXxQM6!3j>~Op0>*(7Z8BWk8vG)j8%)3gZfiOA)~mEr|TaYeKD!A%}*KjgNl2jdH17MqUE~axTrycD9H{vjg7Mu}Zp>(h$PEa6wU(w`Fwo z!|8=QKlu9TYnJ@jviFa6*Qs`38l$kZT{~Ugx|*FYPQfC9ZW-JFCd>}EI1$}Z&Ya$3 z`S8nD2dx@>!Q#4{(hPtlVd6F6xxVJ8c3d9%fLo?k{M06*_pTc9%?|A>})xH<~=M_7~IGj zB0z-FehmqzmajMNEx?=9EcWr<#(v9Qwn|hgRu+5Woi-W8c;FI+4OOYueVl6Y z9rj-K7aDlf2AQ(%&!2}Z^iqzghljy~u|0(|Y+>oUxXnNw+ggS1P#TBPgQTEjNnI26 z0Cs0+>p`)TJW0&;}JHd(ijPdCc!p=d#>;mmETWwXax z{jp`piIIbj%nf@IG0L{7DU@HqEug?ybzD2sa>{;LU4!M+?D6&ra$#wf#*tpLKGhw^ zl&2SSCK^Hl81m3Tfn3~N;RuKpB_%O26o)T$3&=U(`24u4-uw(EIz08udoMeocM@UZ z@i#%ks+G2F`))bshPoi&iT^QT*EWd}k#t>eHe{?#3>7?V2AXMQ{;g-NubJQa7)jGW zf5d^(m<$q}B-+o}{-X%t4%MpKduLMZrJL_|R?@vSNpJjWnH`m1cemj;*lkl?&Wb(u zDdg2B#_hbWqB1}minn+N_(qb0eriHE2*1ro)?<2P(7Qd1hK2(gOyt#+WbzefeLSQ= za``8xBdBY+74wXHpaLK{<<0z<1TNeUbabcAZX?;bVAs(aRYrD_fW>-bAOj!&p^0<9 z7Te=^P@9N~Zq(53EU|!GODdx8kt@OHrJUC-5N8=Rml2RJ{sb&IZ!e^fgw(-(sMf9r zi}Jafbru2__FY{x582Yt-@_V$0hQ$)9#RMz3d%2eN}%%SJl5uMb$X&1zr|&#%!1_O zPXj}gzBRSZ4Liv1-{Z$*^Dt|6wP2qfFnpdHp9U0w{p05n%n|r0h2)XU8vE!MW6Z*W zzTl$sFBr@m22i%&u)oUKvQcA*h(xRX$bd36dFgotFy_5mQXn11FBP>sK>p zlfk`n<(X?W80xRh{^5-0dZD!qRIDm0VC<2vAsCjN8jaYiQRH`n3#l5mN}i;MO9P*J z+aChp$+XS9`3#$(tEY8!vPfDS*`xO%WRt@<_?DlA_Q^FAW_9{~{NP6zRPemUxfviiu3dv_k&P z1uU#A=L4OwQIvN_cME%KU1e#LRz^)J*bLvZ9#`5APLCMqNS>fxc94v$KoX94J^DHQ z7#uh-QVThSK>7V{^)lAb@V2Jo3Pe$mS5s5#_*OR+4AMT5*^f(_|F_WJ2_umb zld(qr}+4;SS4= z80n-hMgHJ^-=_u(Qz~~KM4DdFE_-;m(|m>bCP&ZFo+G$h8=-zay*sL9v1;>0Sj1lz zb6`Y2G&D3gA$(0SO*iOc%QJ8t(p&ORwiUQt(1 zJ2IS*7<+^8-XwE3EZkWub4fD{&~LH|BAXuKD~@~zJU>6vo?i53Az0Jx{glX4FznN9 z@+6FY+l;6FOf8ztvT4_Np`^Zr7CFcMl_H$~Pv>%I54dJ9KGTttuPrQwT?=X}#x2UA zgU-=Vd)C%*z3SuY2s9wC!*jU16AZJke#xOp@QjmfCsFm6KRLd2gh=LphEJO?Ps^*l zKEP#1yy7=0%+GYH%wil*KljBR*s3_Ys`>R5kXKdsuyq||L-LX2kKn`oy>JT*kvRF)&Orv1KeStvvHFLZs{`CB>8W2}5?<)V$ijwpd$e_EEQ#SeXjZR6I}V*=q@*)(S9R6IFaq+)8n3kmd;MC zFL-0s;eke3Ed-#b)o}3~H#|6kObE&wBbaokNihRs;vMc6f}m#gI*Q9$2lwlY3UIr) zreZF{`QkQUeCCNqcB=hFTue<^IbQm+NN`Xk^q+``aN09s&256KU3Pz`y!w*EfhcQ5 zrs^Zt69P5^BTL6CrS%YSVHXsTp6OcdJ#g&9JwA;h!vi2I0RdFiIae(@8AwGu^SGux)!Df_a3EXtnm>*Sv9WNhH6txEq$lix^@G6|wb;bsGD5s@WRZEs=TP{l_w9jZ zbbf)zd!x1IrKOBUEx}8?aCiXlGgB`DK}&P3)6Ui}ByXt6N_o7n>!0z-i;t0J_r1?v zi@B!|fQ&%Z(jP?6cVC_r2E5(~09Yj4Tf-J5%J{y=N5bWXA|l_kQ3nCyY$;t=3K`~u zt8#_nnd55~V4vtG$e4hV%IY%1)t^)g!0nY~!u_q?UnpcT%MKbi=?X|$w92Za1@DbA z?ya7QH&S2(?^qO|Enro-v3b#vL5D-5H<(v>;j&Xy5ebTXNbh@Wh7bx~yY%he%qY(* zSz4K)Ec`W4ZZ76*UB(~V7$IADrd#vq?J%{&eU}`7Qd)FpX;rj0g?JlP`Bg;+e-Lqb zp_40TYOR2>xFplLqu2N@d}`{PBHH)T1RWiNC@RGS<4WT|f723~t*F#1YMo0I(yF0Y7+0T!t8xFMc6c|T1 zbGzWKwyg)+b_!EQQ!UNF)u+efzNtHqy``uyr@T(5a#Z*KITxuN4Aid-4ws(7nh*+Dl(9yeZF6sPYk7k8{DHSOn*DCm*R zF)~@qcjr-M(XBjNpGnrpTLq8@Jbjy>B(euX`bpNANrkD4%gRbx1yyw+E;0%fi_8jS z^2iT4X6O(O-76HhsCXFN|Y`U9{URO731N81|E)f68V;fzG<*U!wm4 zvXNJ9TTOPok|t;nclO3rZU@GDKS4_5JjzOJgM!BG&nVvIU}vETh?8_>x+ zIhESFJG`IBH?dGmkG(n)2VH&2Q(LODZEzDUdlc>UqPIO49%2++w&e-Fw8DP`^H(a8 zTIC9hOQHPitbQCS#c4;At|}-=J+2r z1H%ycPgbzLuO1!}`;8Wav4;?D)y)jC4XP31)g_;aDacI;#43HQIB?vB{Q6v41QMO@ zgJLfkh?#+*G5SVP-~I{2Jje*b4b-J2i`J{UItt%F4FzbG5eI_qRGC{fqD8d(hPW(jAk0O5HVl^wXoitrNNKQD!Y!Q4r8k)T5;{PDC4#W?b zA>*Ydq-rY1$mJ_)G+O%Dxau11M%ihP3Up8AisCr6{RO2i@D54eKU?xT3X@=St3Kl9 zGK=V`mgZ_mP~adWaCc{6XVvxn`_y^G#tlXBQ#@0x;vE9KH#7MyL)^;5fs>vs11C%` zsM9`wJrlRK1}#_(C;a={`r{Yr3?3_-O}1y3&PSZ&f^dF}sTKht2kOheHyc&dg@uzt z>@eS{*)P<-T6g36`1mwDAuD{RULa{g#Xwzj`HONzkF&G{`jPlAFQvmH&8=-j1I8Kj z8VJZn$g=n{D%a6)9~`EqMAXiBzw#0A80>YCFf!=HC&lBfw+!#yjuQU~X<%We=dbDs|kqk(DochlQY6}K}6S0W|?Qs%&dTfcl4 z%kC^5M%IR})n3F z6~1+WR}=M+k;ZKq#7clDPm_Z98h;UG$>Z#1$-o!E5 zA2^uLM}RJO=VxZoxeKi9-@h;ZZqz>@LMth$?2e-i3|p@>WHq`KKPD?|`b38gtKFi% zs50+Z4V7{`w*S%RVY_F1R-mMzm-S3z@*Z0uR4p1B7;5tH@D*v|-~E8a1N}1pviShI zk&awW1-}=8jrjRYm$Y{WJv|{aQMEK%%XM@NEJNJ<_z}IBx(1RL>8?Qp!_UtjN8f^M zZ?>Fh>MwWh_MFgi$4TAIQd=9ZtWrut9k*$qVe$&%P|(Q%)>Ec}A-5ipmJf5AhLXyi z|AUh9ZVazyZP)#tT)+USqO~)+6g_%R(!V>QYIPgEVD6YZoEL8%o0v=4Z5h){C$;cR ze*S5c_?h|03zBceRD-lhs9~q{WOU3<_wVTGKD!(F(|ws}$%A&P+Es%}*iln+VBSS! zgg<_(=)Akj^e{Co>`#!|dQXuul9>yl!>V!KL4gQ@L`lKPxeVKQWCvMHP+7jg864 z9M_b;P)_783k^t#No!m}=UtwfJ7@=F<%T8Xpnzdp@3wZYL~@+Z-`1v6p4Z*5)k@i| z?VG(+G)3z(q@%*zP;vh1DXahDH?Q9NNksaQm`O94RqEYKFu`aXsMx~>*iUt3_SM+J z1g{6++%1DF9une69qZxN4LlhcKJ1nhTAVpT_~*$AFk1y#m={|6(NR?2Yd?CrGEPOc z%Vu18$~GCzqp=pny1L%)nSVh+&sgf6N`f!aOJ7}GF}7SEJNw0+Z+xr0%;J!ul$6e* zh5tD&o9h>z_KOMBMWLqzReCb|N?IBB_2SY}qZ;krsqa=ib)>8{&JU8oG4fZ5)>BE^ z8&6jNA{m*5x#gvJN7}rtl|}~y?k(0U%Zg*iSbu-W?;m&Yib`>NvJ(wVjIt-+lxXd^ z9f!soULz6rN}ecfFrv?!U2cY`_F(#cu9LSy0Ps{)xv1a`JRBioo1gy(QsBrLUKl)) zBU!EfCSfQeVK}KYhw2Y20>uwJB-hgdI80L(;pCK$gdxIi#wO4}XMevbX#czp9dw+U z&NDVT#`00;Uj8gn~>W!op264<$M^DWL~X{{3q0 zn+e9oRj3RQ`j0E_US3{pqoSaURGxk)`-jCinCgoBtz~T~0QQXlww%}OOSRx9Y>e~fCsZBquUiVfQaVK zI`+N)SM|mfavIjUlGautcGJoz`ZJ1xqTk0C94H#jHrD1KG-pfZ5s?PFTY?@HlusWO zcen4lQO*7~uW>2rDo%kZ&ee-CM4U}_O>Qpe3n2RqM2ZS^mlT&+{%YsK%T>yeBvx^D z26J0+mvY;GJdQEM=Y1lScUSFV+)HvbzF<;JqV}=ZURz`NH=ADTwYPzwhCOQLSJ0jw zI5wa#ffn2T*6II`{C>l>;x9lZ$3ySzF5*8%Jm(zk9E6 z`M!o)&$p_Rf+?y$zg))}y!+U!Ejd7p3rWp;9p#Te_P}RMw!}+H?t)vv6aW}Q+V|(Waq|!l8j~&o$XSUlKVl9sI0D`HO5u` zc$PJDgBg2fL8WzsnVtM_unenO1sXNYrxR61Xc$YLHZ8S<0S-=tj&O@N0_>8!rw{H= zpjOGoBx(d{;M3m5ne{XB$5DpdHz5KLQ-S*Q8SXroGlY1l`VfF-88%uC;oM2kwJ@

q-KsWm+(=a`0B+xlWq?vwiZ_k|^oz2>yWygZNu3~ZET$!vZ)uL6}$mH0%C zX5GQ@!LiYig-XluJAxF^{LlXVlQDgiTe zaukbO<`>qG+`g+H1iW~Gq8Ef;?`D3<;bCa_6=*N|SXCFWGUoI3n?fu^K!}-{<#;mp znX!0f(lUCR{g(VLAv(dX_v#0r4_o$eO8A(Y-Fkj{-5%^lr=gH$6x2a=WM&>C;Euz?L~@_Ru-Uv_pkvEl1PNS9-x9eo&^gkD+G&8FTaM(dlq8^v$b9L!mzFi zrP+7(z5N)^`7u8@*OgPq{a$THyAV!@i?z3Ns;{X%NAN0PmaK>rUuiT#O%QBC!|En$ z=we7%8A&YhY@v^f@@chO9g$+*Y#3xDR`LJT^ld6BHXRs zmY|^)WI&(EhJmkwhlN=8ymz?evQa?g)4u&Sdgpt42?=xTMbHXO+CuaM33nObDJ#xm z=W6YI+B#s^^mMtFD_GGlO?otzlyE}dMx`qZ5ZlfykdLHJcIN-v&W zAe>s+!jYd9oZ+Z_|Nb5A#}A111u1Z-QLvvUHvTY3Rz2^)2`^>jo@@KJykzzMm8tPXnBSi|x=O1wQ~C{WvD<-f8~gi9 zxa_f9UXdT-G6`gsT_O4T!TiJhz@WXpesf0J|9kZ3Sk%kqdX$pVB?=j0IcDOtWO>hM zosoG_W$m_V$DVi8$QP0J`1p`bO&J~@kSW-Lw(Iq`tyNDlnG3D%h!Kt)?FE{T&x@e! zrKP&82!{*-cv(jt($+lw9Memm>d=)mTWQd<&&asWk0_H$O7ao!cXk@$UO*JXb&R9TGB(f+uS+c`NY)qG<)zA{O&&|X(bCHO#va%3OI)cR)1a^Ed0La~vy5ayurB7<8hbSJK9j$eJfDpe zt*k&y9hzeZIUhuZ;YSN|7i&2qAoi4@RJF_^0JVxhzM3$8R;=#m$)`DFQ(iG=6eUJLYBtNTP-)MiMJ4Uhdlw!Si~ zs%{GxM5Pp@rInQKE~Oi!JEgljlm-Fm?#?aUAtBw}-Q5j$@_qN5^W)zA><8Ba%f07X zYmR!yJI45-C71QoTI;%MM+nu?(fO84s;5WvXk{9hv?CZz=~OOwt7)&_Ju9C6}DjQSBq(nK!Ho{J}Yh0$1W_7AO(By4QSdK<_K$ zyQcSwuMyJxB+E{gNJ}Qn$b#l^^TS+E&WaNa3a+!0=xYSgSCrAxtq>+V6eWu5$ENHp zi^L!NagFYAjr?)o;KvJt_^=Lq$ZL^2Ew{x9(sNcJ=IeqE7{bC_VFTGun*4H?)4IFf z&<9a@$6l1^*YP5TK~bX+p4+$kww1($ZB3+p+{a&qbl#Q5dp6g2Qu=?oJBg9C%LE}OlK@06YzoGvX?wg@p6 z<|582D$I%sYiqQ=s`VP|ZT+}ckE6AmZ3yo~#shnA-ok68gFy~=fcX{Y`S=Ek(f0HXPbO1Ur|LBl4*fS{ zT`gKD=5dFutXIGdE-)~gw3yW&GB2vN?XB~XG#o_k`ca$T9d_v`W@2Ik4=<=0__T9X zl38x6BYD3g04o4 z92J~ZPV(BWmB0GS=P_8hVqU*=(SGxu+gFmv=4Xg7SpH8VCL=BSq%paH?HDR~r7+TR?7LAe>67>h3F&xwHc=ZWV)flM{nIb$ zu1S+o{aRZ&AWQC*tz8Ep&wgYasKVWjjLYkxvVZ@Re1HiSN<%&AFCTHkw~gz>B(*q* zG5gLND0_+m;Q*NO0OZ)Nju8KsQK5MZa4)y_eN)Lbb=O>ZiORSsai&H-+B}u^-n+Pe zV}$v98(H-8ObpMDU;b-`i81T%#v7Yjr6ffn2$A~1B>k~-Z|nB~E=OqZB?FC(P$x*r={}#GoN&}@m42f6 zBA;X30W5G!&wG$yNPlgF`A%J}dZrj;{r4^*#1%yGyhf#f@5E(pkqpt&zcf;iSC$D1 zZ*kp3&%|FVclF%-JHW(aDM$_HjB%!^Y&*zb1Tej4bE~REMoq~mXvAI-Bq1xBpYGRj zHxjL=XvKYA1S#neld-ZiMU8qlg`ac9bV)<-k&4{wV$o-}w|BnZ9<@wLOwnO{+NM7v z<^1mspM>X>thTa}#{B&B@a#O%Cz9-08ISEi6l%11u$gTcN3CFAB3vF;g07}_d#}DqcDZb z@nsOA;-vn}+6w>12ZdmXu|Q%5w|Cds)A1?QtoEE9>wNl{)S7?3+GZ7$(9lwF47rx= z^`HSprHGS73;w%lKo4H;j?+vYU1|}4;|6TmlOa(+Q)k+u8=k-o<$eOheF59Zv+o|= zZK)IDxZ{;cF^Rp~K^p=&qfqH3RTw<5Uu_sVdUgS|+O7mM6psnhg%HEDA9T?aRd9X} zHEp>N7TgMo&C<>qTEr$|7A*g=D8_6@`m>E77uucN?Tcy#LM@~o4<>t0i zsc*8FiIqj*NMA}ZrprKC5Iz^wfi&4-Kppdz0z{)vLSj=*m6+fNP@yuyA2g8;^JG7* zMn(oU1p7v#11f`6l=TUe*Z2AP;AL45;tr(+qx9zK8Hi~ZD>~g$!|JmWY6GQUaYjSh z#BG3#A8p$dm?fufeqh|>d!W0rZUE7sZbzXeTmGm^G)2;`vJfxe?doc9~Z z9%EFSw_W(wN7&e)b`PwWSsbv9ct4$y^D?SRNMsSw*ESiUgfcs6`Fw{K5EcEvrp!zd z&uFi5&?%vC0L_l@YD#D?GIBE?7ZxgWf(RRn94~0R(V=0yBq@?@!7oc2etnaffL)R? zsesjka``0YF&w)|rUY==dL@cMbvl%H?Qr4W2iA9R{g>8Ah^E4S6_MQ;ZbMgO+5w7m z?Ckzop>lhQKV~sKyRZ%Wwe ziv@k9x%c^r)0T+|D6U%Txe(H~AAJ4*>d$bG?vjqCW?tWOuyGfgO;~%h{gncB4TC3Cnr9R6eKuZoo{}AETSW?;(kN@UH>}8qkYv;^vQk0HC?L zL`}DFck!`&@0-D*d?@!YFTW}LU-UE$Y8KjVEnx{CW}0hRul zq4jW#1>}u5dSkBV8%X@Y*&-kSf{FtRizs~gPbczEFIfzcVllT+u{`Dl1=5bOpy-t< zwC&`H2?^6OgT-O%nhMIG3v?LW1}EH*hc8+{6~9D(uH8P|k1KGd(0=%EysY|?vi3uDwHl6$@crM3s>=8(fdJ_=gF)pX zz=dDbhO*{eUfytg1=SX{^cIgXI>*pOEa)WgaF`5;_9!Fc!Mfx6Jk)1#wM}W+-vHi) zTK3Rbuxt4)O4&gIUZi_wroR$`(|`R^)}h}G)|OW^Fwj$YF5J;uUENP4U5bOS|IKMX z7J-(sm{9k#Y+#FGz)R+qlx#l6kJw>kk> zD47pZX(P_-Dsh#Si9t3l3d{n6f&1$G;B?3sN_9ub=7*h7qTa9YS@#B8-V`E-JEb<2)wuecPTjJ1L41;hu>v)c*xEHO4s zIsy*2M=jA>|Jc~lR4r@wRZz~ij*iX`b00U9V8Z@w8O#k`B_bZ4;Rpu)IoME7`a17o zuP1G15cDJ@RBdb|s^=p9hN5iK!s{Y{5Ht)R81&?!mvswD;a!V z&`&`L%&_wi0bV-CD<@%*a6dh+49sKrl65NB@>w1k+NP(Vb02f16%k+Gzk%eS}; zqjakiAU_B7fs2M^?ZJ&Rh1WQKuzf3{{{-pX!LJ=|J5cvw-FBV@ETId_9T3j`^&ifeI+iHF0@$T!y@w zs%oX!7=RlBCVvkZ6aOL!1Jw%3z$gIRKshEjXz1_-jG!0abu63M>B$JPmb_kV+n$n| zB;d(Mf0QjNSEy9?+uH}phzxe(Jb4i;sOZVr%OvivAGku!UaBcn_AG)wG%V=+0KRN* z?Aa?urrKnmb3eP$t-*J3v=Ap-=+`N-iUk>LjcFq!{@&ik&hNdnNng3 zU=AJuUmTAmnA=WiE1rS*?OXpq(YR#pR0S@sUo@B}nyx&qoDM8pk&K!Jao z%_c)pOpOc#9KfXxzQLnCPpTb8c#frUuWx#$TO?X$$lKpHoB7{hEwKOee9CodThfC$ z+TX5qI{RBFr9K=1HYy(K^3m2oPH}Fv^-Zry#bei^=3m7!2OB7@fI}o9cxZTdQgnKh z1{m`u%;1I4g{i4S%O*-keJ8dL22elj*^-_4-h_3*;34TgrKJ(FjzJF=&$vMrGOoR> zU%h&TB?Ia;ED-0=tJOkrLMBU{tQKN@>*t-rafhl+WW)noJT8K*Pk`9y zQjl}q={u{KiX+HB6=y-Lww6^^<8XSsAd7zf9fCq&n4+1fnflyZh)HcsN(`w>8tJtV ze9kPb%VV5$w_vvIety`IWbQ2YSdqx-{Hys<^*6nOB4#w|nXc>BN;QW)&2}Md;>xvN zL+N=&=T%9sCLIbd4!`PXvl&d9H?1tXlv>(;{3AXE>1jsXGTS2=N=-UBc#n2Ur!{Uo zwJYGxNKPz3#_;8$rQHDV_Ev7l)Cn z>qe!qsakX!#ezT8Ci9jf)Fo zd3mJruA$*K=5pq-(GhSJx*`yQYZ_SZmW23`lHkXo^WBKwo>8Aw6QWW|nsieCR@0cj zjAwOhOh4b`D9`vf8-n>syBYoBSS&-5%p>2J7GRb06A;OTP|jVI(F{ zXf|a!T6@hm`v`v9p8Np$jyTo*$>CIC#e(uido+cSg7HKA%~$;gwG9a@eOgUjPX!Je z4YQuid@P7im~T{^a$kQu_^D%nU-R_qHq4@TYw(zbIR_0bCkj>U!}89K!5f^&(;Qql zl%8fUtLb8tn))x&A+a0-^F?p5-ye9Dv_6{g%c4nC)GpiwvsF)}vWZZ#u-H5Zi|JV% z_?=DDeF&mb%0a#$!^TeN7HzW0l8~rZI=3&K_XZIaRA6h7ik78iLKd|0qSJwG`-W{! zc6PIT6hQ=Z5fs#;AwoET67H~!WQ}!cs^@5=fh3zrL~U6=y9VvG#din%6$%!f#Eq$v zq*_BB6pF>>oHEanwy3UGj+Yz+G|`qC;T#T1wA{spb4rBmdXXBlsu`{tUyb`Zu#^v1zUdY5LZG~{jgXA(II?c*dj%o9USF^qt1wSHU=amFj zo&P=?QbgEQ&O%6jK=_Rw8X`34yf}$+Z8cM{2J5?Yp3*Si-QDfpLcjvo0u7}CQcQ}Q}xXM%WjU=A$!Rd27Pvh~S| zQ?7D&;4;1M5)eHj0X5K)=(_#H!JaRTJ*8GJCf;D(=S3(^{WdZ_2^x07*6!ubAN>{f z`HtqW$Kfwv3&hqz+(kqpqtH@`WM#W8Mi`44w(?-Z^~3R^Jg^4GCY9=KvV5(egM#Mi z9PIVmFYipbA%(tY7HP7d+weh>E(G`*CQ8x0DQ9I}uW_P2L;*Od}T1$X+TmWrrG z={D|X@$xTQNUxNXuzf=6ze#lcJ*}!-bvy`}^9m%9G3nW$MU|}^h z#6CYpAA9pXkoPVnaH$H*WeRL&Fd`DF^+-lonElMfL?Qf+;SVcrfC!Is^3H@$f?|b~ zMAU5V?p(EPdEdf!i9N5UNk%4yB=Cl;uRG6jGHMq##6UDRaB$B|$Cr0^88rVG=;@oeGNav6evsvH zAMVca1XB)xN-+POvcf~{a60ZzPA<0eF%p#}j*g|QI)5gYG|H03C6^A@txr&R7a6}} zkWwnl?F{*dIQ~x$xRgid76U$OD5y=ftAhV<0lX0+)7cn_2aLYWZkI{Q*%xFBUZimO zM%5dDfhdHVQCk|?r>5!!(dY?746j(LNoMljl3^MKnN1qhSmfESB`Tj>e98IdHzO)-cDkERNsT7VwNRi{h`_C5*n zET1>?su8l>T}>|Fvg$%{u~gI^atk`a^2<>OV}?=`IoyP6`IF!qE9R>8akiM0KU*wm zt*F_YV9}fOloubScGtds*4W^i9_5nv&4Df_-Kpt+o!N7Swa#kVrsTwwQ7hU=i5H5M zIDXryt|0;7mnOG7=RHFcqllfI9#7X_;^GQCHvD!Q2x62?*ak?Y;Es61;elCxRv*!@ z3!1D`*KOF*5_3Zdi1C7%EUIHlHRU-Rw9jpKPJD^@7KatH& zjL8u0rtB?hkdWezRjAQ^5?z@Y8+~EBB3j!zn;3{-s*awR=uzQ8Ol;`@vqE8;@kh>N zr%fI`l_y48nkaLXWv-JC8eK(ggJWvQ&SqmW|KmK5x}-rf3D!sOO!krR|96eQ=%d#Q zleshM935QtJZC=FcTgJmOMc5%P|DSi{k0!(WxJxAGa;djK~`wQe>L&}^poZ3@nmNN~F9WI9#PprR<8h*sheAeXWI2@2Syv-cN{m+7&zLrHVM|M?m z=-WC-_k(b>_m@A9U$M=k79IRlcA_~%NW z51`(C2U76-`ezN#2md|k->-T8)qhX=M~OWD=HIJ%KKMuCpAY`IGyfbsYnp#v|NW@{ zec!Vc{O9%me&7F2`tSGs_oV;N_x<;z|IecS_fh}(zE(oX*Ktsf(qJ~8n%b=uyA*Al zgy85XK+dk+fC>OEM}jOY5}%r|+H6ldY=T$W$cSRY$$WD1)KIEedA1CMAO)w6HihG% zb4_IF!kvrN-eg5*s_hRg71LRl_JI^t?A)%&%&v)rN^@mlbwWyv!(E}5uo{kM0yNZe zO|{nUlIf`azHGTU7C@tKjRrIcWs1hjO< z#!%f4rGCuhnkt>IbIegKVny5PuPRW7Tt^x`gq_^D6Gg_v9Bl9SNszyTlZ%W()ze2? zTU}k#Lwi10Th-U+6A|%xgWNaJL(v@kG{Y3&bg<7gj;joGfhQ(yoZIQ~AKQ}5oRYC} z0g+s4D)KH~*q~2zY&FVzm}x{rf%y$0V{h+}SnaK7J$4JBGpr8F80Td7ew_Q6Hg#?d%FIaOz&1&9vplenTJHEhd{$+ zKR!Hqn?yHzoT~NbyA|r%sxx=$y4j90L~E*!x90b|76%|qXc+5TcVYONrHbbGc++qi z(vL}S8ehKJv_A^8yE>Eo?J~&DdAqY{aC!Q4L3l`PXqMu3iGb{RcYDJ`BrYbd!Qgb+ zOq1ju_XN?Lvh07MnvfvxL3vTd$wn?BA(H1~6uxOOD=wA+LjjiVt?i%a|MzF71ne8a zGS`v&imotJl(eCx27@uFr@p<*LnEqo!o_9`)c38?a}EburOjGx>+2guBsDXYD9?XU zEpuqN!lk#lobTOV=;+_ieIh;SGv3*rWMpGwU}9rnW2^XGX=h((sN=oF1|(pir`Hqh zJ6eR)f@>FK31@faLem&7yi1+lK~BxIH-3jnV(y1J)72WAC1}5Cd6D_*i2p~>Ctg;^ z`PRpQxk~kBgQD1KX!8Q`6hQ<_nyzkrjQ@mQVZNnkOzc7*`8&le!lJdfB4%~vl41gfdY>4wm+ z8zFUSZ{EBO*Q(|BJN{A_S}6yxTC%_BxHY8&^z)W_ zGEz0ucxz<@%`oYh>6!NdMJHZLOiaw>5_h=KqCsdZH<|BHM35Ja+>RYjNGJz81aLdf zO`fW01W9OUcw81=^x#Hmw>W?2cP!%^*g(_&_1(YH8ZGAV>ZR+> z%bk2eXE*NE*tj7nZ5uX1w`$-sDJTj~D%@eC$Gi4-+F{k36{hG^TgqHRQ6v8 z6DE{L?YF^Gh0&o_LvQrcLV1bX(xYMn1f|I>ct?IAJvO2{`q1O%7@HU)1Os8+%d z3qP$@??uYOLiHyuR-5kDo1#gT*=h0S83LHsm+$FiNm8ARmrvIpd5B!C?_zyXoON|q z`pAV2rY%su!vLFf-So}KfF_l9qffs-Lp{yYYmp=M{zzxJFOGE3^=6&n4xKy`2yzVO z_cP~4&?t`4;A+z1I0?opqyOhmr;Lw5HZ~yK25mhsCs|oSL%mYk`$Q$&uy7*ZVFBo` zq_n`qP}jiRG`QilEVMb*IV!O=5fIYjrHvxRa)|%oKmy$T%7MC%uy}L}&+&xBX6rZ$ zOQ`8Y+rtZBagVuFYrdf*9h(Ck1#_=d-X{B6vs(aReH#Zf+o{yf+4P z<70PMKtBhHDk)X>+sHyc(u0RK*W%FkkTiwZf^R-F+|g07f2XC_TBL|ZF~ULWk^sC$}baqv#P^#nQRJ-VKGr^EBg_nOW3YNadsK_5MIbHJ2?U!6ZRtfDzN-dkv(- z|K)3XFLWCQ20fx>E99jW~h6^ORaEEus2@fwBLI%x+(5) zmj}d{mG!=KOT+8rHk3##LoYKE>1Z}1J>Od$-U}W^K){UauGfm3jtyj(t#i#S4F+=Nu2ndeEvZrYt32dOrFtMc*KHIt4bkKH^1XEKH9V zYq%^w`@x?Jl<3r2S&`zlgVe{4z2+Rz(;lx6Q+nF{kDVJG7wTn>Q-~v3-46~uE*5Y~ z;@KL8-W?g1%#!@=QY5=4BN8&mSz0|^Y&f}icm(cdk`gr`+gMzRo`n(Lkyla6P5G~w zI@c9r z5O7JKa^%SqZ1V_sl#i%0a%zO|lQhYt59aDsbI-BPH!6%s$;tBb3#5{ntops1SDkuS ztk*F}5aP$=<)>)W`=d1znHM}xf6m7-qZQ)_3(I3sx0U#|bX}`fbPXkH-W&*u2(#8e z+M{dmPolWzu))mI-8Ic$X@ofF?7H2bvpNyo$s~6dt-OE!tX1BNJ>cr4`n0~a;qiFB zGciWKIu~29v@q*M}-;K%SlL&R}!R&{w)KAug(&F zRF^JBUaY!my}@HBtW*s@THx`t=0lM>Pbn%XQuhmRx?g@gA6Fm(Ef}XVaiPR3 z?I(Zw)~XQga@ni+2!~2&2wy{3I2gX1!fB|f&ARRTR!LwAHF}8%ekJ6+4#TZ*TYJ5< zL6r!y9J6Xbg#aI{$$gURE}9fmroaMtCb)^9F(V1d3kwP zsm2lLhaVk{jsm>(7MQzPc8+8YS3ze6eR=s!0AZ3PCdiF-i^@wR-NdCP*6Z|U*Fd!2 z9_?Ubp|wk6J;uc;Gb;p@m!DdlI1qC($~hy1cO|jvsd#E>1@*zgHqO*IFv(vMuzg{_ zC(8`e;@O?B`Yd=-Ru*C;No9A`^cv~%a+lT?QGDF(Shf+iRR$gJn2de-tvmsvVR5FM zkXr5f*47XKn|~m+h56lET&*e*=aqGvT+k*Gjzk0M*Zcw1BhVo&)~n9;-vmwiK2cIo zc(~+}$B}<|6VSf1yyI}Q6Ox0=#67VK*P-c1c%HeK2VA15ZN7n(_TtiK;BnO$ANWJJi%_4X^e{I` zOGqU!x^JI}AY+!Q(v@3FDty&2)%n&T&CCEAp^Ypx1Q_vaXrxt_RO6r6nGnrRa&lGV zGu4415)dzoAvjVCA|jw|K;(7Q&Ob$?$!)Qs7vaY=I{Fv+HeiRjv`E|Hc)P5VjoqoU zkbq{vg?kaiqbVAiZb*34hDVpu(r#jn9mUAkihWk}~n61e#0Sn&0pA)mZx^E>*JD6l^U!9c(UJ zrVuZ-;7*R&9qrhK(UvX=E~^T`Pe*&+Z~}#8J$RG^H{T?sPHM2R6;5zC`@7U4&eB}n zo@n*Nc`e}0H~Eo!;Q5t{!w>8q;Nf>krqx;Q=n*BdnfwTHIT*#cC`(gLoo{h6e_EtC z!(3Lz_f&guYAmlqc&*XJ4rd9Vw- zfJyk_pBrnvSa9Fi`170@ba6fj$^vNj&CLz)FFp{NQ0zSG868A5&U$U4!K38CTM_aL9FBt;FFqK^cY(5GM699(5yKmog5PuL7(_OMbC_ypg1<|{4|3&@#c-K zovrZsAvPKY>3yb#tn4xAi>HaZ?|g38%V&8qf*sA}z?CQaik3E%#$ia36jJ*8xAknG z?o}z41L&&YnHi5{PMjyH?=QDyTbFfvf#iN8*Uvz2#SeVJ{%~?+Lh5@*o%L1JwYA

RS_$qKD#Z?SU+5P&t+%29Me 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 z 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 PK3lRG@BRM^Qx!D! diff --git a/server/tests/test_document_numbering.py b/server/tests/test_document_numbering.py new file mode 100644 index 0000000..c6df5e8 --- /dev/null +++ b/server/tests/test_document_numbering.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +from datetime import UTC, datetime +from decimal import Decimal + +import pytest +from sqlalchemy import create_engine +from sqlalchemy.orm import Session, sessionmaker +from sqlalchemy.pool import StaticPool + +from app.db.base import Base +from app.models.financial_record import ExpenseClaim +from app.services.document_numbering import ( + build_document_number, + generate_unique_expense_claim_no, + is_application_claim_no, +) + + +def build_session() -> Session: + engine = create_engine( + "sqlite+pysqlite:///:memory:", + connect_args={"check_same_thread": False}, + poolclass=StaticPool, + ) + Base.metadata.create_all(bind=engine) + factory = sessionmaker(bind=engine, autoflush=False, autocommit=False) + return factory() + + +def test_build_document_number_uses_kind_prefix_timestamp_and_token() -> None: + timestamp = datetime(2026, 5, 25, 10, 30, 45, tzinfo=UTC) + + assert ( + build_document_number("application", timestamp=timestamp, token="ABCDEFGH") + == "AP-20260525103045-ABCDEFGH" + ) + assert ( + build_document_number("reimbursement", timestamp=timestamp, token="ABCDEFGH") + == "RE-20260525103045-ABCDEFGH" + ) + assert ( + build_document_number("audit", timestamp=timestamp, token="ABCDEFGH") + == "AD-20260525103045-ABCDEFGH" + ) + + +def test_build_document_number_rejects_ambiguous_token_chars() -> None: + timestamp = datetime(2026, 5, 25, 10, 30, 45, tzinfo=UTC) + + with pytest.raises(ValueError): + build_document_number("application", timestamp=timestamp, token="ABCDEF10") + + +def test_generate_unique_expense_claim_no_retries_existing_candidate() -> None: + timestamp = datetime(2026, 5, 25, 10, 30, 45, tzinfo=UTC) + with build_session() as db: + db.add( + ExpenseClaim( + claim_no="RE-20260525103045-ABCDEFGH", + employee_name="张三", + department_name="市场部", + project_code=None, + expense_type="transport", + reason="交通报销", + location="深圳", + amount=Decimal("10.00"), + currency="CNY", + invoice_count=1, + occurred_at=timestamp, + status="draft", + approval_stage="待提交", + risk_flags_json=[], + ) + ) + db.commit() + + tokens = iter(["ABCDEFGH", "HGFEDCBA"]) + + assert ( + generate_unique_expense_claim_no( + db, + "reimbursement", + timestamp=timestamp, + token_factory=lambda: next(tokens), + ) + == "RE-20260525103045-HGFEDCBA" + ) + + +def test_is_application_claim_no_supports_new_and_legacy_prefixes() -> None: + assert is_application_claim_no("AP-20260525103045-ABCDEFGH") + assert is_application_claim_no("APP-20260525-ABC123") + assert not is_application_claim_no("RE-20260525103045-ABCDEFGH") diff --git a/server/tests/test_employee_service.py b/server/tests/test_employee_service.py index 636d1ad..882ccbe 100644 --- a/server/tests/test_employee_service.py +++ b/server/tests/test_employee_service.py @@ -15,6 +15,8 @@ from app.models.organization import OrganizationUnit from app.models.role import Role from app.schemas.employee import EmployeeUpdate from app.services.employee import EmployeeService +from app.services.employee_seed import CANONICAL_DEPARTMENT_CODES +from app.services.employee_time import format_history_datetime def build_session() -> Session: @@ -49,7 +51,7 @@ def test_employee_directory_seeds_rich_employee_data() -> None: history_count = db.scalar(select(func.count()).select_from(EmployeeChangeLog)) assert role_count == 6 - assert org_count == 10 + assert org_count == 7 assert employee_count == 30 assert history_count and history_count >= 30 @@ -194,6 +196,42 @@ def test_employee_meta_includes_organization_options() -> None: assert meta.organizationOptions assert all(item.code and item.name for item in meta.organizationOptions) + assert [item.name for item in meta.organizationOptions] == [ + "人力资源部", + "市场部", + "总裁办", + "技术部", + "生产部", + "财务部", + ] + + +def test_employee_directory_normalizes_legacy_departments() -> None: + with build_session() as db: + service = EmployeeService(db) + service.list_employees() + + legacy_department = OrganizationUnit( + unit_code="RND-CENTER", + name="产品研发中心", + unit_type="department", + ) + employee = db.execute( + select(Employee).where(Employee.employee_no == "E11745") + ).scalar_one() + employee.organization_unit = legacy_department + db.add(legacy_department) + db.commit() + + refreshed = next( + item for item in service.list_employees() if item.employeeNo == "E11745" + ) + meta = service.get_employee_meta() + + assert refreshed.department == "技术部" + assert refreshed.organization is not None + assert refreshed.organization.code == "TECH-DEPT" + assert "RND-CENTER" not in {item.code for item in meta.organizationOptions} def test_update_employee_changes_organization() -> None: @@ -202,7 +240,11 @@ def test_update_employee_changes_organization() -> None: employee = service.list_employees()[0] organizations = service.repository.list_organization_units() current_code = employee.organization.code if employee.organization else None - target = next(unit for unit in organizations if unit.unit_code != current_code) + target = next( + unit + for unit in organizations + if unit.unit_code in CANONICAL_DEPARTMENT_CODES and unit.unit_code != current_code + ) updated = service.update_employee( employee.id, @@ -245,7 +287,7 @@ def test_update_employee_changes_manager() -> None: def test_format_history_datetime_uses_local_timezone_without_seconds() -> None: value = datetime(2026, 5, 20, 6, 30, 45, tzinfo=UTC) - formatted = EmployeeService._format_history_datetime(value) + formatted = format_history_datetime(value) assert formatted == "2026年5月20日14时30分" assert "秒" not in formatted diff --git a/server/tests/test_employee_spreadsheet_import.py b/server/tests/test_employee_spreadsheet_import.py index af4a6e0..cb6fcbe 100644 --- a/server/tests/test_employee_spreadsheet_import.py +++ b/server/tests/test_employee_spreadsheet_import.py @@ -94,7 +94,7 @@ def test_import_employees_updates_existing_employee() -> None: "上海", employee.position, employee.grade, - "FIN-SSC", + "FINANCE-DEPT", "", "华东财务组", "CC-TEST", @@ -132,7 +132,7 @@ def test_import_employees_creates_new_employee() -> None: "上海", "业务专员", "P3", - "FIN-SSC", + "FINANCE-DEPT", "E10234", "华东财务组", "CC-9001", diff --git a/server/tests/test_expense_claim_service.py b/server/tests/test_expense_claim_service.py index 5867e38..e005c13 100644 --- a/server/tests/test_expense_claim_service.py +++ b/server/tests/test_expense_claim_service.py @@ -1,5 +1,6 @@ from __future__ import annotations +import re from datetime import UTC, date, datetime, timedelta from decimal import Decimal @@ -1042,7 +1043,7 @@ def test_upsert_draft_from_ontology_updates_returned_claim_and_preserves_return_ assert manual_returns == [return_flag] -def test_generate_claim_no_uses_max_suffix_instead_of_count() -> None: +def test_generate_claim_no_uses_re_prefix_timestamp_and_random_suffix() -> None: with build_session() as db: db.add_all( [ @@ -1084,7 +1085,10 @@ def test_generate_claim_no_uses_max_suffix_instead_of_count() -> None: service = ExpenseClaimService(db) - assert service._generate_claim_no(datetime(2026, 5, 14, tzinfo=UTC)) == "EXP-202605-004" + assert re.fullmatch( + r"RE-\d{14}-[A-HJ-NP-Z2-9]{8}", + service._generate_claim_no(datetime(2026, 5, 14, tzinfo=UTC)), + ) def test_upsert_draft_from_ontology_retries_claim_no_conflict() -> None: @@ -1100,7 +1104,7 @@ def test_upsert_draft_from_ontology_retries_claim_no_conflict() -> None: db.flush() db.add( ExpenseClaim( - claim_no="EXP-202605-004", + claim_no="RE-20260525101010-ABCDEFGH", employee_name="历史单据", department_name="财务部", project_code=None, @@ -1125,7 +1129,9 @@ def test_upsert_draft_from_ontology_retries_claim_no_conflict() -> None: ) ) service = ExpenseClaimService(db) - generated_claim_nos = iter(["EXP-202605-004", "EXP-202605-005"]) + generated_claim_nos = iter( + ["RE-20260525101010-ABCDEFGH", "RE-20260525101010-HGFEDCBA"] + ) service._generate_claim_no = lambda occurred_at: next(generated_claim_nos) result = service.upsert_draft_from_ontology( @@ -1141,8 +1147,8 @@ def test_upsert_draft_from_ontology_retries_claim_no_conflict() -> None: created_claim = db.get(ExpenseClaim, result["claim_id"]) assert created_claim is not None - assert created_claim.claim_no == "EXP-202605-005" - assert result["claim_no"] == "EXP-202605-005" + assert created_claim.claim_no == "RE-20260525101010-HGFEDCBA" + assert result["claim_no"] == "RE-20260525101010-HGFEDCBA" def test_create_claim_item_adds_blank_draft_row_without_forcing_attachment() -> None: @@ -2629,17 +2635,53 @@ def test_list_archived_claims_returns_company_archived_records_for_finance() -> approval_stage="财务审批", risk_flags_json=[], ), + ExpenseClaim( + claim_no="AP-20260525120000-ABCDEFGH", + employee_name="丙", + department_name="C部", + project_code="PRJ-C", + expense_type="travel_application", + reason="C 申请", + location="成都", + amount=Decimal("800.00"), + currency="CNY", + invoice_count=0, + occurred_at=datetime(2026, 5, 11, 14, 0, tzinfo=UTC), + submitted_at=datetime(2026, 5, 11, 15, 0, tzinfo=UTC), + status="approved", + approval_stage="审批完成", + risk_flags_json=[], + ), + ExpenseClaim( + claim_no="AP-20260525123000-HGFEDCBA", + employee_name="丁", + department_name="D部", + project_code="PRJ-D", + expense_type="travel_application", + reason="D 申请", + location="北京", + amount=Decimal("500.00"), + currency="CNY", + invoice_count=0, + occurred_at=datetime(2026, 5, 11, 16, 0, tzinfo=UTC), + submitted_at=datetime(2026, 5, 11, 17, 0, tzinfo=UTC), + status="submitted", + approval_stage="直属领导审批", + risk_flags_json=[], + ), ] ) db.commit() claims = ExpenseClaimService(db).list_archived_claims(current_user) - assert len(claims) == 1 - assert claims[0].claim_no == "EXP-ARCH-101" + assert {claim.claim_no for claim in claims} == { + "EXP-ARCH-101", + "AP-20260525120000-ABCDEFGH", + } -def test_list_archived_claims_is_empty_for_regular_employee() -> None: +def test_list_archived_claims_returns_only_own_records_for_regular_employee() -> None: current_user = CurrentUserContext( username="zhangsan@example.com", name="张三", @@ -2648,30 +2690,49 @@ def test_list_archived_claims_is_empty_for_regular_employee() -> None: ) with build_session() as db: - db.add( - ExpenseClaim( - claim_no="EXP-ARCH-EMP", - employee_name="张三", - department_name="研发部", - project_code="PRJ-EMP", - expense_type="travel", - reason="本人报销", - location="北京", - amount=Decimal("200.00"), - currency="CNY", - invoice_count=1, - occurred_at=datetime(2026, 5, 10, 9, 0, tzinfo=UTC), - submitted_at=datetime(2026, 5, 10, 10, 0, tzinfo=UTC), - status="approved", - approval_stage="归档入账", - risk_flags_json=[], - ) + db.add_all( + [ + ExpenseClaim( + claim_no="EXP-ARCH-EMP", + employee_name="张三", + department_name="研发部", + project_code="PRJ-EMP", + expense_type="travel", + reason="本人报销", + location="北京", + amount=Decimal("200.00"), + currency="CNY", + invoice_count=1, + occurred_at=datetime(2026, 5, 10, 9, 0, tzinfo=UTC), + submitted_at=datetime(2026, 5, 10, 10, 0, tzinfo=UTC), + status="approved", + approval_stage="归档入账", + risk_flags_json=[], + ), + ExpenseClaim( + claim_no="AP-20260525130000-ABCDEFGH", + employee_name="李四", + department_name="研发部", + project_code="PRJ-EMP", + expense_type="travel_application", + reason="他人申请", + location="上海", + amount=Decimal("500.00"), + currency="CNY", + invoice_count=0, + occurred_at=datetime(2026, 5, 10, 9, 0, tzinfo=UTC), + submitted_at=datetime(2026, 5, 10, 10, 0, tzinfo=UTC), + status="approved", + approval_stage="审批完成", + risk_flags_json=[], + ), + ] ) db.commit() claims = ExpenseClaimService(db).list_archived_claims(current_user) - assert claims == [] + assert [claim.claim_no for claim in claims] == ["EXP-ARCH-EMP"] def test_finance_can_return_but_cannot_delete_submitted_claim() -> None: @@ -2760,6 +2821,79 @@ def test_executive_can_delete_submitted_claim() -> None: assert db.get(ExpenseClaim, claim_id) is None +def test_executive_cannot_delete_archived_claim() -> None: + current_user = CurrentUserContext( + username="executive-archive-delete@example.com", + name="高管", + role_codes=["executive"], + is_admin=False, + ) + + with build_session() as db: + claim = ExpenseClaim( + claim_no="EXP-DEL-ARCHIVE-101", + employee_name="张三", + department_name="市场部", + project_code="PRJ-A", + expense_type="travel", + reason="差旅报销", + location="上海", + amount=Decimal("120.00"), + currency="CNY", + invoice_count=1, + occurred_at=datetime(2026, 5, 11, 9, 0, tzinfo=UTC), + submitted_at=datetime(2026, 5, 11, 10, 0, tzinfo=UTC), + status="approved", + approval_stage="归档入账", + risk_flags_json=[], + ) + db.add(claim) + db.commit() + claim_id = claim.id + + with pytest.raises(ValueError, match="已归档单据不能删除"): + ExpenseClaimService(db).delete_claim(claim_id, current_user) + + assert db.get(ExpenseClaim, claim_id) is not None + + +def test_admin_can_delete_archived_claim() -> None: + current_user = CurrentUserContext( + username="superadmin", + name="系统管理员", + role_codes=["manager"], + is_admin=True, + ) + + with build_session() as db: + claim = ExpenseClaim( + claim_no="EXP-DEL-ARCHIVE-102", + employee_name="张三", + department_name="市场部", + project_code="PRJ-A", + expense_type="travel", + reason="差旅报销", + location="上海", + amount=Decimal("120.00"), + currency="CNY", + invoice_count=1, + occurred_at=datetime(2026, 5, 11, 9, 0, tzinfo=UTC), + submitted_at=datetime(2026, 5, 11, 10, 0, tzinfo=UTC), + status="approved", + approval_stage="归档入账", + risk_flags_json=[], + ) + db.add(claim) + db.commit() + claim_id = claim.id + + deleted = ExpenseClaimService(db).delete_claim(claim_id, current_user) + + assert deleted is not None + assert deleted.claim_no == "EXP-DEL-ARCHIVE-102" + assert db.get(ExpenseClaim, claim_id) is None + + def test_direct_manager_can_return_subordinate_claim_to_pending_submission() -> None: current_user = CurrentUserContext( username="manager-return@example.com", @@ -2945,7 +3079,7 @@ def test_application_submit_skips_ai_review_and_receipt_requirements(monkeypatch ) -def test_direct_manager_can_approve_application_claim_to_completed_stage() -> None: +def test_direct_manager_can_approve_application_claim_to_reimbursement_draft() -> None: current_user = CurrentUserContext( username="manager-application-approve@example.com", name="李经理", @@ -2998,6 +3132,35 @@ def test_direct_manager_can_approve_application_claim_to_completed_stage() -> No assert approved is not None assert approved.status == "approved" assert approved.approval_stage == "审批完成" + archived_claims = ExpenseClaimService(db).list_archived_claims( + CurrentUserContext( + username="finance-archive@example.com", + name="财务归档员", + role_codes=["finance"], + is_admin=False, + ) + ) + assert any(claim.claim_no == "APP-20260525-APPROVE" for claim in archived_claims) + generated_draft = db.query(ExpenseClaim).filter(ExpenseClaim.claim_no.like("RE-%")).one() + assert generated_draft.status == "draft" + assert generated_draft.approval_stage == "待提交" + assert generated_draft.expense_type == "travel" + assert generated_draft.employee_id == employee.id + assert generated_draft.employee_name == "张三" + assert generated_draft.department_name == "交付部" + assert generated_draft.reason == "支撑国网服务器上线部署" + assert generated_draft.location == "上海" + assert generated_draft.amount == Decimal("12000.00") + assert generated_draft.invoice_count == 0 + assert generated_draft.items == [] + assert any( + isinstance(flag, dict) + and flag.get("source") == "application_handoff" + and flag.get("event_type") == "expense_application_to_reimbursement_draft" + and flag.get("application_claim_no") == "APP-20260525-APPROVE" + and flag.get("leader_opinion") == "业务必要,同意申请。" + for flag in generated_draft.risk_flags_json + ) assert any( isinstance(flag, dict) and flag.get("source") == "manual_approval" @@ -3006,10 +3169,69 @@ def test_direct_manager_can_approve_application_claim_to_completed_stage() -> No and flag.get("previous_approval_stage") == "直属领导审批" and flag.get("next_status") == "approved" and flag.get("next_approval_stage") == "审批完成" + and flag.get("generated_draft_claim_id") == generated_draft.id + and flag.get("generated_draft_claim_no") == generated_draft.claim_no for flag in approved.risk_flags_json ) +def test_direct_manager_approval_requires_leader_opinion() -> None: + current_user = CurrentUserContext( + username="manager-application-required-opinion@example.com", + name="李经理", + role_codes=["manager"], + is_admin=False, + ) + + with build_session() as db: + manager = Employee( + employee_no="E8122", + name="李经理", + email="manager-application-required-opinion@example.com", + ) + employee = Employee( + employee_no="E8123", + name="张三", + email="zhangsan-application-required-opinion@example.com", + manager=manager, + ) + db.add_all([manager, employee]) + db.flush() + claim = ExpenseClaim( + claim_no="APP-20260525-REQUIRE-OPINION", + employee_id=employee.id, + employee_name="张三", + department_name="交付部", + project_code="PRJ-A", + expense_type="travel_application", + reason="支撑国网服务器上线部署", + location="上海", + amount=Decimal("12000.00"), + currency="CNY", + invoice_count=0, + occurred_at=datetime(2026, 5, 25, 9, 0, tzinfo=UTC), + submitted_at=datetime(2026, 5, 25, 10, 0, tzinfo=UTC), + status="submitted", + approval_stage="直属领导审批", + risk_flags_json=[], + ) + db.add(claim) + db.commit() + claim_id = claim.id + + with pytest.raises(ValueError, match="领导审核意见不能为空"): + ExpenseClaimService(db).approve_claim( + claim_id, + current_user, + opinion=" ", + ) + + db.refresh(claim) + assert claim.status == "submitted" + assert claim.approval_stage == "直属领导审批" + assert db.query(ExpenseClaim).filter(ExpenseClaim.claim_no.like("RE-%")).count() == 0 + + def test_finance_can_approve_claim_to_archive_stage() -> None: current_user = CurrentUserContext( username="finance-approve@example.com", diff --git a/server/tests/test_ontology_service.py b/server/tests/test_ontology_service.py index 1d6a201..66f5e64 100644 --- a/server/tests/test_ontology_service.py +++ b/server/tests/test_ontology_service.py @@ -244,10 +244,10 @@ def test_semantic_ontology_service_matches_day3_evaluation_set( assert result.run_id.startswith("run_") -def test_semantic_ontology_service_extracts_entities_time_and_constraints() -> None: - session_factory = build_session_factory() - with session_factory() as db: - result = SemanticOntologyService(db).parse( +def test_semantic_ontology_service_extracts_entities_time_and_constraints() -> None: + session_factory = build_session_factory() + with session_factory() as db: + result = SemanticOntologyService(db).parse( OntologyParseRequest( query="张三 2026年4月差旅报销金额超过5000元的明细", user_id="pytest", @@ -255,9 +255,30 @@ def test_semantic_ontology_service_extracts_entities_time_and_constraints() -> N ) assert result.scenario == "expense" - assert result.intent == "query" - assert result.time_range.start_date == "2026-04-01" - assert result.time_range.end_date == "2026-04-30" + assert result.intent == "query" + assert result.time_range.start_date == "2026-04-01" + assert result.time_range.end_date == "2026-04-30" + + +def test_semantic_ontology_service_extracts_new_document_numbers() -> None: + session_factory = build_session_factory() + with session_factory() as db: + result = SemanticOntologyService(db).parse( + OntologyParseRequest( + query="查询 RE-20260525103045-ABCDEFGH 和 AP-20260525113045-HGFEDCBA 的状态", + user_id="pytest", + ) + ) + + claim_codes = { + item.normalized_value + for item in result.entities + if item.type == "expense_claim" + } + assert claim_codes == { + "RE-20260525103045-ABCDEFGH", + "AP-20260525113045-HGFEDCBA", + } def test_semantic_ontology_service_treats_travel_amount_question_as_knowledge_query() -> None: diff --git a/server/tests/test_orchestrator_review_flow.py b/server/tests/test_orchestrator_review_flow.py index f13fed6..8285f9b 100644 --- a/server/tests/test_orchestrator_review_flow.py +++ b/server/tests/test_orchestrator_review_flow.py @@ -687,14 +687,14 @@ def test_orchestrator_application_session_guides_transport_amount_and_submit( ) assert first.status == "blocked" - assert "当前还需要补充:出行方式、预计金额/预算" in first.result["answer"] + assert "当前还需要补充:出行方式、用户预估费用" in first.result["answer"] assert [item["label"] for item in first.result["suggested_actions"]] == ["一次性补充申请信息"] - assert first.result["suggested_actions"][0]["payload"]["prompt_prefill"] == "出行方式:\n预计总费用:" + assert first.result["suggested_actions"][0]["payload"]["prompt_prefill"] == "出行方式:\n用户预估费用:" - assert "当前还需要补充:预计金额/预算" in second.result["answer"] + assert "当前还需要补充:用户预估费用" in second.result["answer"] assert [item["label"] for item in second.result["suggested_actions"]] == ["一次性补充申请信息"] assert second.result["suggested_actions"][0]["action_type"] == "prefill_composer" - assert second.result["suggested_actions"][0]["payload"]["prompt_prefill"] == "预计总费用:" + assert second.result["suggested_actions"][0]["payload"]["prompt_prefill"] == "用户预估费用:" assert "这是模拟的费用申请结果" in third.result["answer"] assert "| 事由 | 支持上海国网服务器部署 |" in third.result["answer"] @@ -713,7 +713,7 @@ def test_orchestrator_application_session_guides_transport_amount_and_submit( application_claims = [ claim for claim in db.query(ExpenseClaim).all() - if claim.claim_no.startswith("APP-20260525-") + if claim.claim_no.startswith("AP-") ] assert len(application_claims) == 1 assert application_claims[0].status == "submitted" diff --git a/server/tests/test_risk_rule_composite_generation.py b/server/tests/test_risk_rule_composite_generation.py new file mode 100644 index 0000000..0996de5 --- /dev/null +++ b/server/tests/test_risk_rule_composite_generation.py @@ -0,0 +1,361 @@ +from __future__ import annotations + +import json +from datetime import UTC, date, datetime +from decimal import Decimal + +from sqlalchemy import create_engine +from sqlalchemy.orm import Session, sessionmaker +from sqlalchemy.pool import StaticPool + +from app.core.agent_enums import AgentAssetDomain +from app.db.base import Base +from app.models.agent_asset import AgentAsset +from app.models.financial_record import ExpenseClaim, ExpenseClaimItem +from app.schemas.agent_asset import AgentAssetRiskRuleGenerateRequest +from app.services.agent_asset_rule_library import AgentAssetRuleLibraryManager +from app.services.agent_asset_spreadsheet import RISK_RULES_LIBRARY +from app.services.risk_rule_generation import RiskRuleGenerationService +from app.services.risk_rule_template_executor import RiskRuleTemplateExecutor + + +class NullRuntimeChatService: + def complete(self, *args, **kwargs) -> None: + return None + + +class CompositeRuntimeChatService: + def complete(self, *args, **kwargs) -> str: + return json.dumps( + { + "name": "招待发票说明校验", + "description": "招待报销已取得发票但缺少客户说明时进入复核。", + "template_key": "composite_rule_v1", + "semantic_type": "entertainment_invoice_reason_check", + "field_keys": ["attachment.invoice_no", "claim.reason"], + "condition_summary": "D=发票号码,E=报销事由;D存在且E未说明客户名称时命中。", + "rule_ir": { + "facts": [ + {"id": "D", "label": "发票号码", "fields": ["attachment.invoice_no"]}, + {"id": "E", "label": "报销事由", "fields": ["claim.reason"]}, + ], + "hit_logic": "D AND NOT CONTAINS(E, 客户)", + }, + "conditions": [ + { + "id": "invoice_present", + "operator": "exists_any", + "fields": ["attachment.invoice_no"], + }, + { + "id": "missing_customer_reason", + "operator": "not_contains_any", + "fields": ["claim.reason"], + "keywords": ["客户", "拜访对象"], + }, + ], + "hit_logic": {"all": ["invoice_present", "missing_customer_reason"]}, + "formula": "HIT WHEN EXISTS(invoice_no) AND NOT CONTAINS(reason, 客户|拜访对象)", + "message_template": "招待发票已上传,但事由缺少客户或拜访对象说明。", + "keywords": [], + "exception_keywords": [], + "flow": { + "start": "招待报销提交", + "evidence": "读取发票号码和报销事由", + "decision": "是否有发票且事由缺少客户说明", + "pass": "客户说明完整,继续流转", + "fail": "缺少客户说明,进入复核", + }, + }, + ensure_ascii=False, + ) + + +class LodgingSemanticRuntimeChatService: + def complete(self, *args, **kwargs) -> str: + return json.dumps( + { + "name": "住宿城市日期一致性校验", + "description": "住宿票据的城市和日期需要能对应本次差旅行程,缺少合理说明时进入复核。", + "template_key": "composite_rule_v1", + "semantic_type": "travel_lodging_city_date_consistency", + "field_keys": [ + "attachment.hotel_city", + "attachment.stay_start_date", + "attachment.stay_end_date", + "attachment.issue_date", + "claim.location", + "item.item_location", + "attachment.route_cities", + "claim.trip_start_date", + "claim.trip_end_date", + "item.item_date", + "claim.reason", + "item.item_reason", + ], + "condition_summary": ( + "D=住宿票据事实,A=住宿城市,B=本次行程城市范围,T=住宿日期或开票日期," + "R=出差起止日期;D存在且[(A不属于B)或(T超出R)]且无合理说明时命中。" + ), + "rule_ir": { + "facts": [ + {"id": "D", "label": "住宿票据事实", "fields": ["attachment.hotel_city", "attachment.ocr_text"]}, + {"id": "A", "label": "住宿城市", "fields": ["attachment.hotel_city"]}, + {"id": "B", "label": "本次行程城市范围", "fields": ["claim.location", "item.item_location", "attachment.route_cities"]}, + {"id": "T", "label": "住宿日期或开票日期", "fields": ["attachment.stay_start_date", "attachment.stay_end_date", "attachment.issue_date"]}, + {"id": "R", "label": "出差起止日期", "fields": ["claim.trip_start_date", "claim.trip_end_date", "item.item_date"]}, + {"id": "E", "label": "合理说明", "fields": ["claim.reason", "item.item_reason"]}, + ], + "hit_logic": "D AND ((A NOT_IN B) OR DATE_OUTSIDE(T,R)) AND NOT CONTAINS(E, exception_keywords)", + }, + "conditions": [ + { + "id": "lodging_document_present", + "operator": "exists_any", + "fields": ["attachment.hotel_city", "attachment.ocr_text"], + }, + { + "id": "lodging_city_outside_trip_scope", + "operator": "not_in_scope", + "left_fields": ["attachment.hotel_city"], + "right_fields": ["claim.location", "item.item_location", "attachment.route_cities"], + }, + { + "id": "lodging_date_outside_trip_range", + "operator": "date_outside_range", + "date_fields": ["attachment.stay_start_date", "attachment.stay_end_date", "attachment.issue_date"], + "range_start_fields": ["claim.trip_start_date", "item.item_date"], + "range_end_fields": ["claim.trip_end_date", "item.item_date"], + }, + { + "id": "missing_reasonable_exception", + "operator": "not_contains_any", + "fields": ["claim.reason", "item.item_reason"], + "keywords": ["延期", "改签", "临时任务"], + }, + ], + "hit_logic": { + "all": [ + "lodging_document_present", + {"any": ["lodging_city_outside_trip_scope", "lodging_date_outside_trip_range"]}, + "missing_reasonable_exception", + ] + }, + "formula": "D AND ((A NOT_IN B) OR DATE_OUTSIDE(T,R)) AND NOT EXCEPTION(E)", + "message_template": "住宿票据城市或日期无法与本次差旅行程形成一致关系,且未识别到合理说明。", + "keywords": [], + "exception_keywords": ["延期", "改签", "临时任务"], + "flow": { + "start": "差旅住宿报销提交", + "evidence": "读取住宿票据事实、行程范围和合理说明", + "decision": "住宿城市或日期是否脱离本次行程,且是否缺少合理说明", + "pass": "城市和日期均能对应行程,或已有合理说明", + "fail": "城市或日期无法对应行程,进入复核", + }, + }, + ensure_ascii=False, + ) + + +def build_session() -> Session: + engine = create_engine( + "sqlite+pysqlite:///:memory:", + connect_args={"check_same_thread": False}, + poolclass=StaticPool, + ) + Base.metadata.create_all(bind=engine) + session_factory = sessionmaker(bind=engine, autoflush=False, autocommit=False) + return session_factory() + + +def _read_payload(manager: AgentAssetRuleLibraryManager, asset: AgentAsset) -> dict: + return manager.read_rule_library_json( + library=RISK_RULES_LIBRARY, + file_name=asset.config_json["rule_document"]["file_name"], + ) + + +def test_lodging_city_date_rule_generates_explainable_composite_json(tmp_path) -> None: + text = ( + "差旅住宿报销时,先确认已上传住宿发票或酒店水单;再读取报销事由、申报目的地、" + "住宿城市、开票日期和行程城市。若住宿票据显示的城市不在本次差旅行程范围内," + "或住宿发生时间明显早于出差开始、晚于出差结束,且没有延期、改签、临时任务等说明," + "则标记为高风险,要求补充住宿原因、行程证明或重新提交票据。" + ) + with build_session() as db: + manager = AgentAssetRuleLibraryManager(rule_root=tmp_path / "rules") + service = RiskRuleGenerationService( + db, + rule_library_manager=manager, + runtime_chat_service=LodgingSemanticRuntimeChatService(), + ) + asset_id = service.generate_rule_asset( + AgentAssetRiskRuleGenerateRequest( + business_domain=AgentAssetDomain.EXPENSE, + expense_category="hotel", + rule_title="住宿城市日期一致性校验", + risk_level="high", + natural_language=text, + requires_attachment=True, + ), + actor="pytest", + ) + asset = db.get(AgentAsset, asset_id) + assert asset is not None + payload = _read_payload(manager, asset) + + assert payload["template_key"] == "composite_rule_v1" + assert payload["semantic_type"] == "travel_lodging_city_date_consistency" + assert payload["params"]["semantic_type"] == "travel_lodging_city_date_consistency" + assert payload["params"]["keywords"] == [] + assert "风险关键词" not in payload["params"]["condition_summary"] + assert "attachment.stay_start_date" in payload["params"]["field_keys"] + assert "claim.trip_start_date" in payload["params"]["field_keys"] + assert payload["params"]["rule_ir"]["facts"] + assert payload["params"]["hit_logic"]["all"][1]["any"] == [ + "lodging_city_outside_trip_scope", + "lodging_date_outside_trip_range", + ] + + +def test_composite_lodging_executor_hits_mismatch_and_respects_exception() -> None: + manifest = { + "template_key": "composite_rule_v1", + "params": { + "template_key": "composite_rule_v1", + "semantic_type": "travel_lodging_city_date_consistency", + "condition_summary": "住宿城市或日期不在本次差旅行程范围内且无合理说明时命中。", + "conditions": [ + { + "id": "lodging_document_present", + "operator": "exists_any", + "fields": ["attachment.hotel_city", "attachment.ocr_text"], + }, + { + "id": "lodging_city_outside_trip_scope", + "operator": "not_in_scope", + "left_fields": ["attachment.hotel_city"], + "right_fields": ["claim.location", "item.item_location", "attachment.route_cities"], + }, + { + "id": "lodging_date_outside_trip_range", + "operator": "date_outside_range", + "date_fields": ["attachment.stay_start_date", "attachment.stay_end_date"], + "range_start_fields": ["claim.trip_start_date", "item.item_date"], + "range_end_fields": ["claim.trip_end_date", "item.item_date"], + }, + { + "id": "missing_reasonable_exception", + "operator": "not_contains_any", + "fields": ["claim.reason", "item.item_reason"], + "keywords": ["延期", "改签", "临时任务"], + }, + ], + "hit_logic": { + "all": [ + "lodging_document_present", + {"any": ["lodging_city_outside_trip_scope", "lodging_date_outside_trip_range"]}, + "missing_reasonable_exception", + ] + }, + }, + "outcomes": {"fail": {"severity": "high"}}, + } + claim = _build_claim(reason="去上海出差住宿", location="上海") + contexts = [ + { + "document_info": { + "hotel_city": "北京", + "stay_start_date": "2026-05-11", + "stay_end_date": "2026-05-12", + "fields": [ + {"key": "hotel_city", "label": "住宿城市", "value": "北京"}, + {"key": "stay_start_date", "label": "入住日期", "value": "2026-05-11"}, + {"key": "stay_end_date", "label": "离店日期", "value": "2026-05-12"}, + ], + }, + "ocr_text": "北京酒店住宿发票", + } + ] + + result = RiskRuleTemplateExecutor().evaluate(manifest, claim=claim, contexts=contexts) + assert result is not None + assert result["evidence"]["condition_results"]["lodging_city_outside_trip_scope"] is True + + claim_with_exception = _build_claim(reason="去上海出差住宿,因临时任务改签至北京", location="上海") + assert RiskRuleTemplateExecutor().evaluate( + manifest, claim=claim_with_exception, contexts=contexts + ) is None + + +def test_model_generated_composite_rule_is_preserved_for_other_categories(tmp_path) -> None: + with build_session() as db: + manager = AgentAssetRuleLibraryManager(rule_root=tmp_path / "rules") + service = RiskRuleGenerationService( + db, + rule_library_manager=manager, + runtime_chat_service=CompositeRuntimeChatService(), + ) + asset_id = service.generate_rule_asset( + AgentAssetRiskRuleGenerateRequest( + business_domain=AgentAssetDomain.EXPENSE, + expense_category="meal", + rule_title="招待发票客户说明校验", + risk_level="medium", + natural_language="招待报销时,如果已经上传发票但报销事由没有客户或拜访对象说明,则提示中风险。", + ), + actor="pytest", + ) + asset = db.get(AgentAsset, asset_id) + assert asset is not None + payload = _read_payload(manager, asset) + + assert payload["template_key"] == "composite_rule_v1" + assert payload["semantic_type"] == "entertainment_invoice_reason_check" + assert payload["params"]["conditions"][0]["operator"] == "exists_any" + assert payload["params"]["hit_logic"] == {"all": ["invoice_present", "missing_customer_reason"]} + assert payload["params"]["message_template"] == "招待发票已上传,但事由缺少客户或拜访对象说明。" + + claim = _build_claim(reason="招待费", location="上海") + result = RiskRuleTemplateExecutor().evaluate( + payload, + claim=claim, + contexts=[{"document_info": {"invoice_no": "INV-20260526001"}}], + ) + assert result is not None + assert result["message"] == "招待发票已上传,但事由缺少客户或拜访对象说明。" + + claim.reason = "招待客户 ACME 的餐费" + assert RiskRuleTemplateExecutor().evaluate( + payload, + claim=claim, + contexts=[{"document_info": {"invoice_no": "INV-20260526001"}}], + ) is None + + +def _build_claim(*, reason: str, location: str) -> ExpenseClaim: + claim = ExpenseClaim( + claim_no="TEST-COMPOSITE-RISK", + employee_name="测试员工", + department_name="测试部门", + expense_type="差旅费", + reason=reason, + location=location, + amount=Decimal("680.00"), + currency="CNY", + invoice_count=1, + occurred_at=datetime(2026, 5, 11, tzinfo=UTC), + status="draft", + ) + claim.trip_start_date = date(2026, 5, 10) + claim.trip_end_date = date(2026, 5, 12) + claim.items = [ + ExpenseClaimItem( + item_date=date(2026, 5, 11), + item_type="住宿费", + item_reason=reason, + item_location=location, + item_amount=Decimal("680.00"), + ) + ] + return claim diff --git a/server/tests/test_risk_rule_generation.py b/server/tests/test_risk_rule_generation.py index aeec1b1..b340090 100644 --- a/server/tests/test_risk_rule_generation.py +++ b/server/tests/test_risk_rule_generation.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from datetime import UTC, datetime +from datetime import UTC, date, datetime from decimal import Decimal from sqlalchemy import create_engine @@ -11,7 +11,8 @@ from sqlalchemy.pool import StaticPool from app.core.agent_enums import AgentAssetDomain, AgentAssetStatus, AgentReviewStatus from app.db.base import Base from app.models.agent_asset import AgentAsset -from app.models.financial_record import ExpenseClaim +from app.models.employee import Employee +from app.models.financial_record import ExpenseClaim, ExpenseClaimItem from app.schemas.agent_asset import ( AgentAssetReviewCreate, AgentAssetRiskRuleGenerateRequest, @@ -23,11 +24,15 @@ from app.schemas.agent_asset import ( from app.services.agent_asset_rule_library import AgentAssetRuleLibraryManager from app.services.agent_asset_spreadsheet import RISK_RULES_LIBRARY from app.services.agent_assets import AgentAssetService +from app.services.agent_foundation_risk_rules import AgentFoundationRiskRuleMixin from app.services.risk_rule_flow_diagram import ( RiskRuleFlowDiagramRenderer, RiskRuleFlowDiagramSpec, ) from app.services.risk_rule_generation import RiskRuleGenerationService +from app.services.risk_rule_generation_jobs import RiskRuleGenerationJobService +from app.services.risk_rule_manifest_normalizer import normalize_risk_rule_manifest +from app.services.risk_rule_template_executor import RiskRuleTemplateExecutor class NullRuntimeChatService: @@ -35,6 +40,41 @@ class NullRuntimeChatService: return None +class TravelRouteSemanticRuntimeChatService: + def complete(self, *args, **kwargs) -> str: + return json.dumps( + { + "name": "差旅票据路线一致性校验", + "description": "交通票或住宿票据城市需要与申报行程形成一致关系。", + "template_key": "field_compare_v1", + "semantic_type": "travel_route_city_consistency", + "field_keys": [ + "attachment.route_cities", + "attachment.hotel_city", + "claim.location", + "item.item_location", + "employee.location", + "claim.reason", + "item.item_reason", + ], + "condition_summary": ( + "A=交通票行程城市∪住宿发票城市,B=申报目的地∪明细发生地点," + "C=员工常驻地;A与B无交集且无合理说明,或A出现B∪C之外城市时命中。" + ), + "keywords": [], + "exception_keywords": ["绕行", "跨城办事", "临时改签"], + "flow": { + "start": "差旅报销提交", + "evidence": "读取票据城市、申报地点、明细地点和报销事由", + "decision": "票据城市是否覆盖申报行程,是否出现额外中转城市", + "pass": "票据城市与申报行程一致", + "fail": "票据城市与申报行程不一致,进入复核", + }, + }, + ensure_ascii=False, + ) + + def build_session() -> Session: engine = create_engine( "sqlite+pysqlite:///:memory:", @@ -58,6 +98,7 @@ def test_generate_risk_rule_asset_creates_draft_json_rule(tmp_path) -> None: AgentAssetRiskRuleGenerateRequest( business_domain=AgentAssetDomain.EXPENSE, expense_category="travel", + rule_title="差旅住宿城市一致性校验", risk_level="high", natural_language="住宿城市必须出现在本次差旅行程城市中,否则提示高风险。", ), @@ -66,6 +107,7 @@ def test_generate_risk_rule_asset_creates_draft_json_rule(tmp_path) -> None: asset = db.get(AgentAsset, asset_id) assert asset is not None + assert asset.name == "差旅住宿城市一致性校验" assert asset.status == AgentAssetStatus.DRAFT.value assert asset.config_json["detail_mode"] == "json_risk" assert asset.config_json["evaluator"] == "template_rule" @@ -78,17 +120,23 @@ def test_generate_risk_rule_asset_creates_draft_json_rule(tmp_path) -> None: rule_path = tmp_path / "rules" / RISK_RULES_LIBRARY / file_name payload = json.loads(rule_path.read_text(encoding="utf-8")) assert payload["rule_code"] == asset.code + assert payload["name"] == "差旅住宿城市一致性校验" assert payload["applies_to"]["expense_categories"] == ["travel"] assert payload["risk_category"] == "差旅费" assert payload["metadata"]["expense_category"] == "travel" + assert payload["metadata"]["rule_title"] == "差旅住宿城市一致性校验" assert payload["outcomes"]["fail"]["severity"] == "high" assert payload["template_key"] == "field_compare_v1" assert payload["metadata"]["natural_language"].startswith("住宿城市") assert payload["inputs"]["fields"] assert payload["flow_diagram_svg"].startswith(" None: assert "feDropShadow" not in payload["flow_diagram_svg"] +def test_set_risk_rule_level_updates_manifest_config_and_flow_svg(tmp_path) -> None: + with build_session() as db: + manager = AgentAssetRuleLibraryManager(rule_root=tmp_path / "rules") + generator = RiskRuleGenerationService( + db, + rule_library_manager=manager, + runtime_chat_service=NullRuntimeChatService(), + ) + + asset_id = generator.generate_rule_asset( + AgentAssetRiskRuleGenerateRequest( + business_domain=AgentAssetDomain.EXPENSE, + expense_category="travel", + rule_title="差旅住宿城市一致性校验", + risk_level="high", + natural_language="住宿城市必须出现在本次差旅行程城市中,否则提示高风险。", + ), + actor="pytest", + ) + + asset_service = AgentAssetService(db) + asset_service.rule_library_manager = manager + updated = asset_service.set_risk_rule_level( + asset_id, + risk_level="low", + actor="pytest", + ) + + assert updated.config_json["severity"] == "low" + asset = db.get(AgentAsset, asset_id) + assert asset is not None + assert asset.config_json["risk_level_label"] == "低风险" + file_name = asset.config_json["rule_document"]["file_name"] + payload = manager.read_rule_library_json( + library=RISK_RULES_LIBRARY, + file_name=file_name, + ) + assert payload["outcomes"]["fail"]["severity"] == "low" + assert payload["metadata"]["risk_level"] == "low" + assert payload["metadata"]["risk_level_label"] == "低风险" + assert "低风险" in payload["metadata"]["flow"]["fail"] + assert "#2563eb" in payload["flow_diagram_svg"] + assert "#dc2626" not in payload["flow_diagram_svg"] + + version = asset_service.repository.get_version(asset_id, asset.working_version) + assert version is not None + assert '"severity": "low"' in version.content + + +def test_enqueue_risk_rule_generation_creates_visible_generating_asset(tmp_path) -> None: + with build_session() as db: + service = RiskRuleGenerationJobService( + db, + rule_library_manager=AgentAssetRuleLibraryManager(rule_root=tmp_path / "rules"), + runtime_chat_service=NullRuntimeChatService(), + ) + request = AgentAssetRiskRuleGenerateRequest( + business_domain=AgentAssetDomain.EXPENSE, + expense_category="travel", + rule_title="差旅城市一致性校验", + risk_level="high", + natural_language="住宿城市必须出现在本次差旅行程城市中,否则提示高风险。", + ) + + asset_id = service.enqueue_rule_asset_generation(request, actor="pytest") + + asset = db.get(AgentAsset, asset_id) + assert asset is not None + assert asset.status == AgentAssetStatus.GENERATING.value + assert asset.owner == "pytest" + assert asset.name == "差旅城市一致性校验" + assert asset.config_json["generation_status"] == "generating" + assert asset.config_json["expense_category_label"] == "差旅费" + assert asset.current_version is None + detail = AgentAssetService(db).get_asset(asset_id) + assert detail is not None + assert detail.status == AgentAssetStatus.GENERATING.value + assert detail.latest_test_summary is None + + service.complete_rule_asset_generation(asset_id, request, actor="pytest") + db.refresh(asset) + + assert asset.status == AgentAssetStatus.DRAFT.value + assert asset.working_version == "v0.1.0" + assert asset.config_json["generation_status"] == "completed" + assert asset.config_json["expense_category_label"] == "差旅费" + assert asset.scenario_json == ["差旅费"] + + +def test_platform_risk_sync_skips_natural_language_drafts() -> None: + assert AgentFoundationRiskRuleMixin._is_user_generated_risk_manifest( + { + "rule_code": "risk.expense.travel.generated_20260525123000000000", + "metadata": { + "stability": "generated_draft", + "source_ref": "自然语言风险规则", + }, + } + ) + assert not AgentFoundationRiskRuleMixin._is_user_generated_risk_manifest( + { + "rule_code": "risk.travel.destination_location_mismatch", + "metadata": {"source_ref": "平台内置风险规则"}, + } + ) + + def test_risk_rule_flow_diagram_uses_risk_level_palette() -> None: renderer = RiskRuleFlowDiagramRenderer() @@ -123,10 +278,380 @@ def test_risk_rule_flow_diagram_uses_risk_level_palette() -> None: assert "#f97316" in render("medium", "中风险") high_svg = render("high", "高风险") assert "#dc2626" in high_svg - assert high_svg.count("#dc2626") == 1 + assert high_svg.count("#dc2626") >= 1 assert "#10a37f" not in high_svg +def test_risk_rule_simulation_extracts_ticket_route_cities() -> None: + with build_session() as db: + service = AgentAssetService(db) + value = service._find_attachment_field_value( + "attachment.route_cities", + "行程城市", + [ + { + "document_fields": [ + {"key": "route", "label": "行程路线", "value": "上海虹桥-武汉"} + ], + "ocr_text": "G123 上海虹桥 至 武汉 二等座", + "summary": "高铁票 上海虹桥-武汉", + } + ], + ) + + assert value == ["上海", "武汉"] + + +def test_current_keyword_city_consistency_rule_hits_ticket_city_mismatch() -> None: + manifest = { + "template_key": "keyword_match_v1", + "params": { + "template_key": "keyword_match_v1", + "field_keys": [ + "attachment.hotel_city", + "claim.location", + "attachment.route_cities", + "item.item_location", + ], + "search_fields": [ + "attachment.hotel_city", + "claim.location", + "attachment.route_cities", + "item.item_location", + ], + "natural_language": "差旅报销时,交通票或住宿票据中的城市必须与申报目的地一致;未说明绕行、跨城或改签原因时标记风险。", + "condition_summary": "检查住宿城市、申报地点、行程城市是否一致", + "keywords": ["绕行", "跨城", "改签", "变更"], + }, + "outcomes": {"fail": {"severity": "medium"}}, + } + claim = ExpenseClaim( + claim_no="TEST-CURRENT-RISK", + employee_name="测试员工", + department_name="测试部门", + expense_type="差旅费", + reason="去北京出差3天", + location="北京", + amount=Decimal("320.00"), + currency="CNY", + invoice_count=1, + occurred_at=datetime.now(UTC), + status="draft", + ) + claim.items = [ + ExpenseClaimItem( + item_date=date.today(), + item_type="交通费", + item_reason="去北京出差3天", + item_location="北京", + item_amount=Decimal("320.00"), + ) + ] + result = RiskRuleTemplateExecutor().evaluate( + manifest, + claim=claim, + contexts=[ + { + "document_info": { + "route_cities": ["武汉", "上海"], + "fields": [ + {"key": "route_cities", "label": "行程城市", "value": ["武汉", "上海"]} + ], + }, + "ocr_text": "武汉 到 上海", + } + ], + ) + + assert result is not None + assert result["evidence"]["city_consistency"]["attachment_values"] == ["武汉", "上海"] + assert result["evidence"]["city_consistency"]["reference_values"] == ["北京"] + + +def test_generate_complex_travel_route_rule_uses_formula_not_keyword_match(tmp_path) -> None: + text = ( + "差旅报销时,先检查是否已上传交通票据、住宿票据或其他能识别城市的附件;" + "再读取申报目的地、明细发生地点、交通票行程城市和住宿发票城市。" + "若交通票或住宿票据中的城市均无法与申报目的地、明细地点形成一致关系," + "且报销事由中没有说明绕行、跨城办事或临时改签原因,则标记为高风险," + "要求补充行程说明或退回修改。" + ) + with build_session() as db: + manager = AgentAssetRuleLibraryManager(rule_root=tmp_path / "rules") + service = RiskRuleGenerationService( + db, + rule_library_manager=manager, + runtime_chat_service=TravelRouteSemanticRuntimeChatService(), + ) + + asset_id = service.generate_rule_asset( + AgentAssetRiskRuleGenerateRequest( + business_domain=AgentAssetDomain.EXPENSE, + expense_category="travel", + rule_title="差旅票据路线一致性校验", + risk_level="high", + natural_language=text, + requires_attachment=True, + ), + actor="pytest", + ) + + asset = db.get(AgentAsset, asset_id) + assert asset is not None + payload = manager.read_rule_library_json( + library=RISK_RULES_LIBRARY, + file_name=asset.config_json["rule_document"]["file_name"], + ) + assert payload["template_key"] == "field_compare_v1" + assert payload["semantic_type"] == "travel_route_city_consistency" + assert payload["params"]["semantic_type"] == "travel_route_city_consistency" + assert payload["params"]["keywords"] == [] + assert payload["params"]["exception_keywords"][:3] == ["绕行", "跨城办事", "跨城"] + assert "A=交通票行程城市" in payload["params"]["condition_summary"] + assert "风险关键词" not in payload["params"]["condition_summary"] + assert "employee.location" in payload["params"]["field_keys"] + assert "route_anomaly_policy" in payload["params"] + + +def test_legacy_city_route_keyword_manifest_is_normalized_before_display_and_execution() -> None: + manifest = { + "schema_version": "2.0", + "rule_code": "risk.expense.travel.legacy_city_keyword", + "name": "差旅票据路线一致性校验", + "description": "差旅报销时读取申报目的地、明细发生地点、交通票行程城市和住宿发票城市。", + "evaluator": "template_rule", + "template_key": "keyword_match_v1", + "risk_category": "差旅费", + "inputs": { + "fields": [ + {"key": "attachment.hotel_city", "label": "住宿城市"}, + {"key": "claim.location", "label": "申报地点"}, + {"key": "attachment.route_cities", "label": "行程城市"}, + ] + }, + "params": { + "template_key": "keyword_match_v1", + "field_keys": [ + "attachment.hotel_city", + "claim.location", + "attachment.route_cities", + ], + "search_fields": [ + "attachment.hotel_city", + "claim.location", + "attachment.route_cities", + ], + "keywords": ["绕行", "跨城办事", "临时改签"], + "condition_summary": "检查住宿城市、申报地点、行程城市是否出现规则描述中的风险关键词", + "natural_language": ( + "差旅报销时,先检查是否已上传交通票据、住宿票据或其他能识别城市的附件;" + "再读取申报目的地、明细发生地点、交通票行程城市和住宿发票城市。" + "若交通票或住宿票据中的城市均无法与申报目的地、明细地点形成一致关系," + "且报销事由中没有说明绕行、跨城办事或临时改签原因,则标记为高风险。" + ), + }, + "outcomes": {"fail": {"severity": "high"}}, + "metadata": { + "condition_summary": "检查住宿城市、申报地点、行程城市是否出现规则描述中的风险关键词", + "flow": { + "decision": "检查住宿城市、申报地点、行程城市是否出现规则描述中的风险关键词" + }, + }, + "flow_diagram_svg": ( + '' + "检查住宿城市、申报地点、行程城市是否出现规则描述中的风险关键词" + "" + ), + } + + normalized = normalize_risk_rule_manifest(manifest) + assert normalized["template_key"] == "field_compare_v1" + assert normalized["semantic_type"] == "travel_route_city_consistency" + assert normalized["params"]["keywords"] == [] + assert "风险关键词" not in normalized["params"]["condition_summary"] + assert "风险关键词" not in normalized["metadata"]["flow"]["decision"] + assert "风险关键词" not in normalized["flow_diagram_svg"] + + claim = ExpenseClaim( + claim_no="TEST-LEGACY-NORMALIZER", + employee_name="测试员工", + department_name="测试部门", + expense_type="差旅费", + reason="去上海办事", + location="上海", + amount=Decimal("520.00"), + currency="CNY", + invoice_count=1, + occurred_at=datetime.now(UTC), + status="draft", + ) + claim.employee = Employee( + employee_no="TEST-EMPLOYEE", + name="测试员工", + email="legacy-route-risk@example.com", + location="武汉", + ) + claim.items = [ + ExpenseClaimItem( + item_date=date.today(), + item_type="交通费", + item_reason="去上海办事", + item_location="上海", + item_amount=Decimal("520.00"), + ) + ] + + result = RiskRuleTemplateExecutor().evaluate( + normalized, + claim=claim, + contexts=[{"document_info": {"route_cities": ["上海", "北京", "武汉"]}}], + ) + + assert result is not None + assert result["evidence"]["city_consistency"]["unexpected_route_cities"] == ["北京"] + + +def test_travel_route_rule_hits_unexpected_intermediate_city_even_when_returning_home() -> None: + manifest = { + "template_key": "field_compare_v1", + "params": { + "template_key": "field_compare_v1", + "semantic_type": "travel_route_city_consistency", + "field_keys": [ + "attachment.route_cities", + "claim.location", + "item.item_location", + "employee.location", + "claim.reason", + ], + "attachment_city_fields": ["attachment.route_cities"], + "reference_city_fields": ["claim.location", "item.item_location"], + "home_city_fields": ["employee.location"], + "exception_fields": ["claim.reason"], + "exception_keywords": ["绕行", "跨城办事", "临时改签"], + "condition_summary": "A=票据路线城市,B=申报城市,C=员工常驻地,A中出现B∪C之外城市则命中。", + }, + "outcomes": {"fail": {"severity": "high"}}, + } + claim = ExpenseClaim( + claim_no="TEST-ROUTE-ANOMALY", + employee_name="测试员工", + department_name="测试部门", + expense_type="差旅费", + reason="去上海办事", + location="上海", + amount=Decimal("520.00"), + currency="CNY", + invoice_count=1, + occurred_at=datetime.now(UTC), + status="draft", + ) + claim.employee = Employee( + employee_no="TEST-EMPLOYEE", + name="测试员工", + email="route-risk@example.com", + location="武汉", + ) + claim.items = [ + ExpenseClaimItem( + item_date=date.today(), + item_type="交通费", + item_reason="去上海办事", + item_location="上海", + item_amount=Decimal("520.00"), + ) + ] + + result = RiskRuleTemplateExecutor().evaluate( + manifest, + claim=claim, + contexts=[ + { + "document_info": { + "route_cities": ["上海", "北京", "武汉"], + "fields": [ + {"key": "route_cities", "label": "行程城市", "value": ["上海", "北京", "武汉"]} + ], + }, + "ocr_text": "上海 到 北京 到 武汉", + } + ], + ) + + assert result is not None + evidence = result["evidence"]["city_consistency"] + assert evidence["reference_values"] == ["上海"] + assert evidence["home_values"] == ["武汉"] + assert evidence["unexpected_route_cities"] == ["北京"] + + +def test_simulation_uses_current_rule_manifest_for_ticket_city_mismatch(tmp_path) -> None: + with build_session() as db: + manager = AgentAssetRuleLibraryManager(rule_root=tmp_path / "rules") + generator = RiskRuleGenerationService( + db, + rule_library_manager=manager, + runtime_chat_service=NullRuntimeChatService(), + ) + asset_id = generator.generate_rule_asset( + AgentAssetRiskRuleGenerateRequest( + business_domain=AgentAssetDomain.EXPENSE, + expense_category="travel", + rule_title="当前差旅票据城市一致性规则", + risk_level="medium", + natural_language="差旅报销时,交通票或住宿票据中的城市必须与申报目的地一致;未说明绕行、跨城或改签原因时标记风险。", + requires_attachment=True, + ), + actor="pytest", + ) + service = AgentAssetService(db) + service.rule_library_manager = manager + asset = db.get(AgentAsset, asset_id) + assert asset is not None + manifest = manager.read_rule_library_json( + library=RISK_RULES_LIBRARY, + file_name=asset.config_json["rule_document"]["file_name"], + ) + manifest["template_key"] = "keyword_match_v1" + manifest["params"]["template_key"] = "keyword_match_v1" + manifest["params"]["keywords"] = ["绕行", "跨城", "改签", "变更"] + manifest["params"]["search_fields"] = [ + "attachment.hotel_city", + "claim.location", + "attachment.route_cities", + "item.item_location", + ] + manifest["params"]["field_keys"] = manifest["params"]["search_fields"] + manager.write_rule_library_json( + library=RISK_RULES_LIBRARY, + file_name=asset.config_json["rule_document"]["file_name"], + payload=manifest, + ) + + simulation = service.simulate_risk_rule_message( + asset_id, + AgentAssetRiskRuleSimulationRequest( + message="去北京出差3天", + attachments=[ + { + "name": "train-ticket.pdf", + "content_type": "application/pdf", + "ocr_text": "武汉 到 上海", + "summary": "高铁票 武汉-上海", + "document_fields": [ + {"key": "route", "label": "行程路线", "value": "武汉-上海"} + ], + } + ], + ), + ) + + assert simulation.ready is True + assert simulation.hit is True + assert simulation.field_values["claim.location"] == "北京" + assert simulation.field_values["attachment.route_cities"] == ["武汉", "上海"] + + def test_risk_rule_requires_test_report_before_review_and_publish(tmp_path) -> None: with build_session() as db: manager = AgentAssetRuleLibraryManager(rule_root=tmp_path / "rules") diff --git a/server/tests/test_user_agent_service.py b/server/tests/test_user_agent_service.py index 9bc047a..d57d63e 100644 --- a/server/tests/test_user_agent_service.py +++ b/server/tests/test_user_agent_service.py @@ -1,5 +1,6 @@ from __future__ import annotations +import re from datetime import UTC, datetime, timedelta from decimal import Decimal @@ -208,11 +209,11 @@ def test_user_agent_application_context_uses_application_language() -> None: assert "| 字段 | 内容 |" in response.answer assert "| 发生时间 | 2026-05-25 至 2026-05-28 |" in response.answer assert "支持上海国网服务器部署" in response.answer - assert "当前还需要补充:出行方式、预计金额/预算" in response.answer + assert "当前还需要补充:出行方式、用户预估费用" in response.answer assert "请先在下面选择报销场景" not in response.answer assert response.review_payload is None assert [item.label for item in response.suggested_actions] == ["一次性补充申请信息"] - assert response.suggested_actions[0].payload["prompt_prefill"] == "出行方式:\n预计总费用:" + assert response.suggested_actions[0].payload["prompt_prefill"] == "出行方式:\n用户预估费用:" def test_user_agent_application_infers_natural_reason_and_expands_single_date() -> None: @@ -222,13 +223,38 @@ def test_user_agent_application_infers_natural_reason_and_expands_single_date() response = build_application_user_agent_response(db, message) assert "| 发生时间 | 2026-05-25 至 2026-05-28 |" in response.answer - assert "| 地点 | 上海 |" in response.answer + assert "| 地点 | 上海市 |" in response.answer assert "| 事由 | 支撑上海国网服务器部署 |" in response.answer assert "当前还需要先补充:申请事由" not in response.answer - assert "当前还需要补充:出行方式、预计金额/预算" in response.answer + assert "当前还需要补充:出行方式、用户预估费用" in response.answer assert [item.label for item in response.suggested_actions] == ["一次性补充申请信息"] +def test_user_agent_application_normalizes_location_to_region_city() -> None: + session_factory = build_session_factory() + yili_message = ( + "发生时间:2026-05-25\n" + "地点:伊犁\n" + "事由:支撑新疆电力仿生产部署\n" + "天数:3天" + ) + beijing_message = ( + "发生时间:2026-05-25\n" + "地点:北京\n" + "事由:支撑总部系统部署\n" + "天数:3天" + ) + with session_factory() as db: + yili_response = build_application_user_agent_response(db, yili_message) + beijing_response = build_application_user_agent_response(db, beijing_message) + + assert "| 发生时间 | 2026-05-25 至 2026-05-28 |" in yili_response.answer + assert "| 地点 | 新疆,伊犁 |" in yili_response.answer + assert "| 事由 | 支撑新疆电力仿生产部署 |" in yili_response.answer + assert "伊犁出差" not in yili_response.answer + assert "| 地点 | 北京市 |" in beijing_response.answer + + def test_user_agent_application_uses_selected_time_and_natural_language_fields() -> None: session_factory = build_session_factory() message = "出差上海,支撑国网服务器上线部署" @@ -262,12 +288,12 @@ def test_user_agent_application_uses_selected_time_and_natural_language_fields() ) assert "| 发生时间 | 2026-05-25 |" in response.answer - assert "| 地点 | 上海 |" in response.answer + assert "| 地点 | 上海市 |" in response.answer assert "| 事由 | 支撑国网服务器上线部署 |" in response.answer - assert "当前还需要补充:出行方式、预计金额/预算" in response.answer + assert "当前还需要补充:出行方式、用户预估费用" in response.answer assert [item.label for item in response.suggested_actions] == ["一次性补充申请信息"] assert response.suggested_actions[0].action_type == "prefill_composer" - assert response.suggested_actions[0].payload["prompt_prefill"] == "出行方式:\n预计总费用:" + assert response.suggested_actions[0].payload["prompt_prefill"] == "出行方式:\n用户预估费用:" def test_user_agent_application_asks_amount_after_transport_choice() -> None: @@ -286,10 +312,10 @@ def test_user_agent_application_asks_amount_after_transport_choice() -> None: ) assert "| 出行方式 | 飞机 |" in response.answer - assert "当前还需要补充:预计金额/预算" in response.answer + assert "当前还需要补充:用户预估费用" in response.answer assert [item.label for item in response.suggested_actions] == ["一次性补充申请信息"] assert response.suggested_actions[0].action_type == "prefill_composer" - assert response.suggested_actions[0].payload["prompt_prefill"] == "预计总费用:" + assert response.suggested_actions[0].payload["prompt_prefill"] == "用户预估费用:" def test_user_agent_application_missing_base_actions_prefill_composer() -> None: @@ -300,10 +326,10 @@ def test_user_agent_application_missing_base_actions_prefill_composer() -> None: "地点:上海\n事由:支撑国网服务器部署\n天数:3天", ) - assert "当前还需要补充:发生时间、出行方式、预计金额/预算" in response.answer + assert "当前还需要补充:发生时间、出行方式、用户预估费用" in response.answer assert [item.label for item in response.suggested_actions] == ["一次性补充申请信息"] assert response.suggested_actions[0].action_type == "prefill_composer" - assert response.suggested_actions[0].payload["prompt_prefill"] == "申请时间段:\n出行方式:\n预计总费用:" + assert response.suggested_actions[0].payload["prompt_prefill"] == "申请时间段:\n出行方式:\n用户预估费用:" def test_user_agent_application_builds_preview_when_amount_is_ready() -> None: @@ -328,7 +354,7 @@ def test_user_agent_application_builds_preview_when_amount_is_ready() -> None: assert "| 字段 | 内容 |" in response.answer assert "| 事由 | 支持上海国网服务器部署 |" in response.answer assert "| 出行方式 | 飞机 |" in response.answer - assert "| 预计总费用 | 12000元 |" in response.answer + assert "| 用户预估费用 | 12000元 |" in response.answer assert "请核对上述信息无误" in response.answer assert "[确认](#application-submit)" in response.answer assert response.requires_confirmation is True @@ -349,11 +375,11 @@ def test_user_agent_application_submit_enters_leader_review() -> None: "| --- | --- |\n" "| 申请类型 | 差旅费用申请 |\n" "| 发生时间 | 2026-05-25 |\n" - "| 地点 | 上海 |\n" + "| 地点 | 上海市 |\n" "| 事由 | 支持上海国网服务器部署 |\n" "| 天数 | 3天 |\n" "| 出行方式 | 飞机 |\n" - "| 预计总费用 | 12000元 |\n\n" + "| 用户预估费用 | 12000元 |\n\n" "请核对上述信息无误,确认无误后 [确认](#application-submit) 提交至审批流程。" ) with session_factory() as db: @@ -372,9 +398,9 @@ def test_user_agent_application_submit_enters_leader_review() -> None: assert "当前操作已完成,单据已经推送给 陈硕 进行审核,请耐心等待" in response.answer assert "当前状态:陈硕审核中" in response.answer assert "预算占用参考" in response.answer - assert "APP-20260525-" in response.answer + assert re.search(r"AP-\d{14}-[A-HJ-NP-Z2-9]{8}", response.answer) assert response.suggested_actions == [] - claim = db.query(ExpenseClaim).filter(ExpenseClaim.claim_no.like("APP-20260525-%")).one() + claim = db.query(ExpenseClaim).filter(ExpenseClaim.claim_no.like("AP-%")).one() assert claim.status == "submitted" assert claim.approval_stage == "直属领导审批" assert claim.expense_type == "travel_application" diff --git a/web/UI/预算中心.jpg b/web/UI/预算中心.jpg new file mode 100644 index 0000000000000000000000000000000000000000..da7fde6da2ad5fca2b0762a7caf6876de7b0bded GIT binary patch literal 150655 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

sfyreBXg*2w7wJ|u~fu}lcPChu-7 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 literal 0 HcmV?d00001 diff --git a/web/src/assets/styles/components/risk-rule-test-dialog.css b/web/src/assets/styles/components/risk-rule-test-dialog.css index 2ba56f7..951c990 100644 --- a/web/src/assets/styles/components/risk-rule-test-dialog.css +++ b/web/src/assets/styles/components/risk-rule-test-dialog.css @@ -36,6 +36,8 @@ .risk-sim-context-panel span, .risk-sim-result-head span, .risk-sim-evidence span, +.risk-sim-recognition-debug > span, +.risk-sim-recognized-fields > span, .risk-sim-file-strip > span { color: #64748b; font-size: 12px; @@ -115,6 +117,12 @@ color: #b91c1c; } +.risk-sim-meta .tone-critical { + border-color: #fca5a5; + background: #fff1f2; + color: #991b1b; +} + .risk-sim-main { min-height: 0; display: grid; @@ -263,6 +271,78 @@ background: rgba(255, 255, 255, 0.12); } +.risk-sim-recognition-debug { + display: grid; + gap: 8px; + margin-top: 11px; +} + +.risk-sim-recognition-debug article { + display: grid; + gap: 8px; + padding: 10px; + border: 1px solid #e2e8f0; + border-radius: 12px; + background: #f8fafc; +} + +.risk-sim-recognition-debug article header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + margin: 0; +} + +.risk-sim-recognition-debug article header strong { + min-width: 0; + overflow: hidden; + color: #0f172a; + font-size: 12px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.risk-sim-recognition-debug article header em { + flex: 0 0 auto; + color: #64748b; + font-size: 11px; + font-style: normal; +} + +.risk-sim-debug-field-list { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.risk-sim-debug-field-list b { + max-width: 100%; + display: inline-flex; + align-items: center; + min-height: 26px; + padding: 0 8px; + overflow: hidden; + border: 1px solid #dbeafe; + border-radius: 999px; + background: #eff6ff; + color: #1d4ed8; + font-size: 11px; + font-weight: 800; + text-overflow: ellipsis; + white-space: nowrap; +} + +.risk-sim-debug-ocr-text { + max-height: 112px; + overflow: auto; + padding: 8px 9px; + border: 1px solid #e2e8f0; + border-radius: 10px; + background: #fff; + color: #334155; +} + .risk-sim-result-card { margin-top: 12px; overflow: hidden; @@ -364,6 +444,53 @@ white-space: nowrap; } +.risk-sim-recognized-fields { + display: grid; + gap: 7px; + padding: 0 12px 12px; +} + +.risk-sim-recognized-fields ul { + display: grid; + gap: 6px; + margin: 0; + padding: 0; + list-style: none; +} + +.risk-sim-recognized-fields li { + min-width: 0; + display: grid; + grid-template-columns: minmax(140px, 0.75fr) auto minmax(130px, 1fr); + gap: 8px; + align-items: center; + padding: 8px 9px; + border: 1px solid #edf2f7; + border-radius: 10px; + background: #f8fafc; +} + +.risk-sim-recognized-fields strong, +.risk-sim-recognized-fields b { + min-width: 0; + overflow: hidden; + color: #0f172a; + font-size: 12px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.risk-sim-recognized-fields em { + padding: 3px 7px; + border-radius: 999px; + background: #ecfdf5; + color: #047857; + font-size: 11px; + font-style: normal; + font-weight: 850; + white-space: nowrap; +} + .risk-sim-evidence { display: grid; gap: 6px; @@ -730,6 +857,10 @@ grid-template-columns: 1fr; } + .risk-sim-recognized-fields li { + grid-template-columns: 1fr; + } + .risk-sim-foot { align-items: stretch; flex-direction: column; diff --git a/web/src/assets/styles/views/audit-view-part2.css b/web/src/assets/styles/views/audit-view-part2.css index a638365..14b4251 100644 --- a/web/src/assets/styles/views/audit-view-part2.css +++ b/web/src/assets/styles/views/audit-view-part2.css @@ -935,6 +935,7 @@ font-weight: 850; } +.risk-rule-create-form input:not([type='checkbox']), .risk-rule-create-form select, .risk-rule-create-form textarea { width: 100%; @@ -945,6 +946,7 @@ font-size: 13px; } +.risk-rule-create-form input:not([type='checkbox']), .risk-rule-create-form select { min-height: 42px; padding: 0 12px; @@ -957,6 +959,7 @@ line-height: 1.6; } +.risk-rule-create-form input:not([type='checkbox']):focus, .risk-rule-create-form select:focus, .risk-rule-create-form textarea:focus { outline: 0; @@ -964,6 +967,7 @@ box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12); } +.risk-rule-create-form input:not([type='checkbox'])::placeholder, .risk-rule-create-form textarea::placeholder { color: #94a3b8; } @@ -1045,6 +1049,118 @@ font-size: 18px; } +.risk-level-switch { + position: relative; + display: inline-flex; +} + +.minor-action.risk-level-action { + padding-right: 10px; +} + +.minor-action.risk-level-action b { + display: inline-flex; + align-items: center; + height: 20px; + padding: 0 7px; + border-radius: 999px; + font-size: 11px; + font-weight: 850; +} + +.minor-action.risk-level-action.low { + border-color: rgba(37, 99, 235, 0.22); + color: #1d4ed8; +} + +.minor-action.risk-level-action.low b { + background: #eff6ff; + color: #1d4ed8; +} + +.minor-action.risk-level-action.medium { + border-color: rgba(249, 115, 22, 0.26); + color: #c2410c; +} + +.minor-action.risk-level-action.medium b { + background: #fff7ed; + color: #c2410c; +} + +.minor-action.risk-level-action.high { + border-color: rgba(220, 38, 38, 0.22); + color: #b91c1c; +} + +.minor-action.risk-level-action.high b { + background: #fef2f2; + color: #b91c1c; +} + +.risk-level-menu { + position: absolute; + right: 0; + bottom: calc(100% + 8px); + z-index: 35; + width: 146px; + padding: 6px; + border: 1px solid #e2e8f0; + border-radius: 10px; + background: #fff; + box-shadow: 0 14px 32px rgba(15, 23, 42, 0.16); +} + +.risk-level-option { + width: 100%; + min-height: 34px; + display: flex; + align-items: center; + gap: 8px; + padding: 0 9px; + border: 0; + border-radius: 8px; + background: transparent; + color: #334155; + font-size: 12px; + font-weight: 800; + text-align: left; +} + +.risk-level-option:hover, +.risk-level-option.active { + background: #f8fafc; +} + +.risk-level-option:disabled { + cursor: not-allowed; + opacity: 0.58; +} + +.risk-level-option i { + margin-left: auto; + font-size: 15px; +} + +.risk-level-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: #94a3b8; +} + +.risk-level-option.low .risk-level-dot { + background: #2563eb; +} + +.risk-level-option.medium .risk-level-dot { + background: #f97316; +} + +.risk-level-option.high .risk-level-dot { + background: #dc2626; +} + .minor-action.danger-action { border-color: rgba(220, 38, 38, 0.2); color: #dc2626; @@ -1174,6 +1290,8 @@ .search-filter, .picker-trigger, .picker-filter, + .risk-level-switch, + .risk-level-switch .minor-action, .toolbar-actions > * { width: 100%; } @@ -1303,6 +1421,62 @@ padding-bottom: 8px; } +.json-risk-score-ring { + --score-ring: #f97316; + --score-ring-bg: #fff7ed; + flex: 0 0 auto; + width: 82px; + height: 82px; + border-radius: 999px; + display: grid; + place-items: center; + align-content: center; + gap: 1px; + border: 2px solid var(--score-ring); + background: var(--score-ring-bg); + box-shadow: 0 12px 30px rgba(15, 23, 42, 0.08); +} + +.json-risk-score-ring strong { + color: #0f172a; + font-size: 22px; + font-weight: 900; + line-height: 1; +} + +.json-risk-score-ring span, +.json-risk-score-ring em { + color: #64748b; + font-size: 11px; + font-style: normal; + font-weight: 700; + line-height: 1.1; +} + +.json-risk-score-ring em { + color: var(--score-ring); +} + +.json-risk-score-ring.low { + --score-ring: #2563eb; + --score-ring-bg: #eff6ff; +} + +.json-risk-score-ring.medium { + --score-ring: #f97316; + --score-ring-bg: #fff7ed; +} + +.json-risk-score-ring.high { + --score-ring: #dc2626; + --score-ring-bg: #fef2f2; +} + +.json-risk-score-ring.critical { + --score-ring: #991b1b; + --score-ring-bg: #fff1f2; +} + .json-risk-editor-title { min-width: 0; display: flex; @@ -1423,6 +1597,55 @@ padding: 0 2px 2px 0; } +.json-risk-generation-failure { + min-height: 360px; + display: grid; + place-items: center; + align-content: center; + gap: 14px; + padding: 44px 24px; + border: 1px solid #fecaca; + border-radius: 12px; + background: #fffafa; + text-align: center; + color: #475569; +} + +.json-risk-generation-failure i { + width: 54px; + height: 54px; + display: grid; + place-items: center; + border-radius: 18px; + background: #fef2f2; + color: #dc2626; + font-size: 28px; +} + +.json-risk-generation-failure h3 { + margin: 0; + color: #991b1b; + font-size: 18px; + font-weight: 850; +} + +.json-risk-generation-failure p { + max-width: 520px; + margin: 8px auto 0; + color: #475569; + font-size: 13px; + line-height: 1.7; +} + +.json-risk-generation-failure small { + display: block; + max-width: 640px; + margin-top: 12px; + color: #b91c1c; + line-height: 1.6; + word-break: break-word; +} + .json-risk-main-stage { min-width: 0; min-height: 0; @@ -1521,14 +1744,19 @@ color: #dc2626; } +.json-risk-meta-badge.critical { + background: #fff1f2; + color: #991b1b; +} + .json-risk-meta-badge.medium { background: #fff7ed; color: #ea580c; } .json-risk-meta-badge.low { - background: #ecfdf5; - color: #059669; + background: #eff6ff; + color: #1d4ed8; } .json-risk-meta-badge.test-passed { diff --git a/web/src/assets/styles/views/audit-view.css b/web/src/assets/styles/views/audit-view.css index ec4ed08..7f34403 100644 --- a/web/src/assets/styles/views/audit-view.css +++ b/web/src/assets/styles/views/audit-view.css @@ -1,560 +1,574 @@ -.skill-center { - height: 100%; - min-height: 0; -} - -.skill-view-enter-active, -.skill-view-leave-active { - transition: opacity 220ms ease, transform 300ms var(--ease); -} - -.skill-view-enter-from, -.skill-view-leave-to { - opacity: 0; - transform: translateY(14px); -} - -.skill-list, -.skill-detail { - height: 100%; - min-height: 0; -} - -.skill-detail { - display: grid; - grid-template-rows: minmax(0, 1fr) auto; - gap: 12px; -} - -.skill-detail.spreadsheet-skill-detail { - gap: 10px; -} - -.skill-list { - display: flex; - flex-direction: column; - min-height: 0; - padding: 18px 20px; - overflow: hidden; -} - -.status-tabs { - display: flex; - gap: 18px; - padding-bottom: 12px; - border-bottom: 1px solid #edf2f7; -} - -.status-tabs button { - position: relative; - border: 0; - background: transparent; - color: #64748b; - font-size: 14px; - font-weight: 760; -} - -.status-tabs button.active { - color: #0f172a; -} - -.status-tabs button.active::after { - content: ""; - position: absolute; - left: 0; - right: 0; - bottom: -13px; - height: 3px; - border-radius: 999px; - background: #10b981; -} - -.list-toolbar { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - padding: 14px 0 10px; -} - -.filter-set { - display: flex; - align-items: center; - gap: 10px; - flex: 1 1 auto; - flex-wrap: wrap; -} - -.search-filter { - width: 280px; - min-height: 38px; - display: inline-flex; - align-items: center; - gap: 8px; - padding: 0 11px; - border: 1px solid #d7e0ea; - border-radius: 8px; - background: #fff; - color: #64748b; -} - -.search-filter i { - color: #94a3b8; - font-size: 16px; -} - -.search-filter input { - width: 100%; - min-width: 0; - border: 0; - outline: 0; - background: transparent; - color: #0f172a; - font-size: 13px; -} - -.search-filter input::placeholder { - color: #94a3b8; -} - -.search-filter:focus-within { - border-color: rgba(16, 185, 129, 0.48); - box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1); -} - -.picker-trigger, -.ghost-filter-btn, -.create-btn, -.row-action { - min-height: 38px; - border-radius: 8px; - font-size: 13px; - font-weight: 760; -} - -.picker-filter { - position: relative; -} - -.picker-trigger { - min-width: 124px; - display: inline-flex; - align-items: center; - justify-content: space-between; - gap: 8px; - padding: 0 34px 0 12px; - border: 1px solid #d7e0ea; - background: #fff; - color: #334155; - white-space: nowrap; -} - -.picker-label { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.picker-trigger .mdi { - position: absolute; - right: 12px; - top: 50%; - transform: translateY(-50%); - color: #64748b; - pointer-events: none; -} - -.picker-trigger:hover, -.picker-filter.open .picker-trigger { - border-color: rgba(16, 185, 129, 0.34); - background: #f6fffb; - color: #0f9f78; -} - -.picker-popover { - position: absolute; - top: calc(100% + 8px); - left: 0; - width: 224px; - z-index: 40; - display: grid; - gap: 14px; - padding: 16px; - border: 1px solid #d7e0ea; - border-radius: 12px; - background: #fff; - box-shadow: 0 18px 42px rgba(15, 23, 42, 0.16); -} - -.picker-popover header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} - -.picker-popover header strong { - color: #0f172a; - font-size: 15px; -} - -.picker-popover header button { - width: 30px; - height: 30px; - display: grid; - place-items: center; - border: 0; - border-radius: 8px; - background: transparent; - color: #64748b; -} - -.picker-popover header button:hover { - background: #f1f5f9; - color: #0f172a; -} - -.picker-option-list { - display: grid; - gap: 8px; - max-height: 240px; - overflow-y: auto; -} - -.picker-option { - min-height: 36px; - display: inline-flex; - align-items: center; - padding: 0 12px; - border: 1px solid #d7e0ea; - border-radius: 8px; - background: #fff; - color: #334155; - font-size: 13px; - font-weight: 750; - text-align: left; -} - -.picker-option:hover, -.picker-option.active { - border-color: rgba(16, 185, 129, 0.32); - background: rgba(16, 185, 129, 0.08); - color: #059669; -} - -.toolbar-actions { - display: flex; - align-items: center; - gap: 10px; -} - -.ghost-filter-btn { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 0 12px; - border: 1px solid #d7e0ea; - background: #fff; - color: #475569; -} - -.ghost-filter-btn:hover { - border-color: rgba(16, 185, 129, 0.28); - color: #059669; -} - -.create-btn { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 0 14px; - border: 0; - background: #059669; - color: #fff; - box-shadow: 0 8px 18px rgba(5, 150, 105, 0.18); -} - -.create-btn:disabled { - background: #cbd5e1; - color: #f8fafc; - box-shadow: none; - cursor: not-allowed; -} - -.hint { - display: inline-flex; - align-items: center; - gap: 6px; - margin: 0 0 12px; - color: #64748b; - font-size: 12px; -} - -.hint .mdi { - color: #94a3b8; -} - -.active-filter-strip { - display: flex; - gap: 8px; - flex-wrap: wrap; - margin-bottom: 12px; -} - -.active-filter-chip { - min-height: 28px; - display: inline-flex; - align-items: center; - padding: 0 10px; - border-radius: 999px; - background: rgba(16, 185, 129, 0.1); - color: #047857; - font-size: 12px; - font-weight: 800; -} - -.table-wrap { - flex: 1 1 auto; - position: relative; - min-height: 400px; - overflow: auto; - border: 1px solid #edf2f7; - border-radius: 12px; - background: linear-gradient(180deg, #fcfefd 0%, #f4f8f6 100%); - display: flex; - flex-direction: column; - align-items: stretch; - justify-content: flex-start; -} - -.table-wrap.is-empty { - align-items: center; - justify-content: center; -} - -.table-wrap table { - width: 100%; - align-self: flex-start; -} - -.table-state, -.detail-inline-state { - width: 100%; - min-height: 220px; - display: grid; - place-items: center; - gap: 12px; - padding: 28px 24px; - text-align: center; - color: #64748b; - align-self: center; -} - -.detail-inline-state { - min-height: 180px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - text-align: left; -} - -.table-state > .table-loading, -.detail-inline-state > .table-loading { - width: 100%; -} - -.table-state i, -.detail-inline-state i { - font-size: 28px; - color: #94a3b8; -} - -.table-state.error i, -.detail-inline-state.error i { - color: #dc2626; -} - -.table-state.empty i { - color: #0ea5e9; -} - -.table-state p, -.detail-inline-state p { - margin-top: 6px; - font-size: 13px; - line-height: 1.6; -} - -.detail-inline-state strong { - color: #0f172a; - font-size: 14px; - font-weight: 850; -} - -.detail-inline-state > div { - flex: 1 1 auto; -} - -.state-action { - height: 36px; - display: inline-flex; - align-items: center; - justify-content: center; - padding: 0 14px; - border: 1px solid rgba(16, 185, 129, 0.28); - border-radius: 8px; - background: #fff; - color: #059669; - font-size: 13px; - font-weight: 760; -} - -.list-foot { - display: flex; - align-items: center; - justify-content: flex-end; - padding-top: 12px; -} - -.page-summary { - color: #64748b; - font-size: 12px; - font-weight: 700; -} - +.skill-center { + height: 100%; + min-height: 0; +} + +.skill-view-enter-active, +.skill-view-leave-active { + transition: opacity 220ms ease, transform 300ms var(--ease); +} + +.skill-view-enter-from, +.skill-view-leave-to { + opacity: 0; + transform: translateY(14px); +} + +.skill-list, +.skill-detail { + height: 100%; + min-height: 0; +} + +.skill-detail { + display: grid; + grid-template-rows: minmax(0, 1fr) auto; + gap: 12px; +} + +.skill-detail.spreadsheet-skill-detail { + gap: 10px; +} + +.skill-list { + display: flex; + flex-direction: column; + min-height: 0; + padding: 18px 20px; + overflow: hidden; +} + +.status-tabs { + display: flex; + gap: 18px; + padding-bottom: 12px; + border-bottom: 1px solid #edf2f7; +} + +.status-tabs button { + position: relative; + border: 0; + background: transparent; + color: #64748b; + font-size: 14px; + font-weight: 760; +} + +.status-tabs button.active { + color: #0f172a; +} + +.status-tabs button.active::after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: -13px; + height: 3px; + border-radius: 999px; + background: #10b981; +} + +.list-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 14px 0 10px; +} + +.filter-set { + display: flex; + align-items: center; + gap: 10px; + flex: 1 1 auto; + flex-wrap: wrap; +} + +.search-filter { + width: 280px; + min-height: 38px; + display: inline-flex; + align-items: center; + gap: 8px; + padding: 0 11px; + border: 1px solid #d7e0ea; + border-radius: 8px; + background: #fff; + color: #64748b; +} + +.search-filter i { + color: #94a3b8; + font-size: 16px; +} + +.search-filter input { + width: 100%; + min-width: 0; + border: 0; + outline: 0; + background: transparent; + color: #0f172a; + font-size: 13px; +} + +.search-filter input::placeholder { + color: #94a3b8; +} + +.search-filter:focus-within { + border-color: rgba(16, 185, 129, 0.48); + box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1); +} + +.picker-trigger, +.ghost-filter-btn, +.create-btn, +.row-action { + min-height: 38px; + border-radius: 8px; + font-size: 13px; + font-weight: 760; +} + +.picker-filter { + position: relative; +} + +.picker-trigger { + min-width: 124px; + display: inline-flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 0 34px 0 12px; + border: 1px solid #d7e0ea; + background: #fff; + color: #334155; + white-space: nowrap; +} + +.picker-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.picker-trigger .mdi { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + color: #64748b; + pointer-events: none; +} + +.picker-trigger:hover, +.picker-filter.open .picker-trigger { + border-color: rgba(16, 185, 129, 0.34); + background: #f6fffb; + color: #0f9f78; +} + +.picker-popover { + position: absolute; + top: calc(100% + 8px); + left: 0; + width: 224px; + z-index: 40; + display: grid; + gap: 14px; + padding: 16px; + border: 1px solid #d7e0ea; + border-radius: 12px; + background: #fff; + box-shadow: 0 18px 42px rgba(15, 23, 42, 0.16); +} + +.picker-popover header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.picker-popover header strong { + color: #0f172a; + font-size: 15px; +} + +.picker-popover header button { + width: 30px; + height: 30px; + display: grid; + place-items: center; + border: 0; + border-radius: 8px; + background: transparent; + color: #64748b; +} + +.picker-popover header button:hover { + background: #f1f5f9; + color: #0f172a; +} + +.picker-option-list { + display: grid; + gap: 8px; + max-height: 240px; + overflow-y: auto; +} + +.picker-option { + min-height: 36px; + display: inline-flex; + align-items: center; + padding: 0 12px; + border: 1px solid #d7e0ea; + border-radius: 8px; + background: #fff; + color: #334155; + font-size: 13px; + font-weight: 750; + text-align: left; +} + +.picker-option:hover, +.picker-option.active { + border-color: rgba(16, 185, 129, 0.32); + background: rgba(16, 185, 129, 0.08); + color: #059669; +} + +.toolbar-actions { + display: flex; + align-items: center; + gap: 10px; +} + +.ghost-filter-btn { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 0 12px; + border: 1px solid #d7e0ea; + background: #fff; + color: #475569; +} + +.ghost-filter-btn:hover { + border-color: rgba(16, 185, 129, 0.28); + color: #059669; +} + +.create-btn { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 0 14px; + border: 0; + background: #059669; + color: #fff; + box-shadow: 0 8px 18px rgba(5, 150, 105, 0.18); +} + +.create-btn:disabled { + background: #cbd5e1; + color: #f8fafc; + box-shadow: none; + cursor: not-allowed; +} + +.hint { + display: inline-flex; + align-items: center; + gap: 6px; + margin: 0 0 12px; + color: #64748b; + font-size: 12px; +} + +.hint .mdi { + color: #94a3b8; +} + +.active-filter-strip { + display: flex; + gap: 8px; + flex-wrap: wrap; + margin-bottom: 12px; +} + +.active-filter-chip { + min-height: 28px; + display: inline-flex; + align-items: center; + padding: 0 10px; + border-radius: 999px; + background: rgba(16, 185, 129, 0.1); + color: #047857; + font-size: 12px; + font-weight: 800; +} + +.table-wrap { + flex: 1 1 auto; + position: relative; + min-height: 400px; + overflow: auto; + border: 1px solid #edf2f7; + border-radius: 12px; + background: linear-gradient(180deg, #fcfefd 0%, #f4f8f6 100%); + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: flex-start; +} + +.table-wrap.is-empty { + align-items: center; + justify-content: center; +} + +.table-wrap table { + width: 100%; + align-self: flex-start; +} + +.table-state, +.detail-inline-state { + width: 100%; + min-height: 220px; + display: grid; + place-items: center; + gap: 12px; + padding: 28px 24px; + text-align: center; + color: #64748b; + align-self: center; +} + +.detail-inline-state { + min-height: 180px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + text-align: left; +} + +.table-state > .table-loading, +.detail-inline-state > .table-loading { + width: 100%; +} + +.table-state i, +.detail-inline-state i { + font-size: 28px; + color: #94a3b8; +} + +.table-state.error i, +.detail-inline-state.error i { + color: #dc2626; +} + +.table-state.empty i { + color: #0ea5e9; +} + +.table-state p, +.detail-inline-state p { + margin-top: 6px; + font-size: 13px; + line-height: 1.6; +} + +.detail-inline-state strong { + color: #0f172a; + font-size: 14px; + font-weight: 850; +} + +.detail-inline-state > div { + flex: 1 1 auto; +} + +.state-action { + height: 36px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 14px; + border: 1px solid rgba(16, 185, 129, 0.28); + border-radius: 8px; + background: #fff; + color: #059669; + font-size: 13px; + font-weight: 760; +} + +.list-foot { + display: flex; + align-items: center; + justify-content: flex-end; + padding-top: 12px; +} + +.page-summary { + color: #64748b; + font-size: 12px; + font-weight: 700; +} + table { width: 100%; min-width: 1260px; border-collapse: collapse; } - -th, -td { - padding: 14px 12px; - border-bottom: 1px solid #edf2f7; - text-align: center; - vertical-align: middle; - color: #334155; - font-size: 12px; -} - -th { - background: #f8fafc; - color: #64748b; - font-weight: 800; - white-space: nowrap; -} - -tbody tr { - cursor: pointer; - transition: background 180ms ease; -} - -th:first-child, -td:first-child { - text-align: left; -} - -tbody tr:hover { - background: #f8fbff; -} - -.skill-name-cell { - display: grid; - grid-template-columns: 38px minmax(0, 1fr); - gap: 10px; - align-items: center; -} - -.skill-avatar { - width: 38px; - height: 38px; - display: grid; - place-items: center; - border-radius: 11px; - color: #fff; - font-size: 13px; - font-weight: 900; -} - -.skill-avatar.emerald { background: linear-gradient(135deg, #10b981, #059669); } -.skill-avatar.rose { background: linear-gradient(135deg, #f43f5e, #e11d48); } -.skill-avatar.violet { background: linear-gradient(135deg, #8b5cf6, #7c3aed); } -.skill-avatar.blue { background: linear-gradient(135deg, #3b82f6, #2563eb); } -.skill-avatar.amber { background: linear-gradient(135deg, #f59e0b, #ea580c); } - -.skill-name-cell strong { - display: block; - color: #0f172a; - font-size: 13px; - font-weight: 850; -} - -.skill-name-cell span:last-child { - display: block; - margin-top: 4px; - color: #64748b; - font-size: 12px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.scope-pill, -.status-pill { - display: inline-flex; - align-items: center; - justify-content: center; - min-height: 26px; - padding: 0 9px; - border-radius: 999px; - font-size: 11px; - font-weight: 800; - white-space: nowrap; -} - -.scope-pill { - background: #f1f5f9; - color: #475569; -} - -.status-pill.success { - background: #dcfce7; - color: #059669; -} - -.status-pill.warning { - background: #fff7ed; - color: #ea580c; -} - -.status-pill.draft { - background: #eef2ff; - color: #6366f1; -} - -.status-pill.danger { - background: #fee2e2; - color: #dc2626; -} - -.status-pill.disabled { - background: #e2e8f0; - color: #475569; -} - -.row-action { - padding: 0 12px; - border: 1px solid rgba(16, 185, 129, 0.32); - background: #fff; - color: #059669; -} - -.row-action:hover { - background: rgba(16, 185, 129, 0.08); -} - + +th, +td { + padding: 14px 12px; + border-bottom: 1px solid #edf2f7; + text-align: center; + vertical-align: middle; + color: #334155; + font-size: 12px; +} + +th { + background: #f8fafc; + color: #64748b; + font-weight: 800; + white-space: nowrap; +} + +tbody tr { + cursor: pointer; + transition: background 180ms ease; +} + +th:first-child, +td:first-child { + text-align: left; +} + +tbody tr:hover { + background: #f8fbff; +} + +tbody tr.is-disabled { + cursor: not-allowed; + opacity: 0.72; +} + +tbody tr.is-disabled:hover { + background: transparent; +} + +.skill-name-cell { + display: grid; + grid-template-columns: 38px minmax(0, 1fr); + gap: 10px; + align-items: center; +} + +.skill-avatar { + width: 38px; + height: 38px; + display: grid; + place-items: center; + border-radius: 11px; + color: #fff; + font-size: 13px; + font-weight: 900; +} + +.skill-avatar.emerald { background: linear-gradient(135deg, #10b981, #059669); } +.skill-avatar.rose { background: linear-gradient(135deg, #f43f5e, #e11d48); } +.skill-avatar.violet { background: linear-gradient(135deg, #8b5cf6, #7c3aed); } +.skill-avatar.blue { background: linear-gradient(135deg, #3b82f6, #2563eb); } +.skill-avatar.amber { background: linear-gradient(135deg, #f59e0b, #ea580c); } + +.skill-name-cell strong { + display: block; + color: #0f172a; + font-size: 13px; + font-weight: 850; +} + +.skill-name-cell span:last-child { + display: block; + margin-top: 4px; + color: #64748b; + font-size: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.scope-pill, +.status-pill { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 26px; + padding: 0 9px; + border-radius: 999px; + font-size: 11px; + font-weight: 800; + white-space: nowrap; +} + +.scope-pill { + background: #f1f5f9; + color: #475569; +} + +.status-pill.success { + background: #dcfce7; + color: #059669; +} + +.status-pill.warning { + background: #fff7ed; + color: #ea580c; +} + +.status-pill.draft { + background: #eef2ff; + color: #6366f1; +} + +.status-pill.info { + background: #e0f2fe; + color: #0284c7; +} + +.status-pill.danger { + background: #fee2e2; + color: #dc2626; +} + +.status-pill.disabled { + background: #e2e8f0; + color: #475569; +} + +.row-action { + padding: 0 12px; + border: 1px solid rgba(16, 185, 129, 0.32); + background: #fff; + color: #059669; +} + +.row-action:hover { + background: rgba(16, 185, 129, 0.08); +} + .detail-scroll { height: 100%; overflow: auto; @@ -582,920 +596,920 @@ tbody tr:hover { min-height: 260px; align-self: center; } - -.detail-hero { - display: grid; - grid-template-columns: minmax(0, 1fr) 320px; - align-items: start; - gap: 10px; - padding: 16px 20px; -} - -.hero-title { - min-width: 0; -} - -.skill-badge { - display: inline-flex; - align-items: center; - min-height: 24px; - padding: 0 8px; - border-radius: 999px; - color: #fff; - font-size: 11px; - font-weight: 800; -} - -.skill-badge.emerald { background: linear-gradient(135deg, #10b981, #059669); } -.skill-badge.rose { background: linear-gradient(135deg, #f43f5e, #e11d48); } -.skill-badge.violet { background: linear-gradient(135deg, #8b5cf6, #7c3aed); } -.skill-badge.blue { background: linear-gradient(135deg, #3b82f6, #2563eb); } -.skill-badge.amber { background: linear-gradient(135deg, #f59e0b, #ea580c); } - -.hero-title h2 { - margin-top: 10px; - color: #0f172a; - font-size: 24px; - font-weight: 850; -} - -.hero-title p { - margin-top: 8px; - max-width: 820px; - color: #64748b; - font-size: 14px; - line-height: 1.6; -} - -.hero-review-meta { - display: flex; - align-items: center; - gap: 10px; - flex-wrap: wrap; - margin-top: 12px; -} - -.inline-review-meta { - margin-top: 10px; -} - -.review-note-block { - display: grid; - gap: 6px; - margin-top: 12px; - padding: 12px 14px; - border: 1px solid rgba(16, 185, 129, 0.16); - border-radius: 12px; - background: linear-gradient(180deg, #f8fffc, #ffffff); -} - -.review-note-block strong { - color: #0f172a; - font-size: 13px; - font-weight: 850; -} - -.review-note-block p, -.review-note-block span { - color: #64748b; - font-size: 12px; - line-height: 1.6; -} - -.review-note-block.muted { - border-color: #e2e8f0; - background: #f8fafc; -} - -.hero-review-meta span { - display: inline-flex; - align-items: center; - gap: 5px; - min-height: 26px; - padding: 0 9px; - border-radius: 999px; - background: #f8fafc; - color: #475569; - font-size: 12px; - font-weight: 800; -} - -.hero-review-meta i { - color: #059669; - font-size: 15px; -} - -.hero-stats { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 8px; -} - -.hero-stat { - min-height: 54px; - display: grid; - align-content: center; - gap: 4px; - padding: 9px 12px; - border-radius: 10px; - background: #f8fafc; - border: 1px solid #e5eaf0; -} - -.hero-stat span { - display: block; - color: #64748b; - font-size: 11px; - font-weight: 800; - line-height: 1.2; -} - -.hero-stat strong { - display: block; - color: #0f172a; - font-size: 15px; - font-weight: 850; - line-height: 1.25; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.hero-stat .status-pill { - width: fit-content; - min-height: 22px; - padding: 0 8px; -} - -.detail-grid { - display: grid; - grid-template-columns: minmax(0, 1.35fr) minmax(320px, 0.78fr); - gap: 16px; -} - -.detail-grid.skill-md-detail-grid { - grid-template-columns: minmax(0, 1fr) 372px; - align-items: stretch; -} - -.detail-grid.skill-md-detail-grid .detail-main { - height: 100%; -} - -.detail-main, -.detail-side { - display: grid; - gap: 16px; - align-content: start; -} - -.detail-card, -.side-card { - padding: 18px; -} - -.card-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 16px; - margin-bottom: 16px; -} - -.card-head h3 { - color: #0f172a; - font-size: 16px; - font-weight: 850; -} - -.card-head p { - margin-top: 6px; - color: #64748b; - font-size: 13px; - line-height: 1.5; -} - -.edit-badge { - display: inline-flex; - align-items: center; - min-height: 28px; - padding: 0 10px; - border-radius: 999px; - background: #ecfeff; - color: #0891b2; - font-size: 12px; - font-weight: 800; -} - -.form-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 14px; -} - -.field { - display: grid; - gap: 7px; -} - -.field.span-2 { - grid-column: span 2; -} - -.field span { - color: #64748b; - font-size: 12px; - font-weight: 800; -} - -.field input, -.field textarea, -.prompt-block textarea { - width: 100%; - border: 1px solid #d7e0ea; - border-radius: 10px; - background: #fff; - color: #0f172a; - font-size: 13px; - line-height: 1.55; - padding: 10px 12px; - resize: vertical; -} - -.field input[readonly], -.field textarea[readonly], -.prompt-block textarea[readonly] { - background: #f8fafc; - color: #334155; -} - -.markdown-card { - min-height: 620px; - display: grid; - grid-template-rows: auto minmax(0, 1fr); -} - -.json-editor-card { - display: grid; - gap: 12px; -} - -.spreadsheet-rule-card { - display: grid; - gap: 14px; -} - -.spreadsheet-editor-shell { - min-height: 0; - height: 100%; - display: flex; - flex-direction: column; - gap: 8px; - padding: 10px; -} - -.spreadsheet-editor-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 16px; -} - -.spreadsheet-editor-title { - min-width: 0; - display: flex; - align-items: flex-start; - gap: 12px; -} - -.spreadsheet-editor-title h2 { - color: #0f172a; - font-size: 18px; - font-weight: 850; -} - -.spreadsheet-editor-title p { - margin-top: 2px; - max-width: 760px; - color: #64748b; - font-size: 12px; - line-height: 1.4; -} - -.spreadsheet-editor-actions { - display: flex; - align-items: center; - gap: 10px; - flex-wrap: wrap; -} - -.spreadsheet-editor-meta { - display: flex; - gap: 8px; - flex-wrap: wrap; -} - -.spreadsheet-editor-meta span { - min-height: 28px; - display: inline-flex; - align-items: center; - gap: 6px; - padding: 0 10px; - border-radius: 999px; - background: #f8fafc; - color: #475569; - font-size: 12px; - font-weight: 750; -} - -.spreadsheet-editor-meta strong { - color: #0f172a; - font-weight: 850; -} - -.spreadsheet-editor-body { - flex: 1 1 auto; - min-height: 0; - display: grid; - grid-template-columns: minmax(0, 1fr) 286px; - gap: 14px; - align-items: start; -} - -.spreadsheet-main-stage { - min-height: 0; - height: 100%; - display: flex; - flex-direction: column; - gap: 10px; -} - -.spreadsheet-workbench { - flex: 1 1 auto; - position: relative; - min-height: 0; - overflow: visible; - border: 1px solid #dbe4ee; - border-radius: 12px; - background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%); -} - -.spreadsheet-workbench .rule-spreadsheet-host { - min-height: 0; - height: 100%; - overflow: visible; -} - -.spreadsheet-editor-foot { - display: flex; - align-items: center; - justify-content: space-between; - gap: 14px; - color: #64748b; - font-size: 12px; - line-height: 1.5; -} - -.spreadsheet-change-center { - min-height: 0; - height: 100%; - align-self: stretch; - display: grid; - grid-template-rows: auto minmax(0, 1fr) auto; - gap: 12px; - padding: 14px; - border: 1px solid #dbe4ee; - border-radius: 14px; - background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%); - overflow: hidden; -} - -.change-center-head h3, -.change-center-head p, -.change-center-section header, -.change-center-section p { - margin: 0; -} - -.change-center-head h3 { - color: #0f172a; - font-size: 15px; - font-weight: 900; -} - -.change-center-head p { - margin-top: 3px; - color: #64748b; - font-size: 12px; -} - -.version-pair-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 8px; -} - -.version-pair-card { - display: grid; - gap: 4px; - min-height: 84px; - padding: 10px; - border-radius: 12px; -} - -.version-pair-card span { - color: #64748b; - font-size: 11px; - font-weight: 800; -} - -.version-pair-card strong { - color: #0f172a; - font-size: 16px; - font-weight: 900; -} - -.version-pair-card b { - width: fit-content; - min-height: 20px; - display: inline-flex; - align-items: center; - padding: 0 7px; - border-radius: 999px; - font-size: 11px; - font-weight: 850; -} - -.version-pair-card.published { - background: rgba(16, 185, 129, 0.1); -} - -.version-pair-card.published b { - background: #dcfce7; - color: #059669; -} - -.version-pair-card.working { - background: rgba(37, 99, 235, 0.08); -} - -.version-pair-card.working b { - background: #dbeafe; - color: #2563eb; -} - -.change-center-section { - display: grid; - gap: 8px; -} - -.change-history-section { - min-height: 0; - grid-template-rows: auto minmax(0, 1fr); -} - -.change-center-section > header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; -} - -.change-center-section > header strong { - color: #0f172a; - font-size: 13px; - font-weight: 900; -} - -.change-center-section > header small, -.change-center-section > header button { - color: #64748b; - font-size: 11px; - font-weight: 800; -} - -.change-center-section > header button { - padding: 0; - border: 0; - background: transparent; - color: #2563eb; - cursor: pointer; -} - -.change-center-list { - display: grid; - align-content: start; - gap: 8px; - min-height: 0; - overflow-y: auto; - padding-right: 2px; -} - -.change-center-item { - display: grid; - gap: 8px; - padding: 10px; - border: 1px solid #e2e8f0; - border-radius: 12px; - background: #fff; -} - -.change-center-item.active { - border-color: rgba(16, 185, 129, 0.35); - background: rgba(16, 185, 129, 0.05); -} - -.change-center-item > button { - display: grid; - gap: 5px; - padding: 0; - border: 0; - background: transparent; - text-align: left; - cursor: pointer; -} - -.change-center-item > button:disabled { - cursor: default; -} - -.change-center-item > button div { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; -} - -.change-center-item > button strong { - color: #0f172a; - font-size: 13px; - font-weight: 900; -} - -.change-center-item > button span, -.change-center-item > button p, -.change-center-item > button small { - color: #64748b; - font-size: 11px; -} - -.change-center-item > button p { - margin: 0; - line-height: 1.45; -} - -.change-record-summary-empty { - color: #64748b; - font-size: 11px; - line-height: 1.5; -} - -.change-record-item { - gap: 10px; - width: 100%; - border: 1px solid #e2e8f0; - background: #fff; - color: inherit; - text-align: left; - cursor: pointer; - transition: border-color 180ms ease, box-shadow 180ms ease, transform 180ms ease; -} - -.change-record-item:hover { - border-color: rgba(16, 185, 129, 0.35); - box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08); - transform: translateY(-1px); -} - -.change-record-item:focus-visible { - outline: 3px solid rgba(16, 185, 129, 0.18); - outline-offset: 1px; -} - -.change-record-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 10px; -} - -.change-record-head > div { - display: grid; - gap: 3px; -} - -.change-record-head strong { - color: #0f172a; - font-size: 13px; - font-weight: 900; -} - -.change-record-head span, -.change-record-item p, -.change-record-more { - color: #64748b; - font-size: 11px; -} - -.change-record-head b { - min-height: 22px; - display: inline-flex; - align-items: center; - padding: 0 8px; - border-radius: 999px; - background: #eef2ff; - color: #4f46e5; - font-size: 11px; - font-weight: 850; -} - -.change-record-item p { - margin: 0; - line-height: 1.5; -} - -.change-record-preview { - display: grid; - gap: 6px; -} - -.change-record-preview span { - display: block; - padding: 7px 8px; - border-radius: 10px; - background: #f8fafc; - color: #334155; - font-size: 11px; - line-height: 1.45; - overflow-wrap: anywhere; -} - -.change-record-more { - font-weight: 800; -} - -.change-detail-content { - gap: 14px; -} - -.change-detail-meta { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 10px; -} - -.change-detail-meta article { - display: grid; - gap: 4px; - min-height: 0; - padding: 10px 12px; - border-radius: 14px; - background: #f8fafc; -} - -.change-detail-meta span { - color: #64748b; - font-size: 12px; -} - -.change-detail-meta strong { - color: #0f172a; - font-size: 15px; - font-weight: 900; -} - -.change-flow-empty { - color: #64748b; - font-size: 11px; -} - -.rule-drawer-backdrop { - position: fixed; - inset: 0; - z-index: 80; - display: flex; - justify-content: flex-end; - background: rgba(15, 23, 42, 0.34); - backdrop-filter: blur(3px); -} - -.rule-drawer { - width: min(860px, 78vw); - height: 100%; - display: grid; - grid-template-rows: auto auto minmax(0, 1fr); - gap: 18px; - padding: 22px; - overflow: auto; - background: #fff; - box-shadow: -18px 0 42px rgba(15, 23, 42, 0.18); -} - -.timeline-drawer { - width: min(640px, 62vw); -} - -.rule-drawer-head { - display: flex; - align-items: start; - justify-content: space-between; - gap: 16px; -} - -.rule-drawer-head span { - color: #2563eb; - font-size: 12px; - font-weight: 850; -} - -.rule-drawer-head h3 { - margin: 4px 0 0; - color: #0f172a; - font-size: 20px; - font-weight: 950; -} - -.rule-drawer-head button { - width: 34px; - height: 34px; - border: 0; - border-radius: 999px; - background: #f1f5f9; - color: #475569; - cursor: pointer; -} - -.rule-drawer-state { - min-height: 160px; - display: grid; - place-items: center; - gap: 10px; - color: #64748b; -} - -.rule-drawer-state.error { - color: #dc2626; -} - -.rule-timeline-list { - display: grid; - align-content: start; -} - -.rule-timeline-item { - position: relative; - display: grid; - grid-template-columns: 30px minmax(0, 1fr); - gap: 12px; - padding-bottom: 20px; -} - -.rule-timeline-item:not(:last-child)::after { - content: ''; - position: absolute; - left: 14px; - top: 28px; - bottom: 0; - width: 2px; - background: #e2e8f0; -} - -.rule-timeline-item > i { - position: relative; - z-index: 1; - width: 30px; - height: 30px; - display: grid; - place-items: center; - border-radius: 999px; - background: #f1f5f9; - color: #475569; -} - -.rule-timeline-item > i.success { - background: #dcfce7; - color: #059669; -} - -.rule-timeline-item > i.warning { - background: #fef3c7; - color: #d97706; -} - -.rule-timeline-item > i.danger { - background: #fee2e2; - color: #dc2626; -} - -.rule-timeline-item > i.info { - background: #dbeafe; - color: #2563eb; -} - -.rule-timeline-item header { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px; -} - -.rule-timeline-item header strong { - color: #0f172a; - font-size: 14px; -} - -.rule-timeline-item header b { - min-height: 22px; - display: inline-flex; - align-items: center; - padding: 0 8px; - border-radius: 999px; - background: #f1f5f9; - color: #475569; - font-size: 11px; -} - -.rule-timeline-item header span, -.rule-timeline-item p, -.rule-timeline-item small { - color: #64748b; -} - -.rule-timeline-item p { - margin: 8px 0 4px; - line-height: 1.6; -} - -.compare-toolbar { - display: flex; - align-items: end; - gap: 12px; -} - -.compare-toolbar label { - min-width: 0; - display: grid; - gap: 6px; - flex: 1; -} - -.compare-toolbar span { - color: #64748b; - font-size: 12px; - font-weight: 850; -} - -.compare-toolbar select { - width: 100%; - min-height: 40px; - padding: 0 12px; - border: 1px solid #cbd5e1; - border-radius: 12px; - background: #fff; -} - -.compare-toolbar i { - margin-bottom: 10px; - color: #94a3b8; -} - -.compare-content { - min-height: 0; - display: grid; - grid-template-rows: auto auto minmax(0, 1fr); - gap: 18px; -} - -.compare-summary-grid { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 10px; -} - -.compare-summary-grid article { - display: grid; - gap: 4px; - align-content: center; - min-height: 0; - padding: 10px 12px; - border-radius: 14px; - background: #f8fafc; -} - -.compare-summary-grid span { - color: #64748b; - font-size: 12px; -} - -.compare-summary-grid strong { - color: #0f172a; - font-size: 22px; - font-weight: 950; - line-height: 1.1; -} - -.compare-panel { - display: grid; - gap: 10px; -} - + +.detail-hero { + display: grid; + grid-template-columns: minmax(0, 1fr) 320px; + align-items: start; + gap: 10px; + padding: 16px 20px; +} + +.hero-title { + min-width: 0; +} + +.skill-badge { + display: inline-flex; + align-items: center; + min-height: 24px; + padding: 0 8px; + border-radius: 999px; + color: #fff; + font-size: 11px; + font-weight: 800; +} + +.skill-badge.emerald { background: linear-gradient(135deg, #10b981, #059669); } +.skill-badge.rose { background: linear-gradient(135deg, #f43f5e, #e11d48); } +.skill-badge.violet { background: linear-gradient(135deg, #8b5cf6, #7c3aed); } +.skill-badge.blue { background: linear-gradient(135deg, #3b82f6, #2563eb); } +.skill-badge.amber { background: linear-gradient(135deg, #f59e0b, #ea580c); } + +.hero-title h2 { + margin-top: 10px; + color: #0f172a; + font-size: 24px; + font-weight: 850; +} + +.hero-title p { + margin-top: 8px; + max-width: 820px; + color: #64748b; + font-size: 14px; + line-height: 1.6; +} + +.hero-review-meta { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; + margin-top: 12px; +} + +.inline-review-meta { + margin-top: 10px; +} + +.review-note-block { + display: grid; + gap: 6px; + margin-top: 12px; + padding: 12px 14px; + border: 1px solid rgba(16, 185, 129, 0.16); + border-radius: 12px; + background: linear-gradient(180deg, #f8fffc, #ffffff); +} + +.review-note-block strong { + color: #0f172a; + font-size: 13px; + font-weight: 850; +} + +.review-note-block p, +.review-note-block span { + color: #64748b; + font-size: 12px; + line-height: 1.6; +} + +.review-note-block.muted { + border-color: #e2e8f0; + background: #f8fafc; +} + +.hero-review-meta span { + display: inline-flex; + align-items: center; + gap: 5px; + min-height: 26px; + padding: 0 9px; + border-radius: 999px; + background: #f8fafc; + color: #475569; + font-size: 12px; + font-weight: 800; +} + +.hero-review-meta i { + color: #059669; + font-size: 15px; +} + +.hero-stats { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; +} + +.hero-stat { + min-height: 54px; + display: grid; + align-content: center; + gap: 4px; + padding: 9px 12px; + border-radius: 10px; + background: #f8fafc; + border: 1px solid #e5eaf0; +} + +.hero-stat span { + display: block; + color: #64748b; + font-size: 11px; + font-weight: 800; + line-height: 1.2; +} + +.hero-stat strong { + display: block; + color: #0f172a; + font-size: 15px; + font-weight: 850; + line-height: 1.25; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.hero-stat .status-pill { + width: fit-content; + min-height: 22px; + padding: 0 8px; +} + +.detail-grid { + display: grid; + grid-template-columns: minmax(0, 1.35fr) minmax(320px, 0.78fr); + gap: 16px; +} + +.detail-grid.skill-md-detail-grid { + grid-template-columns: minmax(0, 1fr) 372px; + align-items: stretch; +} + +.detail-grid.skill-md-detail-grid .detail-main { + height: 100%; +} + +.detail-main, +.detail-side { + display: grid; + gap: 16px; + align-content: start; +} + +.detail-card, +.side-card { + padding: 18px; +} + +.card-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + margin-bottom: 16px; +} + +.card-head h3 { + color: #0f172a; + font-size: 16px; + font-weight: 850; +} + +.card-head p { + margin-top: 6px; + color: #64748b; + font-size: 13px; + line-height: 1.5; +} + +.edit-badge { + display: inline-flex; + align-items: center; + min-height: 28px; + padding: 0 10px; + border-radius: 999px; + background: #ecfeff; + color: #0891b2; + font-size: 12px; + font-weight: 800; +} + +.form-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; +} + +.field { + display: grid; + gap: 7px; +} + +.field.span-2 { + grid-column: span 2; +} + +.field span { + color: #64748b; + font-size: 12px; + font-weight: 800; +} + +.field input, +.field textarea, +.prompt-block textarea { + width: 100%; + border: 1px solid #d7e0ea; + border-radius: 10px; + background: #fff; + color: #0f172a; + font-size: 13px; + line-height: 1.55; + padding: 10px 12px; + resize: vertical; +} + +.field input[readonly], +.field textarea[readonly], +.prompt-block textarea[readonly] { + background: #f8fafc; + color: #334155; +} + +.markdown-card { + min-height: 620px; + display: grid; + grid-template-rows: auto minmax(0, 1fr); +} + +.json-editor-card { + display: grid; + gap: 12px; +} + +.spreadsheet-rule-card { + display: grid; + gap: 14px; +} + +.spreadsheet-editor-shell { + min-height: 0; + height: 100%; + display: flex; + flex-direction: column; + gap: 8px; + padding: 10px; +} + +.spreadsheet-editor-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; +} + +.spreadsheet-editor-title { + min-width: 0; + display: flex; + align-items: flex-start; + gap: 12px; +} + +.spreadsheet-editor-title h2 { + color: #0f172a; + font-size: 18px; + font-weight: 850; +} + +.spreadsheet-editor-title p { + margin-top: 2px; + max-width: 760px; + color: #64748b; + font-size: 12px; + line-height: 1.4; +} + +.spreadsheet-editor-actions { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.spreadsheet-editor-meta { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.spreadsheet-editor-meta span { + min-height: 28px; + display: inline-flex; + align-items: center; + gap: 6px; + padding: 0 10px; + border-radius: 999px; + background: #f8fafc; + color: #475569; + font-size: 12px; + font-weight: 750; +} + +.spreadsheet-editor-meta strong { + color: #0f172a; + font-weight: 850; +} + +.spreadsheet-editor-body { + flex: 1 1 auto; + min-height: 0; + display: grid; + grid-template-columns: minmax(0, 1fr) 286px; + gap: 14px; + align-items: start; +} + +.spreadsheet-main-stage { + min-height: 0; + height: 100%; + display: flex; + flex-direction: column; + gap: 10px; +} + +.spreadsheet-workbench { + flex: 1 1 auto; + position: relative; + min-height: 0; + overflow: visible; + border: 1px solid #dbe4ee; + border-radius: 12px; + background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%); +} + +.spreadsheet-workbench .rule-spreadsheet-host { + min-height: 0; + height: 100%; + overflow: visible; +} + +.spreadsheet-editor-foot { + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px; + color: #64748b; + font-size: 12px; + line-height: 1.5; +} + +.spreadsheet-change-center { + min-height: 0; + height: 100%; + align-self: stretch; + display: grid; + grid-template-rows: auto minmax(0, 1fr) auto; + gap: 12px; + padding: 14px; + border: 1px solid #dbe4ee; + border-radius: 14px; + background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%); + overflow: hidden; +} + +.change-center-head h3, +.change-center-head p, +.change-center-section header, +.change-center-section p { + margin: 0; +} + +.change-center-head h3 { + color: #0f172a; + font-size: 15px; + font-weight: 900; +} + +.change-center-head p { + margin-top: 3px; + color: #64748b; + font-size: 12px; +} + +.version-pair-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; +} + +.version-pair-card { + display: grid; + gap: 4px; + min-height: 84px; + padding: 10px; + border-radius: 12px; +} + +.version-pair-card span { + color: #64748b; + font-size: 11px; + font-weight: 800; +} + +.version-pair-card strong { + color: #0f172a; + font-size: 16px; + font-weight: 900; +} + +.version-pair-card b { + width: fit-content; + min-height: 20px; + display: inline-flex; + align-items: center; + padding: 0 7px; + border-radius: 999px; + font-size: 11px; + font-weight: 850; +} + +.version-pair-card.published { + background: rgba(16, 185, 129, 0.1); +} + +.version-pair-card.published b { + background: #dcfce7; + color: #059669; +} + +.version-pair-card.working { + background: rgba(37, 99, 235, 0.08); +} + +.version-pair-card.working b { + background: #dbeafe; + color: #2563eb; +} + +.change-center-section { + display: grid; + gap: 8px; +} + +.change-history-section { + min-height: 0; + grid-template-rows: auto minmax(0, 1fr); +} + +.change-center-section > header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.change-center-section > header strong { + color: #0f172a; + font-size: 13px; + font-weight: 900; +} + +.change-center-section > header small, +.change-center-section > header button { + color: #64748b; + font-size: 11px; + font-weight: 800; +} + +.change-center-section > header button { + padding: 0; + border: 0; + background: transparent; + color: #2563eb; + cursor: pointer; +} + +.change-center-list { + display: grid; + align-content: start; + gap: 8px; + min-height: 0; + overflow-y: auto; + padding-right: 2px; +} + +.change-center-item { + display: grid; + gap: 8px; + padding: 10px; + border: 1px solid #e2e8f0; + border-radius: 12px; + background: #fff; +} + +.change-center-item.active { + border-color: rgba(16, 185, 129, 0.35); + background: rgba(16, 185, 129, 0.05); +} + +.change-center-item > button { + display: grid; + gap: 5px; + padding: 0; + border: 0; + background: transparent; + text-align: left; + cursor: pointer; +} + +.change-center-item > button:disabled { + cursor: default; +} + +.change-center-item > button div { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.change-center-item > button strong { + color: #0f172a; + font-size: 13px; + font-weight: 900; +} + +.change-center-item > button span, +.change-center-item > button p, +.change-center-item > button small { + color: #64748b; + font-size: 11px; +} + +.change-center-item > button p { + margin: 0; + line-height: 1.45; +} + +.change-record-summary-empty { + color: #64748b; + font-size: 11px; + line-height: 1.5; +} + +.change-record-item { + gap: 10px; + width: 100%; + border: 1px solid #e2e8f0; + background: #fff; + color: inherit; + text-align: left; + cursor: pointer; + transition: border-color 180ms ease, box-shadow 180ms ease, transform 180ms ease; +} + +.change-record-item:hover { + border-color: rgba(16, 185, 129, 0.35); + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08); + transform: translateY(-1px); +} + +.change-record-item:focus-visible { + outline: 3px solid rgba(16, 185, 129, 0.18); + outline-offset: 1px; +} + +.change-record-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; +} + +.change-record-head > div { + display: grid; + gap: 3px; +} + +.change-record-head strong { + color: #0f172a; + font-size: 13px; + font-weight: 900; +} + +.change-record-head span, +.change-record-item p, +.change-record-more { + color: #64748b; + font-size: 11px; +} + +.change-record-head b { + min-height: 22px; + display: inline-flex; + align-items: center; + padding: 0 8px; + border-radius: 999px; + background: #eef2ff; + color: #4f46e5; + font-size: 11px; + font-weight: 850; +} + +.change-record-item p { + margin: 0; + line-height: 1.5; +} + +.change-record-preview { + display: grid; + gap: 6px; +} + +.change-record-preview span { + display: block; + padding: 7px 8px; + border-radius: 10px; + background: #f8fafc; + color: #334155; + font-size: 11px; + line-height: 1.45; + overflow-wrap: anywhere; +} + +.change-record-more { + font-weight: 800; +} + +.change-detail-content { + gap: 14px; +} + +.change-detail-meta { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; +} + +.change-detail-meta article { + display: grid; + gap: 4px; + min-height: 0; + padding: 10px 12px; + border-radius: 14px; + background: #f8fafc; +} + +.change-detail-meta span { + color: #64748b; + font-size: 12px; +} + +.change-detail-meta strong { + color: #0f172a; + font-size: 15px; + font-weight: 900; +} + +.change-flow-empty { + color: #64748b; + font-size: 11px; +} + +.rule-drawer-backdrop { + position: fixed; + inset: 0; + z-index: 80; + display: flex; + justify-content: flex-end; + background: rgba(15, 23, 42, 0.34); + backdrop-filter: blur(3px); +} + +.rule-drawer { + width: min(860px, 78vw); + height: 100%; + display: grid; + grid-template-rows: auto auto minmax(0, 1fr); + gap: 18px; + padding: 22px; + overflow: auto; + background: #fff; + box-shadow: -18px 0 42px rgba(15, 23, 42, 0.18); +} + +.timeline-drawer { + width: min(640px, 62vw); +} + +.rule-drawer-head { + display: flex; + align-items: start; + justify-content: space-between; + gap: 16px; +} + +.rule-drawer-head span { + color: #2563eb; + font-size: 12px; + font-weight: 850; +} + +.rule-drawer-head h3 { + margin: 4px 0 0; + color: #0f172a; + font-size: 20px; + font-weight: 950; +} + +.rule-drawer-head button { + width: 34px; + height: 34px; + border: 0; + border-radius: 999px; + background: #f1f5f9; + color: #475569; + cursor: pointer; +} + +.rule-drawer-state { + min-height: 160px; + display: grid; + place-items: center; + gap: 10px; + color: #64748b; +} + +.rule-drawer-state.error { + color: #dc2626; +} + +.rule-timeline-list { + display: grid; + align-content: start; +} + +.rule-timeline-item { + position: relative; + display: grid; + grid-template-columns: 30px minmax(0, 1fr); + gap: 12px; + padding-bottom: 20px; +} + +.rule-timeline-item:not(:last-child)::after { + content: ''; + position: absolute; + left: 14px; + top: 28px; + bottom: 0; + width: 2px; + background: #e2e8f0; +} + +.rule-timeline-item > i { + position: relative; + z-index: 1; + width: 30px; + height: 30px; + display: grid; + place-items: center; + border-radius: 999px; + background: #f1f5f9; + color: #475569; +} + +.rule-timeline-item > i.success { + background: #dcfce7; + color: #059669; +} + +.rule-timeline-item > i.warning { + background: #fef3c7; + color: #d97706; +} + +.rule-timeline-item > i.danger { + background: #fee2e2; + color: #dc2626; +} + +.rule-timeline-item > i.info { + background: #dbeafe; + color: #2563eb; +} + +.rule-timeline-item header { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; +} + +.rule-timeline-item header strong { + color: #0f172a; + font-size: 14px; +} + +.rule-timeline-item header b { + min-height: 22px; + display: inline-flex; + align-items: center; + padding: 0 8px; + border-radius: 999px; + background: #f1f5f9; + color: #475569; + font-size: 11px; +} + +.rule-timeline-item header span, +.rule-timeline-item p, +.rule-timeline-item small { + color: #64748b; +} + +.rule-timeline-item p { + margin: 8px 0 4px; + line-height: 1.6; +} + +.compare-toolbar { + display: flex; + align-items: end; + gap: 12px; +} + +.compare-toolbar label { + min-width: 0; + display: grid; + gap: 6px; + flex: 1; +} + +.compare-toolbar span { + color: #64748b; + font-size: 12px; + font-weight: 850; +} + +.compare-toolbar select { + width: 100%; + min-height: 40px; + padding: 0 12px; + border: 1px solid #cbd5e1; + border-radius: 12px; + background: #fff; +} + +.compare-toolbar i { + margin-bottom: 10px; + color: #94a3b8; +} + +.compare-content { + min-height: 0; + display: grid; + grid-template-rows: auto auto minmax(0, 1fr); + gap: 18px; +} + +.compare-summary-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; +} + +.compare-summary-grid article { + display: grid; + gap: 4px; + align-content: center; + min-height: 0; + padding: 10px 12px; + border-radius: 14px; + background: #f8fafc; +} + +.compare-summary-grid span { + color: #64748b; + font-size: 12px; +} + +.compare-summary-grid strong { + color: #0f172a; + font-size: 22px; + font-weight: 950; + line-height: 1.1; +} + +.compare-panel { + display: grid; + gap: 10px; +} + diff --git a/web/src/assets/styles/views/budget-center-view.css b/web/src/assets/styles/views/budget-center-view.css new file mode 100644 index 0000000..e1efd90 --- /dev/null +++ b/web/src/assets/styles/views/budget-center-view.css @@ -0,0 +1,499 @@ +.budget-center-page { + min-width: 0; + display: grid; + gap: 14px; + color: #1f2937; +} + +.budget-local-head { + min-height: 34px; + display: flex; + align-items: center; +} + +.budget-local-head h2 { + margin: 0; + color: #111827; + font-size: 24px; + line-height: 1.2; + font-weight: 800; +} + +.budget-summary-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + border: 1px solid #e5eaf1; + border-radius: 8px; + background: #fff; + overflow: hidden; +} + +.budget-summary-card { + min-height: 118px; + padding: 22px 28px; + display: grid; + grid-template-columns: 64px minmax(0, 1fr); + align-items: center; + gap: 18px; + border-right: 1px solid #edf1f6; +} + +.budget-summary-card:last-child { + border-right: 0; +} + +.summary-icon { + width: 54px; + height: 54px; + border-radius: 50%; + display: grid; + place-items: center; + font-size: 30px; +} + +.summary-icon.green { + background: #e8f7ef; + color: #07965f; +} + +.summary-icon.blue { + background: #edf4ff; + color: #2f7fd7; +} + +.summary-icon.orange { + background: #fff4e5; + color: #df9300; +} + +.budget-summary-card span:not(.summary-icon) { + display: block; + color: #1f2937; + font-size: 14px; + font-weight: 700; +} + +.budget-summary-card strong { + display: block; + margin-top: 8px; + color: #111827; + font-size: 24px; + line-height: 1; + font-weight: 500; + font-variant-numeric: tabular-nums; + white-space: nowrap; +} + +.budget-summary-card em { + display: block; + margin-top: 10px; + color: #8a94a6; + font-size: 13px; + font-style: normal; +} + +.budget-filter-bar { + min-height: 62px; + border: 1px solid #e5eaf1; + border-radius: 8px; + background: #fff; + padding: 12px 18px; + display: flex; + align-items: center; + gap: 22px; +} + +.budget-filter-bar label { + display: inline-flex; + align-items: center; + gap: 10px; + color: #1f2937; + font-size: 14px; + font-weight: 700; +} + +.budget-filter-bar select, +.budget-table-foot select { + height: 34px; + min-width: 150px; + border: 1px solid #dbe2ec; + border-radius: 5px; + background: #fff; + color: #1f2937; + padding: 0 34px 0 12px; + font-size: 14px; +} + +.budget-primary-btn { + margin-left: auto; + height: 36px; + border: 0; + border-radius: 5px; + background: #0aa66f; + color: #fff; + padding: 0 18px; + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 14px; + font-weight: 800; +} + +.budget-work-grid { + display: grid; + grid-template-columns: 240px minmax(0, 1fr); + gap: 10px; +} + +.budget-department-panel, +.budget-table-panel, +.budget-chart-panel, +.budget-alert-panel { + border: 1px solid #e5eaf1; + border-radius: 8px; + background: #fff; + overflow: hidden; +} + +.budget-department-panel header, +.budget-table-panel > header, +.budget-card-head { + min-height: 48px; + padding: 13px 18px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid #edf1f6; +} + +.budget-department-panel strong, +.budget-table-panel > header strong, +.budget-card-head strong { + color: #111827; + font-size: 16px; + font-weight: 800; +} + +.department-search { + position: relative; + margin: 12px 14px 8px; +} + +.department-search i { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #9aa5b5; +} + +.department-search input { + width: 100%; + height: 36px; + border: 1px solid #e2e8f0; + border-radius: 5px; + padding: 0 12px 0 34px; + background: #fff; + color: #1f2937; +} + +.department-list { + display: grid; + gap: 4px; + padding: 8px 12px 16px; +} + +.department-list button { + height: 38px; + border: 0; + border-radius: 5px; + background: transparent; + color: #4b5563; + padding: 0 12px; + display: flex; + align-items: center; + gap: 10px; + font-size: 14px; + font-weight: 700; + text-align: left; +} + +.department-list button.active { + background: #e9f7f1; + color: #07965f; +} + +.budget-table-wrap { + overflow-x: auto; +} + +.budget-table-panel table { + width: 100%; + min-width: 1040px; + border-collapse: collapse; +} + +.budget-table-panel th, +.budget-table-panel td { + padding: 13px 18px; + border-bottom: 1px solid #edf1f6; + border-right: 1px solid #edf1f6; + color: #273142; + font-size: 14px; + text-align: left; + white-space: nowrap; +} + +.budget-table-panel th:last-child, +.budget-table-panel td:last-child { + border-right: 0; +} + +.budget-table-panel th { + background: #fafbfd; + color: #1f2937; + font-weight: 800; +} + +.budget-rate { + width: 96px; + display: grid; + gap: 6px; +} + +.budget-rate span { + color: #273142; + font-size: 13px; +} + +.budget-rate div { + height: 6px; + border-radius: 999px; + background: #e9edf3; + overflow: hidden; +} + +.budget-rate em { + display: block; + height: 100%; + border-radius: inherit; +} + +.budget-rate em.ok { + background: #13a66b; +} + +.budget-rate em.warn { + background: #f2a51a; +} + +.budget-rate em.danger { + background: #ef4444; +} + +.budget-warning-red { + color: #e24b4b !important; + font-weight: 800; +} + +.budget-warning-yellow { + color: #e3a008 !important; + font-weight: 800; +} + +.budget-row-actions { + display: flex; + align-items: center; + gap: 14px; +} + +.budget-row-actions button { + border: 0; + background: transparent; + color: #1c7ed6; + font-size: 14px; + font-weight: 800; +} + +.budget-table-foot { + min-height: 52px; + padding: 10px 18px; + display: flex; + align-items: center; + justify-content: flex-end; + gap: 10px; +} + +.budget-table-foot button { + width: 32px; + height: 32px; + border: 1px solid #dbe2ec; + border-radius: 5px; + background: #fff; + color: #64748b; +} + +.budget-table-foot button.active { + border-color: #10a873; + color: #10a873; + font-weight: 800; +} + +.budget-table-foot span { + color: #4b5563; + font-size: 14px; +} + +.budget-bottom-grid { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(360px, 0.82fr); + gap: 10px; +} + +.budget-card-head button { + border: 0; + background: transparent; + color: #1c7ed6; + font-size: 14px; + font-weight: 800; +} + +.budget-chart-legend { + display: flex; + align-items: center; + gap: 22px; + color: #4b5563; + font-size: 13px; +} + +.budget-chart-legend span { + display: inline-flex; + align-items: center; + gap: 7px; +} + +.legend-line { + width: 18px; + height: 0; + border-top: 2px dashed #2f7fd7; +} + +.legend-line.used { + border-top-style: solid; + border-top-color: #13a66b; +} + +.budget-chart-panel { + padding-bottom: 16px; +} + +.budget-chart-panel .budget-trend-chart { + margin: 12px 18px 0; +} + +.budget-alert-list { + display: grid; + padding: 12px 20px 18px; +} + +.budget-alert-row { + min-height: 46px; + display: grid; + grid-template-columns: 12px 120px minmax(0, 1fr) 92px; + gap: 12px; + align-items: center; + border-bottom: 1px solid #edf1f6; +} + +.budget-alert-row:last-child { + border-bottom: 0; +} + +.budget-alert-row i { + width: 8px; + height: 8px; + border-radius: 999px; +} + +.budget-alert-row i.danger { + background: #ef4444; +} + +.budget-alert-row i.warn { + background: #f59e0b; +} + +.budget-alert-row i.ok { + background: #13a66b; +} + +.budget-alert-row strong { + color: #273142; + font-size: 14px; + font-weight: 800; +} + +.budget-alert-row span { + min-width: 0; + color: #4b5563; + font-size: 14px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.budget-alert-row time { + color: #6b7280; + font-size: 13px; + text-align: right; +} + +@media (max-width: 1280px) { + .budget-summary-grid, + .budget-bottom-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .budget-work-grid { + grid-template-columns: 1fr; + } + + .budget-department-panel { + min-height: auto; + } + + .department-list { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (max-width: 760px) { + .budget-summary-grid, + .budget-bottom-grid { + grid-template-columns: 1fr; + } + + .budget-filter-bar { + align-items: stretch; + flex-direction: column; + } + + .budget-filter-bar label, + .budget-filter-bar select, + .budget-primary-btn { + width: 100%; + } + + .department-list { + grid-template-columns: 1fr; + } + + .budget-alert-row { + grid-template-columns: 12px minmax(0, 1fr); + } + + .budget-alert-row span, + .budget-alert-row time { + grid-column: 2; + } +} diff --git a/web/src/assets/styles/views/travel-reimbursement-create-view.css b/web/src/assets/styles/views/travel-reimbursement-create-view.css index 81e0104..b3d953a 100644 --- a/web/src/assets/styles/views/travel-reimbursement-create-view.css +++ b/web/src/assets/styles/views/travel-reimbursement-create-view.css @@ -647,6 +647,10 @@ box-shadow: 0 10px 22px rgba(226, 232, 240, 0.48); } +.message-bubble-application-preview { + max-width: min(100%, 980px); +} + .message-bubble-review-risk-low { border-color: rgba(37, 99, 235, 0.72); background: linear-gradient(180deg, rgba(239, 246, 255, 0.72), rgba(255, 255, 255, 0.96)); @@ -759,6 +763,170 @@ margin: 0; } +.application-preview-table { + margin-top: 12px; + overflow: hidden; + border: 1px solid #d7e4f2; + border-radius: 8px; + background: #ffffff; + color: #334155; + font-size: var(--wb-fs-bubble); +} + +.application-preview-footer { + margin-top: 12px; + color: #334155; + font-size: var(--wb-fs-bubble); + line-height: 1.58; +} + +.application-preview-row { + display: grid; + grid-template-columns: 108px minmax(0, 1fr); + min-height: 38px; + border-top: 1px solid #e6edf5; +} + +.application-preview-row.editable { + cursor: pointer; +} + +.application-preview-row.editable:hover { + background: #f8fbff; +} + +.application-preview-row.editable:hover .application-preview-label, +.application-preview-row.editable:hover .application-preview-value { + background: #f8fbff; +} + +.application-preview-row.editable.missing:hover .application-preview-value { + background: #fff4e6; +} + +.application-preview-row.editable:focus-visible { + position: relative; + z-index: 1; + outline: 2px solid rgba(37, 99, 235, 0.45); + outline-offset: -2px; +} + +.application-preview-row:first-child { + border-top: 0; +} + +.application-preview-row.head { + min-height: 34px; + background: #f8fafc; + color: #475569; + font-size: var(--wb-fs-caption); + font-weight: 850; +} + +.application-preview-row > span { + min-width: 0; + display: flex; + align-items: center; + padding: 8px 12px; +} + +.application-preview-label { + border-right: 1px solid #e6edf5; + background: #fbfdff; + color: #64748b; + font-weight: 760; +} + +.application-preview-value { + position: relative; + gap: 8px; + color: #0f172a; + font-weight: 650; +} + + .application-preview-row.missing .application-preview-value { + background: #fff7ed; + color: #b45309; + } + + .application-preview-row.highlight .application-preview-label { + background: #f0fdf4; + color: #047857; + } + + .application-preview-row.highlight .application-preview-value { + background: #f7fee7; + color: #166534; + font-weight: 780; + } + + .application-preview-row.highlight.missing .application-preview-value { + background: #fff7ed; + color: #b45309; + } + +.application-preview-text { + min-width: 0; + overflow-wrap: anywhere; + line-height: 1.45; +} + +.application-preview-edit-btn { + flex: 0 0 auto; + width: 24px; + height: 24px; + display: grid; + place-items: center; + border: 1px solid transparent; + border-radius: 6px; + background: #eff6ff; + color: #2563eb; + cursor: pointer; + opacity: 0; + transition: opacity 0.16s ease, border-color 0.16s ease, background 0.16s ease; +} + +.application-preview-edit-btn i { + font-size: 14px; +} + +.application-preview-row:hover .application-preview-edit-btn, +.application-preview-edit-btn:focus-visible { + opacity: 1; +} + +.application-preview-edit-btn:hover:not(:disabled), +.application-preview-edit-btn:focus-visible { + border-color: #bfdbfe; + background: #dbeafe; +} + +.application-preview-edit-btn:disabled { + cursor: not-allowed; + opacity: 0.42; +} + +.application-preview-input { + width: 100%; + min-width: 0; + height: 30px; + border: 1px solid #93c5fd; + border-radius: 6px; + background: #ffffff; + color: #0f172a; + font: inherit; + font-weight: 650; + line-height: 1.2; + padding: 0 9px; + outline: none; + box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12); +} + +.application-preview-select { + cursor: pointer; + appearance: auto; +} + .message-answer-markdown :deep(ul), .message-answer-markdown :deep(ol) { margin: 0; diff --git a/web/src/assets/styles/views/travel-request-detail-view-part2.css b/web/src/assets/styles/views/travel-request-detail-view-part2.css index 9d2e2c0..edb98ee 100644 --- a/web/src/assets/styles/views/travel-request-detail-view-part2.css +++ b/web/src/assets/styles/views/travel-request-detail-view-part2.css @@ -725,12 +725,18 @@ } .hero-fact-grid { - grid-template-columns: repeat(5, minmax(132px, 1fr)); - overflow-x: auto; + grid-template-columns: minmax(280px, 1.4fr) repeat(3, minmax(0, 1fr)); + overflow: visible; } .hero-fact { - min-width: 132px; + min-width: 0; + padding-inline: 16px; + } + + .hero-fact strong { + white-space: nowrap; + word-break: normal; } .detail-expense-table { @@ -822,6 +828,18 @@ white-space: normal; } + .application-detail-facts { + grid-template-columns: 1fr; + } + + .application-detail-fact { + border-left: 0; + } + + .application-detail-fact:nth-child(2) { + border-top: 1px solid #edf2f7; + } + .detail-card { padding: 14px 16px; } diff --git a/web/src/assets/styles/views/travel-request-detail-view.css b/web/src/assets/styles/views/travel-request-detail-view.css index 5405c2d..9cc4dc4 100644 --- a/web/src/assets/styles/views/travel-request-detail-view.css +++ b/web/src/assets/styles/views/travel-request-detail-view.css @@ -180,7 +180,7 @@ .hero-fact-grid { display: grid; - grid-template-columns: repeat(5, minmax(0, 1fr)); + grid-template-columns: minmax(240px, 1.25fr) repeat(3, minmax(0, 1fr)); gap: 0; } @@ -216,13 +216,20 @@ } .hero-fact strong { + min-width: 0; + max-width: 100%; color: #0f172a; font-size: 16px; font-weight: 800; line-height: 1.4; + overflow-wrap: anywhere; white-space: nowrap; } +.hero-fact:first-child strong { + font-size: 15px; +} + .hero-fact strong.amount { font-size: 22px; font-weight: 900; @@ -489,6 +496,20 @@ margin-bottom: 4px; } +.detail-card-title-with-icon { + display: inline-flex; + align-items: center; + gap: 8px; + line-height: 1.5; +} + +.detail-card-title-with-icon i { + margin-top: 1px; + color: #334155; + font-size: 18px; + line-height: 1; +} + .detail-card-head p { color: #64748b; font-size: 12px; @@ -571,6 +592,70 @@ white-space: pre-wrap; } +.application-detail-facts { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-top: 4px; + overflow: hidden; + border: 1px solid #e2e8f0; + border-radius: 8px; + background: #fff; +} + +.application-detail-fact { + display: grid; + grid-template-columns: minmax(96px, 28%) minmax(0, 1fr); + min-height: 48px; + border-top: 1px solid #edf2f7; + border-left: 1px solid #edf2f7; +} + +.application-detail-fact:nth-child(-n + 2) { + border-top: 0; +} + +.application-detail-fact:nth-child(2n + 1) { + border-left: 0; +} + +.application-detail-fact span, +.application-detail-fact strong { + display: flex; + align-items: center; + min-width: 0; + padding: 11px 14px; + line-height: 1.5; +} + +.application-detail-fact span { + background: #f8fafc; + color: #64748b; + font-size: 12px; + font-weight: 800; +} + +.application-detail-fact strong { + border-left: 1px solid #edf2f7; + color: #0f172a; + font-size: 13px; + font-weight: 750; + overflow-wrap: anywhere; +} + +.application-detail-fact.highlight span { + background: #eefcf6; + color: #047857; +} + +.application-detail-fact.highlight strong { + background: #f6fef9; +} + +.application-detail-fact.emphasis strong { + color: #047857; + font-weight: 850; +} + .detail-note-editor { display: grid; gap: 10px; @@ -640,6 +725,54 @@ font-weight: 850; } + +.application-leader-opinion { + display: grid; + gap: 10px; + margin-top: 12px; +} + +.application-leader-opinion-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + color: #475569; + font-size: 14px; + line-height: 1.5; +} + +.application-leader-opinion-head span { + display: inline-flex; + align-items: center; + gap: 8px; + color: #0f172a; + font-weight: 850; + font-size: 16px; +} + +.application-leader-opinion-head span i { + margin-top: 1px; + color: #334155; + font-size: 18px; +} + +.application-leader-opinion-head strong { + color: #047857; + font-weight: 800; + font-size: 14px; +} + +.inline-leader-opinion { + padding: 0; + border: 0; + background: transparent; +} + +.application-leader-opinion-display { + min-height: 64px; +} + .detail-expense-table { min-width: 0; overflow-x: auto; diff --git a/web/src/components/charts/BudgetTrendChart.vue b/web/src/components/charts/BudgetTrendChart.vue new file mode 100644 index 0000000..a153c1c --- /dev/null +++ b/web/src/components/charts/BudgetTrendChart.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/web/src/components/layout/SidebarRail.vue b/web/src/components/layout/SidebarRail.vue index 679ec9a..37af2d6 100644 --- a/web/src/components/layout/SidebarRail.vue +++ b/web/src/components/layout/SidebarRail.vue @@ -33,6 +33,7 @@ > {{ item.displayLabel }} + {{ item.badge }} @@ -83,7 +84,7 @@ @@ -463,6 +463,16 @@ onBeforeUnmount(() => { opacity var(--rail-fade-duration) var(--rail-motion-ease); } +.nav-unread-dot { + flex: 0 0 auto; + width: 8px; + height: 8px; + border: 2px solid #fff; + border-radius: 999px; + background: #ef4444; + box-shadow: 0 6px 14px rgba(239, 68, 68, 0.26); +} + .rail-user { position: relative; min-width: 0; @@ -668,6 +678,14 @@ onBeforeUnmount(() => { overflow: hidden; } +.rail-collapsed .nav-unread-dot { + position: absolute; + top: 10px; + right: 11px; + width: 9px; + height: 9px; +} + .rail-collapsed { overflow: visible; } diff --git a/web/src/components/shared/RiskRuleFlowDiagram.vue b/web/src/components/shared/RiskRuleFlowDiagram.vue index 5cae51c..6ad26e3 100644 --- a/web/src/components/shared/RiskRuleFlowDiagram.vue +++ b/web/src/components/shared/RiskRuleFlowDiagram.vue @@ -47,26 +47,11 @@

@@ -74,87 +59,7 @@ @@ -566,7 +504,7 @@ const displaySvg = computed(() => { .risk-rule-flow-svg-viewport { position: relative; width: 100%; - height: 280px; + height: 360px; overflow: hidden; border-radius: 6px; background: #ffffff; @@ -575,16 +513,11 @@ const displaySvg = computed(() => { align-items: center; justify-content: center; user-select: none; - cursor: grab; -} - -.risk-rule-flow-svg-viewport:active { - cursor: grabbing; } .risk-rule-flow-svg-canvas { - width: 760px; - height: 280px; + width: 860px; + height: 360px; flex-shrink: 0; } @@ -633,7 +566,7 @@ const displaySvg = computed(() => { } .risk-rule-flow-image { - width: min(760px, 100%); + width: min(860px, 100%); display: block; pointer-events: none; user-select: none; diff --git a/web/src/components/shared/RiskRuleTestDialog.vue b/web/src/components/shared/RiskRuleTestDialog.vue index 1a39929..b2d8967 100644 --- a/web/src/components/shared/RiskRuleTestDialog.vue +++ b/web/src/components/shared/RiskRuleTestDialog.vue @@ -49,6 +49,31 @@ +
+ 单据识别明细 +
+
+ {{ document.filename || '临时单据' }} + {{ formatDocumentMeta(document) }} +
+

摘要:{{ document.summary }}

+
+ + {{ field.label }}[{{ field.key }}]:{{ field.value }} + +
+

+ OCR原文:{{ trimDebugText(document.text, 800) }} +

+
+
+
@@ -75,6 +100,20 @@
+
+ 规则实际取用字段 +
    +
  • + {{ field.label }} + {{ field.source }} + {{ field.value }} +
  • +
+
+
判断依据
    @@ -262,6 +301,16 @@ import { formatTestError, formatTime } from './riskRuleTestDialogUtils.js' +import { + buildDocumentBrief, + buildEvidenceItems as buildEvidenceItemsModel, + buildRecognizedFieldRows as buildRecognizedFieldRowsModel, + buildResultFields as buildResultFieldsModel, + formatDocumentMeta, + formatFieldLabel, + resolveFileStatusLabel, + trimDebugText +} from './riskRuleTestDialogDisplay.js' const props = defineProps({ open: { @@ -568,8 +617,9 @@ async function recognizeTemporaryFiles(files, activeSessionId) { messages.value.push(buildMessage( 'assistant', recognizedCount - ? `已完成 ${recognizedCount} 份临时单据识别。请核对右侧识别字段,字段不足时可以直接在输入框补充。` - : '上传文件没有提取到足够字段,暂不能直接执行规则。请在输入框补充票据城市、金额、发票号等关键信息。' + ? `已完成 ${recognizedCount} 份临时单据识别。下面会展示 OCR 结构化字段和原文片段,请先核对这些信息;字段不足时可以直接在输入框补充。` + : '上传文件没有提取到足够字段。下面仍会展示 OCR 返回内容,方便判断是票据质量问题还是字段映射问题。请在输入框补充城市、金额、发票号等关键信息。', + { recognitionDocuments: documents } )) } catch (error) { if (!isActiveSession(activeSessionId)) return @@ -601,52 +651,15 @@ function buildMessage(role, text, extra = {}) { } function buildResultFields(result) { - const values = result?.field_values && typeof result.field_values === 'object' - ? result.field_values - : {} - return Object.entries(values).slice(0, 8).map(([key, value]) => ({ - key, - label: formatFieldLabel(fields.value.find((field) => field.key === key) || { key }), - value: Array.isArray(value) ? value.join('、') : String(value ?? '-') - })) + return buildResultFieldsModel(result, fields.value) +} + +function buildRecognizedFieldRows(result) { + return buildRecognizedFieldRowsModel(result, fields.value) } function buildEvidenceItems(result) { - const evidence = result?.evidence && typeof result.evidence === 'object' - ? result.evidence - : {} - const items = [] - if (Array.isArray(evidence.failed_conditions)) { - evidence.failed_conditions.slice(0, 3).forEach((condition) => { - const left = Array.isArray(condition.left_values) ? condition.left_values.join('、') : '-' - const right = Array.isArray(condition.right_values) ? condition.right_values.join('、') : '-' - items.push(`${formatFieldName(condition.left)}:${left};${formatFieldName(condition.right)}:${right}`) - }) - } - if (Array.isArray(evidence.missing_fields)) { - evidence.missing_fields.slice(0, 5).forEach((field) => { - items.push(`${formatFieldName(field)} 缺失`) - }) - } - if (Array.isArray(evidence.keyword_hits)) { - items.push(`命中关键词:${evidence.keyword_hits.join('、')}`) - } - if (evidence.condition_summary) { - items.push(String(evidence.condition_summary)) - } - return [...new Set(items)].slice(0, 5) -} - -function formatFieldLabel(field) { - const key = String(field?.key || '').trim() - const label = String(field?.display || field?.label || '').trim() - if (!key) return label || '-' - if (!label || label === key) return key - return label.includes(`[${key}]`) ? label : `${label}[${key}]` -} - -function formatFieldName(key) { - return formatFieldLabel(fields.value.find((field) => field.key === key) || { key }) + return buildEvidenceItemsModel(result, fields.value) } function toAttachmentPayload(file) { @@ -713,23 +726,6 @@ function documentHasMeaningfulText(document) { ) } -function buildDocumentBrief(document) { - const fields = Array.isArray(document?.document_fields) ? document.document_fields : [] - if (fields.length) { - return fields.slice(0, 4).map((field) => `${field.label}:${field.value}`).join(';') - } - return String(document?.summary || document?.text || '未提取到结构化字段').slice(0, 120) -} - -function resolveFileStatusLabel(file) { - return file.statusText || { - pending: '待发送', - recognizing: '识别中', - recognized: '已识别', - failed: '识别失败' - }[file.status] || '待识别' -} - function buildRecognitionStepDescription() { if (!requiresAttachment.value) return '当前规则不需要附件,直接根据文字测试事实抽取字段。' if (recognitionBusy.value) return '正在读取临时附件并提取 OCR 字段。' diff --git a/web/src/components/shared/riskRuleTestDialogDisplay.js b/web/src/components/shared/riskRuleTestDialogDisplay.js new file mode 100644 index 0000000..45518c0 --- /dev/null +++ b/web/src/components/shared/riskRuleTestDialogDisplay.js @@ -0,0 +1,110 @@ +export function formatFieldLabel(field) { + const key = String(field?.key || '').trim() + const label = String(field?.display || field?.label || '').trim() + if (!key) return label || '-' + if (!label || label === key) return key + return label.includes(`[${key}]`) ? label : `${label}[${key}]` +} + +export function buildResultFields(result, fields = []) { + const values = result?.field_values && typeof result.field_values === 'object' + ? result.field_values + : {} + return Object.entries(values).slice(0, 8).map(([key, value]) => ({ + key, + label: formatFieldLabel(fields.find((field) => field.key === key) || { key }), + value: Array.isArray(value) ? value.join('、') : String(value ?? '-') + })) +} + +export function buildRecognizedFieldRows(result, fields = []) { + const rows = Array.isArray(result?.recognized_fields) ? result.recognized_fields : [] + return rows.slice(0, 12).map((field, index) => ({ + key: String(field?.key || `field-${index}`), + label: formatFieldLabel( + fields.find((item) => item.key === field?.key) || { + key: field?.key, + label: field?.label + } + ), + source: formatRecognitionSource(field?.source), + value: formatDebugValue(field?.value) + })) +} + +export function buildEvidenceItems(result, fields = []) { + const evidence = result?.evidence && typeof result.evidence === 'object' + ? result.evidence + : {} + const items = [] + if (Array.isArray(evidence.failed_conditions)) { + evidence.failed_conditions.slice(0, 3).forEach((condition) => { + const left = Array.isArray(condition.left_values) ? condition.left_values.join('、') : '-' + const right = Array.isArray(condition.right_values) ? condition.right_values.join('、') : '-' + items.push(`${formatFieldName(condition.left, fields)}:${left};${formatFieldName(condition.right, fields)}:${right}`) + }) + } + if (Array.isArray(evidence.missing_fields)) { + evidence.missing_fields.slice(0, 5).forEach((field) => { + items.push(`${formatFieldName(field, fields)} 缺失`) + }) + } + if (Array.isArray(evidence.keyword_hits)) { + items.push(`命中关键词:${evidence.keyword_hits.join('、')}`) + } + if (evidence.condition_summary) { + items.push(String(evidence.condition_summary)) + } + return [...new Set(items)].slice(0, 5) +} + +export function buildDocumentBrief(document) { + const fields = Array.isArray(document?.document_fields) ? document.document_fields : [] + if (fields.length) { + return fields.slice(0, 6).map((field) => `${field.label}:${field.value}`).join(';') + } + return String(document?.summary || document?.text || '未提取到结构化字段').slice(0, 120) +} + +export function formatDocumentMeta(document) { + const labels = [ + document?.document_type_label || '', + document?.scene_label || '', + document?.avg_score ? `置信度 ${Math.round(Number(document.avg_score) * 100)}%` : '' + ].filter(Boolean) + return labels.join(' · ') || '未分类' +} + +export function resolveFileStatusLabel(file) { + return file.statusText || { + pending: '待发送', + recognizing: '识别中', + recognized: '已识别', + failed: '识别失败' + }[file.status] || '待识别' +} + +export function trimDebugText(text, maxLength = 800) { + const value = String(text || '').replace(/\s+/g, ' ').trim() + if (!value) return '' + return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value +} + +function formatRecognitionSource(source) { + return { + manual: '手动输入', + ocr: 'OCR结构字段', + inferred: '文本推断', + model_refined: '模型过滤' + }[String(source || '').trim()] || '未标注来源' +} + +function formatDebugValue(value) { + if (Array.isArray(value)) return value.map((item) => String(item ?? '')).filter(Boolean).join('、') || '-' + if (value && typeof value === 'object') return JSON.stringify(value) + return String(value ?? '-') +} + +function formatFieldName(key, fields) { + return formatFieldLabel(fields.find((field) => field.key === key) || { key }) +} diff --git a/web/src/composables/useAppShell.js b/web/src/composables/useAppShell.js index 45f0d08..ac9413c 100644 --- a/web/src/composables/useAppShell.js +++ b/web/src/composables/useAppShell.js @@ -1,8 +1,7 @@ import { computed, ref, watch } from 'vue' import { useRoute, useRouter } from 'vue-router' -import { useApprovalInbox } from './useApprovalInbox.js' -import { useNavigation, navItems } from './useNavigation.js' +import { useNavigation, navItems } from './useNavigation.js' import { useRequests } from './useRequests.js' import { useSystemState } from './useSystemState.js' import { useToast } from './useToast.js' @@ -23,14 +22,15 @@ export function useAppShell() { const smartEntryOpen = ref(false) const smartEntryContext = ref({ prompt: '', - source: 'requests', + source: 'documents', request: null, files: [], conversation: null, scope: null }) - const smartEntrySessionId = ref(0) - const smartEntryInvalidatedDraftClaimId = ref('') + const smartEntrySessionId = ref(0) + const smartEntryRevealToken = ref(0) + const smartEntryInvalidatedDraftClaimId = ref('') const selectedRequestSnapshot = ref(null) const { activeView, currentView, setView } = useNavigation() @@ -49,7 +49,6 @@ export function useAppShell() { } = useRequests() const { currentUser } = useSystemState() const { toast } = useToast() - const { refreshApprovalInbox } = useApprovalInbox() const customRange = ref({ start: '2024-07-06', end: '2024-07-12' }) @@ -83,21 +82,14 @@ export function useAppShell() { return null }) - const detailMode = computed(() => ['app-request-detail', 'app-document-detail'].includes(route.name)) - const logDetailMode = computed(() => route.name === 'app-log-detail') - const detailAlerts = computed(() => (detailMode.value ? buildDetailAlerts(selectedRequest.value) : [])) - - const requestsListActive = computed(() => activeView.value === 'requests' && !detailMode.value) - const documentsListActive = computed(() => activeView.value === 'documents' && !detailMode.value) - const workbenchActive = computed(() => activeView.value === 'workbench') - - watch(requestsListActive, (isActive, wasActive) => { - if (isActive && !wasActive) { - void reloadRequests() - } - }) - - watch(documentsListActive, (isActive, wasActive) => { + const detailMode = computed(() => route.name === 'app-document-detail') + const logDetailMode = computed(() => route.name === 'app-log-detail') + const detailAlerts = computed(() => (detailMode.value ? buildDetailAlerts(selectedRequest.value) : [])) + + const documentsListActive = computed(() => activeView.value === 'documents' && !detailMode.value) + const workbenchActive = computed(() => activeView.value === 'workbench') + + watch(documentsListActive, (isActive, wasActive) => { if (isActive && !wasActive) { void reloadRequests() } @@ -178,6 +170,10 @@ export function useAppShell() { } function openFinancialAssistantCreate(source) { + if (smartEntryOpen.value) { + smartEntryRevealToken.value += 1 + return + } smartEntryOpen.value = true smartEntryContext.value = { prompt: '', @@ -237,6 +233,7 @@ export function useAppShell() { return ( documentType === 'application' || documentType === 'expense_application' + || normalizedClaimNo.startsWith('AP-') || normalizedClaimNo.startsWith('APP-') ) } @@ -266,8 +263,12 @@ export function useAppShell() { } } - async function openSmartEntry(payload = {}) { - const conversation = await resolveSmartEntryConversation(payload) + async function openSmartEntry(payload = {}) { + if (smartEntryOpen.value) { + smartEntryRevealToken.value += 1 + return + } + const conversation = await resolveSmartEntryConversation(payload) const scope = resolveSmartEntryClaimScope(payload) smartEntryOpen.value = true @@ -294,13 +295,12 @@ export function useAppShell() { await reloadRequests() if (status === 'submitted') { smartEntryOpen.value = false - void refreshApprovalInbox() toast( isApplicationDocument ? `${claimNo || '该'}申请单已提交${approvalStage ? `,当前节点:${approvalStage}` : ',等待直属领导审批'}。` : `${claimNo || '该'}单据已完成 AI预审${approvalStage ? `,当前节点:${approvalStage}` : ',并已提交审批'}。` ) - router.push({ name: activeView.value === 'documents' ? 'app-documents' : 'app-requests' }) + router.push({ name: 'app-documents' }) return } toast( @@ -310,23 +310,21 @@ export function useAppShell() { ) } - function openRequestDetail(request) { - selectedRequestSnapshot.value = request || null - const routeName = activeView.value === 'documents' ? 'app-document-detail' : 'app-request-detail' - router.push({ - name: routeName, - params: { requestId: request.claimId || request.id } - }) - } - - function closeRequestDetail() { - router.push({ name: activeView.value === 'documents' ? 'app-documents' : 'app-requests' }) - } - - async function handleRequestUpdated() { - await reloadRequests() - void refreshApprovalInbox() - } + function openRequestDetail(request) { + selectedRequestSnapshot.value = request || null + router.push({ + name: 'app-document-detail', + params: { requestId: request.claimId || request.id } + }) + } + + function closeRequestDetail() { + router.push({ name: 'app-documents' }) + } + + async function handleRequestUpdated() { + await reloadRequests() + } async function handleRequestDeleted(payload = {}) { const deletedClaimId = String(payload.claimId || payload.claim_id || '').trim() @@ -335,11 +333,10 @@ export function useAppShell() { smartEntryInvalidatedDraftClaimId.value = deletedClaimId } - await reloadRequests() - void refreshApprovalInbox() - selectedRequestSnapshot.value = null - router.push({ name: activeView.value === 'documents' ? 'app-documents' : 'app-requests' }) - } + await reloadRequests() + selectedRequestSnapshot.value = null + router.push({ name: 'app-documents' }) + } return { activeRange, @@ -374,9 +371,10 @@ export function useAppShell() { selectedRequest, setView, smartEntryContext, - smartEntryInvalidatedDraftClaimId, - smartEntryOpen, - smartEntrySessionId, + smartEntryInvalidatedDraftClaimId, + smartEntryOpen, + smartEntryRevealToken, + smartEntrySessionId, detailAlerts, toast, topBarView diff --git a/web/src/composables/useDocumentCenterInbox.js b/web/src/composables/useDocumentCenterInbox.js new file mode 100644 index 0000000..a914b34 --- /dev/null +++ b/web/src/composables/useDocumentCenterInbox.js @@ -0,0 +1,176 @@ +import { computed, ref } from 'vue' + +import { fetchApprovalExpenseClaims, fetchArchivedExpenseClaims, fetchExpenseClaims } from '../services/reimbursements.js' +import { + DOCUMENT_VIEWED_KEYS_CHANGE_EVENT, + countNewDocuments, + readViewedDocumentKeys, + resolveDocumentNewKey +} from '../utils/documentCenterNewState.js' +import { mapExpenseClaimToRequest } from './useRequests.js' + +const SOURCE_PRIORITY = { + owned: 1, + approval: 2, + archive: 3 +} + +const documentRows = ref([]) +const viewedDocumentKeys = ref(readViewedDocumentKeys()) +const loading = ref(false) +let refreshTimer = null +let viewedKeysListenerAttached = false + +function normalizeClaimText(...values) { + for (const value of values) { + const normalized = String(value || '').trim() + if (normalized) { + return normalized + } + } + + return '' +} + +function buildDocumentInboxRow(claim, source) { + const request = mapExpenseClaimToRequest(claim) + const claimId = normalizeClaimText(request.claimId, request.id, claim?.id, claim?.claim_id) + const documentNo = normalizeClaimText(request.documentNo, request.claimNo, request.id, claim?.claim_no) + const documentKey = normalizeClaimText(claimId, documentNo) + + return documentKey + ? { + source, + claimId: claimId || documentKey, + documentNo, + documentKey: `${source}:${documentKey}` + } + : null +} + +function sourcePriority(row) { + return SOURCE_PRIORITY[row?.source] || 0 +} + +function mergeNonArchivedRows(rows) { + const rowMap = new Map() + + rows.filter(Boolean).forEach((row) => { + const key = normalizeClaimText(row.claimId, row.documentNo, row.documentKey) + if (!key) { + return + } + + const current = rowMap.get(key) + if (!current || sourcePriority(row) >= sourcePriority(current)) { + rowMap.set(key, row) + } + }) + + return Array.from(rowMap.values()) +} + +function uniqueRowsByNewKey(rows) { + const seenKeys = new Set() + + return rows.filter((row) => { + const key = resolveDocumentNewKey(row) + if (!key || seenKeys.has(key)) { + return false + } + + seenKeys.add(key) + return true + }) +} + +function mapClaimsToRows(claims, source) { + return Array.isArray(claims) + ? claims.map((claim) => buildDocumentInboxRow(claim, source)).filter(Boolean) + : [] +} + +export function buildDocumentInboxRows({ ownedClaims = [], approvalClaims = [], archivedClaims = [] } = {}) { + const ownedRows = mapClaimsToRows(ownedClaims, 'owned') + const approvalRows = mapClaimsToRows(approvalClaims, 'approval') + const archiveRows = mapClaimsToRows(archivedClaims, 'archive') + + return uniqueRowsByNewKey([ + ...mergeNonArchivedRows([...ownedRows, ...approvalRows]), + ...archiveRows + ]) +} + +function refreshViewedDocumentKeys() { + viewedDocumentKeys.value = readViewedDocumentKeys() +} + +function attachViewedKeysListener() { + if (typeof window === 'undefined' || viewedKeysListenerAttached) { + return + } + + window.addEventListener(DOCUMENT_VIEWED_KEYS_CHANGE_EVENT, refreshViewedDocumentKeys) + viewedKeysListenerAttached = true +} + +async function readClaimList(fetcher) { + const result = await fetcher() + return Array.isArray(result) ? result : [] +} + +export function useDocumentCenterInbox() { + attachViewedKeysListener() + + const unreadCount = computed(() => countNewDocuments(documentRows.value, viewedDocumentKeys.value)) + const hasUnread = computed(() => unreadCount.value > 0) + + async function refreshDocumentInbox() { + loading.value = true + + try { + const [ownedResult, approvalResult, archiveResult] = await Promise.allSettled([ + readClaimList(fetchExpenseClaims), + readClaimList(fetchApprovalExpenseClaims), + readClaimList(fetchArchivedExpenseClaims) + ]) + + documentRows.value = buildDocumentInboxRows({ + ownedClaims: ownedResult.status === 'fulfilled' ? ownedResult.value : [], + approvalClaims: approvalResult.status === 'fulfilled' ? approvalResult.value : [], + archivedClaims: archiveResult.status === 'fulfilled' ? archiveResult.value : [] + }) + refreshViewedDocumentKeys() + } finally { + loading.value = false + } + } + + function startDocumentInboxPolling(intervalMs = 45000) { + stopDocumentInboxPolling() + + if (typeof window === 'undefined') { + return + } + + refreshTimer = window.setInterval(() => { + void refreshDocumentInbox() + }, intervalMs) + } + + function stopDocumentInboxPolling() { + if (refreshTimer && typeof window !== 'undefined') { + window.clearInterval(refreshTimer) + refreshTimer = null + } + } + + return { + hasUnread, + loading, + refreshDocumentInbox, + startDocumentInboxPolling, + stopDocumentInboxPolling, + unreadCount + } +} diff --git a/web/src/composables/useNavigation.js b/web/src/composables/useNavigation.js index 415a293..f62a870 100644 --- a/web/src/composables/useNavigation.js +++ b/web/src/composables/useNavigation.js @@ -3,7 +3,7 @@ import { useRoute, useRouter } from 'vue-router' import { icons } from '../data/icons.js' -export const appViews = ['overview', 'workbench', 'documents', 'requests', 'approval', 'archive', 'policies', 'audit', 'employees', 'logs', 'settings'] +export const appViews = ['overview', 'workbench', 'documents', 'budget', 'policies', 'audit', 'employees', 'logs', 'settings'] export const navItems = [ { @@ -31,28 +31,12 @@ export const navItems = [ desc: '统一查看申请、报销、审批与归档。' }, { - id: 'requests', - label: '报销中心', - navHint: '查看和管理报销单据', - icon: icons.list, - title: '报销中心', - desc: '集中查看草稿、审批进度、票据状态与风险提示。' - }, - { - id: 'approval', - label: '审批中心', - navHint: '处理审批任务', - icon: icons.approval, - title: '审批中心', - desc: '按优先级处理待审批事项,控制时效与风险。' - }, - { - id: 'archive', - label: '归档中心', - navHint: '查阅公司已归档财务数据', - icon: icons.archive, - title: '归档中心', - desc: '集中保存公司已归档入账的报销单据,形成完整财务归档库。' + id: 'budget', + label: '预算中心', + navHint: '管理预算额度、预算占用与超预算预警', + icon: icons.budget, + title: '预算中心', + desc: '配置部门、项目及费用类型预算,跟踪申请占用、报销核销与超预算预警。' }, { id: 'policies', @@ -100,9 +84,7 @@ const viewRouteNames = { overview: 'app-overview', workbench: 'app-workbench', documents: 'app-documents', - requests: 'app-requests', - approval: 'app-approval', - archive: 'app-archive', + budget: 'app-budget', policies: 'app-policies', audit: 'app-audit', logs: 'app-logs', @@ -114,7 +96,7 @@ const routeNameViews = Object.fromEntries( Object.entries(viewRouteNames).map(([view, routeName]) => [routeName, view]) ) -routeNameViews['app-request-detail'] = 'requests' +routeNameViews['app-request-detail'] = 'documents' routeNameViews['app-document-detail'] = 'documents' routeNameViews['app-log-detail'] = 'logs' diff --git a/web/src/composables/useRequests.js b/web/src/composables/useRequests.js index 6cfc9a2..f60417f 100644 --- a/web/src/composables/useRequests.js +++ b/web/src/composables/useRequests.js @@ -148,6 +148,7 @@ function resolveDocumentTypeMeta(claim, typeCode) { const isApplication = explicitType === DOCUMENT_TYPE_APPLICATION || explicitType === 'expense_application' + || claimNo.startsWith('AP-') || claimNo.startsWith('APP-') || normalizedType === 'application' || normalizedType.endsWith('_application') diff --git a/web/src/data/icons.js b/web/src/data/icons.js index 42c9e16..c469100 100644 --- a/web/src/data/icons.js +++ b/web/src/data/icons.js @@ -5,6 +5,7 @@ export const icons = { workspace: iconPath(''), list: iconPath(''), approval: iconPath(''), + budget: iconPath(''), archive: iconPath(''), file: iconPath(''), skill: iconPath(''), diff --git a/web/src/router/index.js b/web/src/router/index.js index 25cb501..654fe3b 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -10,7 +10,7 @@ import LoginRouteView from '../views/LoginRouteView.vue' import SetupRouteView from '../views/SetupRouteView.vue' const appChildRoutes = appViews - .filter((view) => view !== 'requests' && view !== 'documents') + .filter((view) => view !== 'documents') .map((view) => ({ path: view, name: `app-${view}`, @@ -71,21 +71,24 @@ const router = createRouter({ }, { path: '/app/requests', - name: 'app-requests', - component: AppShellRouteView, - meta: { - requiresAuth: true, - appView: 'requests' - } + redirect: { name: 'app-documents' } }, { path: '/app/requests/:requestId', - name: 'app-request-detail', - component: AppShellRouteView, - meta: { - requiresAuth: true, - appView: 'requests' - } + redirect: (to) => ({ + name: 'app-document-detail', + params: { requestId: to.params.requestId }, + query: to.query, + hash: to.hash + }) + }, + { + path: '/app/approval', + redirect: { name: 'app-documents' } + }, + { + path: '/app/archive', + redirect: { name: 'app-documents' } }, { path: '/app/logs/:logKind/:logId', diff --git a/web/src/services/agentAssets.js b/web/src/services/agentAssets.js index cca6f2a..46e856d 100644 --- a/web/src/services/agentAssets.js +++ b/web/src/services/agentAssets.js @@ -220,6 +220,14 @@ export function setRiskRuleAssetEnabled(assetId, enabled, options = {}) { }) } +export function setRiskRuleAssetLevel(assetId, riskLevel, options = {}) { + return apiRequest(`/agent-assets/${assetId}/risk-rule-level`, { + method: 'POST', + body: JSON.stringify({ risk_level: riskLevel }), + headers: buildWriteHeaders(options) + }) +} + export function fetchAgentAssetSpreadsheetChangeRecords(assetId, limit = 30) { return apiRequest( `/agent-assets/${assetId}/spreadsheet/change-records${buildQuery({ limit })}` diff --git a/web/src/services/ontology.js b/web/src/services/ontology.js index 784a031..7b2c66c 100644 --- a/web/src/services/ontology.js +++ b/web/src/services/ontology.js @@ -1,8 +1,9 @@ import { apiRequest } from './api.js' -export function fetchOntologyParse(payload) { +export function fetchOntologyParse(payload, options = {}) { return apiRequest('/ontology/parse', { method: 'POST', - body: JSON.stringify(payload) + body: JSON.stringify(payload), + ...options }) } diff --git a/web/src/utils/accessControl.js b/web/src/utils/accessControl.js index 7761ffa..2d7f7f3 100644 --- a/web/src/utils/accessControl.js +++ b/web/src/utils/accessControl.js @@ -1,23 +1,20 @@ -export const DEFAULT_APP_VIEW_ORDER = [ - 'overview', - 'workbench', - 'documents', - 'requests', - 'approval', - 'archive', - 'policies', - 'audit', +export const DEFAULT_APP_VIEW_ORDER = [ + 'overview', + 'workbench', + 'documents', + 'budget', + 'policies', + 'audit', 'logs', 'employees', 'settings' ] -const ALWAYS_VISIBLE_VIEWS = new Set(['workbench', 'documents', 'requests', 'policies']) -const VIEW_ROLE_RULES = { - overview: ['finance', 'executive'], - approval: ['approver', 'finance', 'executive'], - archive: ['finance', 'executive', 'auditor'], - audit: ['auditor', 'finance'], +const ALWAYS_VISIBLE_VIEWS = new Set(['workbench', 'documents', 'policies']) +const VIEW_ROLE_RULES = { + overview: ['finance', 'executive'], + budget: ['finance', 'executive'], + audit: ['auditor', 'finance'], logs: ['manager'], employees: ['manager'], settings: ['manager'] @@ -48,18 +45,22 @@ export function isExecutiveUser(user) { return normalizedRoleCodes(user).includes('executive') } -export function canManageExpenseClaims(user) { - if (Boolean(user?.isAdmin)) { - return true - } - - return normalizedRoleCodes(user).some((roleCode) => CLAIM_MANAGER_ROLE_CODES.has(roleCode)) -} - -export function canReturnExpenseClaims(user) { - if (Boolean(user?.isAdmin)) { - return true - } +export function canManageExpenseClaims(user) { + if (Boolean(user?.isAdmin)) { + return true + } + + return normalizedRoleCodes(user).some((roleCode) => CLAIM_MANAGER_ROLE_CODES.has(roleCode)) +} + +export function canDeleteArchivedExpenseClaims(user) { + return Boolean(user?.isAdmin) +} + +export function canReturnExpenseClaims(user) { + if (Boolean(user?.isAdmin)) { + return true + } return normalizedRoleCodes(user).some((roleCode) => CLAIM_RETURN_ROLE_CODES.has(roleCode)) } @@ -72,14 +73,18 @@ export function canApproveLeaderExpenseClaims(user) { return normalizedRoleCodes(user).some((roleCode) => CLAIM_LEADER_APPROVAL_ROLE_CODES.has(roleCode)) } -export function canAccessAppView(user, viewId) { - if (!viewId || !user) { - return false - } - - if (isManagerUser(user)) { - return true - } +export function canAccessAppView(user, viewId) { + if (!viewId || !user) { + return false + } + + if (!DEFAULT_APP_VIEW_ORDER.includes(viewId)) { + return false + } + + if (isManagerUser(user)) { + return true + } if (ALWAYS_VISIBLE_VIEWS.has(viewId)) { return true diff --git a/web/src/utils/applicationApproval.js b/web/src/utils/applicationApproval.js new file mode 100644 index 0000000..f485a36 --- /dev/null +++ b/web/src/utils/applicationApproval.js @@ -0,0 +1,105 @@ +function normalizeText(value) { + return String(value || '').trim() +} + +function isEmailLike(value) { + return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(normalizeText(value)) +} + +function resolveDisplayName(...values) { + for (const value of values) { + const normalized = normalizeText(value) + if (normalized && !isEmailLike(normalized)) { + return normalized + } + } + return '' +} + +function toDate(value) { + if (!value) { + return null + } + const nextDate = new Date(value) + return Number.isNaN(nextDate.getTime()) ? null : nextDate +} + +function formatDateTime(value) { + const date = toDate(value) + if (!date) { + return '' + } + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + return `${year}-${month}-${day} ${hours}:${minutes}` +} + +function getRiskFlags(request) { + const flags = request?.riskFlags || request?.risk_flags_json || [] + return Array.isArray(flags) ? flags : [] +} + +function getLatestEvent(events) { + const sortedEvents = events + .filter((item) => item && typeof item === 'object') + .map((item) => ({ ...item, eventDate: toDate(item.created_at || item.createdAt) })) + .filter((item) => item.eventDate) + .sort((left, right) => left.eventDate.getTime() - right.eventDate.getTime()) + return sortedEvents.length ? sortedEvents[sortedEvents.length - 1] : null +} + +export function findLeaderApprovalEvent(request) { + return getLatestEvent( + getRiskFlags(request).filter((flag) => { + const source = normalizeText(flag?.source) + const eventType = normalizeText(flag?.event_type || flag?.eventType) + const previousStage = normalizeText(flag?.previous_approval_stage || flag?.previousApprovalStage) + const nextStage = normalizeText(flag?.next_approval_stage || flag?.nextApprovalStage) + return ( + source === 'manual_approval' + && ( + eventType === 'expense_application_approval' + || previousStage.includes('直属领导') + || previousStage.includes('领导审批') + || nextStage.includes('财务') + || nextStage.includes('审批完成') + ) + ) + }) + ) +} + +export function buildLeaderApprovalInfo(request) { + const event = findLeaderApprovalEvent(request) + if (!event) { + return { + opinion: '', + operator: '', + time: '', + generatedDraftClaimNo: '' + } + } + + return { + opinion: normalizeText(event.opinion) || normalizeText(event.message), + operator: resolveDisplayName( + event.operator, + event.operator_name, + event.operatorName, + request?.profileManager, + request?.managerName + ), + time: formatDateTime(event.created_at || event.createdAt), + generatedDraftClaimNo: normalizeText(event.generated_draft_claim_no || event.generatedDraftClaimNo) + } +} + +export function resolveGeneratedDraftClaimNo(responsePayload) { + const event = findLeaderApprovalEvent({ + riskFlags: responsePayload?.risk_flags_json || responsePayload?.riskFlags || [] + }) + return normalizeText(event?.generated_draft_claim_no || event?.generatedDraftClaimNo) +} diff --git a/web/src/utils/assistantSuggestedActionPrefill.js b/web/src/utils/assistantSuggestedActionPrefill.js index fe4c37a..d1d6db1 100644 --- a/web/src/utils/assistantSuggestedActionPrefill.js +++ b/web/src/utils/assistantSuggestedActionPrefill.js @@ -5,7 +5,7 @@ const APPLICATION_FIELD_PREFILLS = { reason: '事由:', days: '天数:', transport_mode: '出行方式:', - amount: '预计总费用:' + amount: '用户预估费用:' } export function resolveSuggestedActionPrefill(action = {}) { diff --git a/web/src/utils/detailAlerts.js b/web/src/utils/detailAlerts.js index b33c5c3..eacf026 100644 --- a/web/src/utils/detailAlerts.js +++ b/web/src/utils/detailAlerts.js @@ -34,6 +34,7 @@ function isApplicationDocumentRequest(request) { return ( documentType === 'application' || documentType === 'expense_application' + || claimNo.startsWith('AP-') || claimNo.startsWith('APP-') || typeCode === 'application' || typeCode.endsWith('_application') diff --git a/web/src/utils/documentCenterNewState.js b/web/src/utils/documentCenterNewState.js index 2b83a41..33590fb 100644 --- a/web/src/utils/documentCenterNewState.js +++ b/web/src/utils/documentCenterNewState.js @@ -1,5 +1,6 @@ const STORAGE_KEY = 'x-financial.documents.viewed' const SCOPE_STORAGE_KEY = 'x-financial.documents.scope' +export const DOCUMENT_VIEWED_KEYS_CHANGE_EVENT = 'x-financial.documents.viewed-change' function getStorage() { return typeof window === 'undefined' ? null : window.localStorage @@ -30,6 +31,10 @@ export function writeViewedDocumentKeys(keys, storage = getStorage()) { } storage.setItem(STORAGE_KEY, JSON.stringify(Array.from(keys).filter(Boolean))) + + if (typeof window !== 'undefined' && storage === window.localStorage) { + window.dispatchEvent(new CustomEvent(DOCUMENT_VIEWED_KEYS_CHANGE_EVENT)) + } } export function readDocumentScope(fallback, allowedScopes = [], storage = getStorage()) { diff --git a/web/src/utils/documentCenterRows.js b/web/src/utils/documentCenterRows.js new file mode 100644 index 0000000..72dfae1 --- /dev/null +++ b/web/src/utils/documentCenterRows.js @@ -0,0 +1,47 @@ +import { isApplicationRequestLike } from './documentClassification.js' + +const ARCHIVED_CLAIM_STATUSES = new Set(['approved', 'completed', 'paid']) + +function isArchivedRequestPayload(request) { + if (!request) { + return false + } + + const normalizedStatus = String(request.status || '').trim().toLowerCase() + const stage = String(request.approval_stage || request.approvalStage || '').trim() + + if (stage === '归档入账' || stage === 'completed') { + return true + } + + if (stage.includes('归档') || stage.includes('入账')) { + return true + } + + if ( + isApplicationRequestLike(request) + && ARCHIVED_CLAIM_STATUSES.has(normalizedStatus) + && ['审批完成', '申请归档'].includes(stage) + ) { + return true + } + + return ARCHIVED_CLAIM_STATUSES.has(normalizedStatus) + && (stage === '' || stage === '归档入账' || stage === 'completed') +} + +export function isArchivedDocumentRow(row) { + if (!row) { + return false + } + + if (row.archived === true) { + return true + } + + return isArchivedRequestPayload(row.rawRequest || row) +} + +export function excludeArchivedDocumentRows(rows) { + return (Array.isArray(rows) ? rows : []).filter((row) => !isArchivedDocumentRow(row)) +} diff --git a/web/src/utils/documentClassification.js b/web/src/utils/documentClassification.js new file mode 100644 index 0000000..48c96b2 --- /dev/null +++ b/web/src/utils/documentClassification.js @@ -0,0 +1,22 @@ +export function isApplicationRequestLike(value) { + const explicitType = String( + value?.documentTypeCode + || value?.document_type_code + || value?.documentType + || value?.document_type + || '' + ).trim() + const claimNo = String(value?.claim_no || value?.claimNo || value?.documentNo || value?.id || '') + .trim() + .toUpperCase() + const typeCode = String(value?.typeCode || value?.expense_type || value?.expenseType || '').trim() + + return ( + explicitType === 'application' + || explicitType === 'expense_application' + || claimNo.startsWith('AP-') + || claimNo.startsWith('APP-') + || typeCode === 'application' + || typeCode.endsWith('_application') + ) +} diff --git a/web/src/utils/expenseApplicationDetail.js b/web/src/utils/expenseApplicationDetail.js new file mode 100644 index 0000000..5328e58 --- /dev/null +++ b/web/src/utils/expenseApplicationDetail.js @@ -0,0 +1,106 @@ +function normalizeText(value) { + return String(value || '').trim() +} + +function isProvided(value) { + const text = normalizeText(value) + return Boolean(text) && !['待补充', '暂无', '无', '未知'].includes(text) +} + +function resolveApplicationDetailPayload(request = {}) { + const flags = Array.isArray(request.risk_flags_json) + ? request.risk_flags_json + : Array.isArray(request.riskFlags) + ? request.riskFlags + : [] + const detailFlag = flags.find((flag) => + flag && + typeof flag === 'object' && + normalizeText(flag.source) === 'application_detail' + ) + const detail = detailFlag?.application_detail || detailFlag?.applicationDetail || {} + return detail && typeof detail === 'object' ? detail : {} +} + +function pickDetailValue(detail, request, keys = [], fallback = '') { + for (const key of keys) { + const value = normalizeText(detail[key] ?? request[key]) + if (isProvided(value)) return value + } + return normalizeText(fallback) +} + +export function buildApplicationDetailFactItems(request = {}) { + const detail = resolveApplicationDetailPayload(request) + const amountDisplay = normalizeText(request.amountDisplay || request.amount) + const rows = [ + { + key: 'application_type', + label: '申请类型', + value: pickDetailValue(detail, request, ['application_type', 'typeLabel'], request.typeLabel) + }, + { + key: 'time', + label: '发生时间', + value: pickDetailValue(detail, request, ['time', 'occurredDisplay', 'period'], request.occurredDisplay) + }, + { + key: 'location', + label: '地点', + value: pickDetailValue(detail, request, ['location', 'sceneTarget', 'city'], request.sceneTarget) + }, + { + key: 'reason', + label: '事由', + value: pickDetailValue(detail, request, ['reason'], request.reason) + }, + { + key: 'days', + label: '天数', + value: pickDetailValue(detail, request, ['days']) + }, + { + key: 'transport_mode', + label: '出行方式', + value: pickDetailValue(detail, request, ['transport_mode']) + }, + { + key: 'grade', + label: '职级', + value: pickDetailValue(detail, request, ['grade', 'profileGrade', 'employee_grade'], request.profileGrade), + highlight: true + }, + { + key: 'lodging_daily_cap', + label: '住宿上限/天', + value: pickDetailValue(detail, request, ['lodging_daily_cap']), + highlight: true + }, + { + key: 'subsidy_daily_cap', + label: '补贴标准/天', + value: pickDetailValue(detail, request, ['subsidy_daily_cap']), + highlight: true + }, + { + key: 'transport_policy', + label: '交通费用口径', + value: pickDetailValue(detail, request, ['transport_policy'], '车票、机票暂无实时价格接口,按真实票据实报实销') + }, + { + key: 'policy_estimate', + label: '规则测算参考', + value: pickDetailValue(detail, request, ['policy_estimate']), + highlight: true + }, + { + key: 'amount', + label: '用户预估费用', + value: pickDetailValue(detail, request, ['amount'], amountDisplay), + highlight: true, + emphasis: true + } + ] + + return rows.filter((row) => isProvided(row.value)) +} diff --git a/web/src/utils/expenseApplicationOntology.js b/web/src/utils/expenseApplicationOntology.js index 94f2e9d..b75312e 100644 --- a/web/src/utils/expenseApplicationOntology.js +++ b/web/src/utils/expenseApplicationOntology.js @@ -47,6 +47,8 @@ const PROMPT_FIELD_LABELS = [ '出行方式', '交通方式', '交通工具', + '用户预估费用', + '预估费用', '预计总费用', '预计费用', '预计金额', @@ -186,10 +188,18 @@ export function expandApplicationTimeWithDays(timeText, days = 0) { return `${formatApplicationDate(startDate)} 至 ${formatApplicationDate(endDate)}` } +function normalizeApplicationTimeCandidate(value) { + const text = String(value || '').trim().replace(/^[,,、。;;\s]+/, '') + if (!text) return '' + if (/20\d{2}[-/.年]\d{1,2}[-/.月]\d{1,2}日?/.test(text)) return text + if (/今天|明天|后天|本周|下周|上周|本月|下月|月底|月初/.test(text)) return text + return '' +} + export function resolveApplicationTimeRange(ontology, prompt) { const range = ontology?.time_range || {} - const baseTime = resolveTimeRangeText(ontology) - || resolvePromptField(prompt, ['发生时间', '业务发生时间', '申请时间', '时间']) + const baseTime = normalizeApplicationTimeCandidate(resolveTimeRangeText(ontology)) + || normalizeApplicationTimeCandidate(resolvePromptField(prompt, ['发生时间', '业务发生时间', '申请时间', '时间'])) if (range.start_date && range.end_date && range.start_date !== range.end_date) { return `${range.start_date} 至 ${range.end_date}` } @@ -220,9 +230,94 @@ export function resolvePromptField(prompt, labels = []) { return match ? match[1].trim().replace(/[,。;;]$/, '') : '' } -export function resolveApplicationReason(prompt) { +function normalizeApplicationTransportMode(value) { + const text = String(value || '').trim() + if (/飞机|机票|航班/.test(text)) return '飞机' + if (/轮船|船票|客轮|渡轮|邮轮/.test(text)) return '轮船' + if (/火车|高铁|动车|铁路|列车/.test(text)) return '火车' + return text +} + +function cleanupApplicationReasonCandidate(value, location = '') { + let text = String(value || '').trim() + if (!text) return '' + + text = text + .replace(/^(?:发生时间|业务发生时间|申请时间|时间|地点|业务地点|发生地点|天数|出差天数|申请天数|出行方式|交通方式|交通工具|用户预估费用|预估费用|预计总费用|预计费用|预计金额|申请金额|预算|金额)\s*[::]\s*/u, '') + .replace(/20\d{2}[-/.年]\d{1,2}[-/.月]\d{1,2}日?/gu, '') + .replace(/(?:出差|申请)?(?:\d+|[一二两三四五六七八九十]{1,3})\s*天/gu, '') + .replace(/(?:¥|¥)?\s*\d+(?:\.\d+)?\s*(?:元|块|万元|人民币)?/gu, '') + .replace(/(?:高铁|动车|火车|铁路|列车|飞机|机票|航班|轮船|船票|客轮|渡轮|邮轮)/gu, '') + .replace(/[,,、。;;]+/g, ',') + .replace(/^\s*(申请|费用申请|业务|本次|去|到|前往|赴)\s*/u, '') + .replace(/^[,\s]+|[,\s]+$/g, '') + .trim() + + const normalizedLocation = String(location || '').trim() + if (normalizedLocation) { + const escapedLocation = escapeRegExp(normalizedLocation) + text = text + .replace(new RegExp(`^${escapedLocation}(?:出差)?(?:,|,|、)?`, 'u'), '') + .replace(new RegExp(`^(?:去|到|前往|赴)${escapedLocation}(?:出差)?(?:,|,|、)?`, 'u'), '') + .trim() + } + + if (!text) return '' + if (/^20\d{2}[-/.年]\d{1,2}[-/.月]\d{1,2}日?$/.test(text)) return '' + if (/^(?:\d+|[一二两三四五六七八九十]{1,3})\s*天$/.test(text)) return '' + if (/^[\u4e00-\u9fa5]{1,8}$/.test(text) && !/服务|支撑|支持|部署|实施|验收|拜访|对接|沟通|培训|会议|采购|安装|维护|上线|调试|项目/.test(text)) { + return '' + } + + return text +} + +function resolveApplicationLocationText(ontology, prompt) { + const locationEntity = resolveEntity(ontology, 'location') + return locationEntity?.normalized_value + || locationEntity?.value + || resolvePromptField(prompt, ['地点', '业务地点', '发生地点', '目的地']) + || '' +} + +export function resolveApplicationReason(prompt, ontology = null) { + const location = resolveApplicationLocationText(ontology, prompt) + const reasonEntity = resolveEntity(ontology, 'reason') || resolveEntity(ontology, 'business_reason') + const entityReason = String(reasonEntity?.normalized_value || reasonEntity?.value || '').trim() + if (entityReason) { + return cleanupApplicationReasonCandidate(entityReason, location) || entityReason + } + const labeled = resolvePromptField(prompt, ['事由', '申请事由', '出差事由', '原因', '用途']) - return labeled || String(prompt || '').trim() + if (labeled) { + return cleanupApplicationReasonCandidate(labeled, location) || labeled + } + + const candidates = String(prompt || '') + .split(/[\n,。;;]+/u) + .map((item) => cleanupApplicationReasonCandidate(item, location)) + .filter(Boolean) + const businessCandidate = candidates.find((item) => /服务|支撑|支持|部署|实施|验收|拜访|对接|沟通|培训|会议|采购|安装|维护|上线|调试|项目/.test(item)) + return businessCandidate || candidates.sort((left, right) => right.length - left.length)[0] || '' +} + +function resolveApplicationTransportMode(ontology, prompt) { + const transportEntity = resolveEntity(ontology, 'transport_mode') + || resolveEntity(ontology, 'transport') + const fromEntity = normalizeApplicationTransportMode( + transportEntity?.normalized_value || transportEntity?.value || '' + ) + if (fromEntity) return fromEntity + + const labeled = resolvePromptField(prompt, ['出行方式', '交通方式', '交通工具']) + const fromLabel = normalizeApplicationTransportMode(labeled) + if (fromLabel) return fromLabel + + const text = String(prompt || '') + if (/飞机|机票|航班/.test(text)) return '飞机' + if (/轮船|船票|客轮|渡轮|邮轮/.test(text)) return '轮船' + if (/火车|高铁|动车|铁路|列车/.test(text)) return '火车' + return '' } export function resolveAttachmentPolicy(expenseTypeCode, amount = 0) { @@ -260,17 +355,16 @@ export function resolveAttachmentPolicy(expenseTypeCode, amount = 0) { export function buildApplicationFieldsFromOntology(ontology, prompt, currentUser = {}) { const expenseTypeCode = resolveExpenseTypeCode(ontology) const amount = resolveApplicationAmount(ontology) - const locationEntity = resolveEntity(ontology, 'location') const documentTypeEntity = resolveEntity(ontology, 'document_type') const workflowStageEntity = resolveEntity(ontology, 'workflow_stage') const attachmentPolicy = resolveAttachmentPolicy(expenseTypeCode, amount.value) const timeRange = resolveApplicationTimeRange(ontology, prompt) || '待补充' - const location = locationEntity?.normalized_value - || locationEntity?.value - || resolvePromptField(prompt, ['地点', '业务地点', '发生地点']) + const location = resolveApplicationLocationText(ontology, prompt) || '待补充' - const reason = resolveApplicationReason(prompt) || '待补充' + const reason = resolveApplicationReason(prompt, ontology) || '待补充' + const days = resolvePromptDays(prompt) + const transportMode = resolveApplicationTransportMode(ontology, prompt) const fields = { documentType: documentTypeEntity?.normalized_value || 'expense_application', @@ -284,6 +378,8 @@ export function buildApplicationFieldsFromOntology(ontology, prompt, currentUser timeRange, location, reason, + days: days ? `${days}天` : '', + transportMode, applicant: currentUser.name || currentUser.username || '当前用户', department: currentUser.department || currentUser.departmentName || '待补充', preApprovalRequired: PRE_APPROVAL_TYPES.has(expenseTypeCode), diff --git a/web/src/utils/expenseApplicationPreview.js b/web/src/utils/expenseApplicationPreview.js new file mode 100644 index 0000000..208efa3 --- /dev/null +++ b/web/src/utils/expenseApplicationPreview.js @@ -0,0 +1,528 @@ +import { buildApplicationFieldsFromOntology } from './expenseApplicationOntology.js' + +const APPLICATION_SESSION_TYPE = 'application' +const APPLICATION_CREATE_PATTERN = /申请|事前|前置|预算|出差|差旅|采购|会务|会议|培训|办公用品|交通|住宿/ +const APPLICATION_QUERY_PATTERN = /查询|状态|进度|列表|有哪些|材料清单|需要哪些|制度|标准|规则|怎么规定/ + +export const APPLICATION_PREVIEW_FIELD_DEFINITIONS = [ + { key: 'applicationType', label: '申请类型' }, + { key: 'grade', label: '职级', highlight: true }, + { key: 'time', label: '发生时间' }, + { key: 'location', label: '地点' }, + { key: 'reason', label: '事由' }, + { key: 'days', label: '天数' }, + { key: 'transportMode', label: '出行方式' }, + { key: 'lodgingDailyCap', label: '住宿上限/天', highlight: true, editable: false, required: false }, + { key: 'subsidyDailyCap', label: '补贴标准/天', highlight: true, editable: false, required: false }, + { key: 'transportPolicy', label: '交通费用口径', highlight: true, editable: false, required: false }, + { key: 'policyEstimate', label: '规则测算参考', highlight: true, editable: false, required: false }, + { key: 'amount', label: '用户预估费用', highlight: true } +] + +export const APPLICATION_TRANSPORT_MODE_OPTIONS = ['火车', '飞机', '轮船'] + +const APPLICATION_POLICY_PENDING_TEXT = '填写地点和天数后自动测算' +const APPLICATION_TRANSPORT_REIMBURSEMENT_TEXT = '车票、机票暂无实时价格接口,按真实票据实报实销' + +function compactText(value) { + return String(value || '').replace(/\s+/g, '') +} + +function resolveFirstMatch(text, patterns = []) { + for (const pattern of patterns) { + const match = text.match(pattern) + const value = String(match?.groups?.value || match?.[1] || '').trim() + if (value) return value.replace(/[,。;;]$/, '') + } + return '' +} + +function normalizeDateText(value) { + return String(value || '').replace(/[/.]/g, '-').replace(/\s+/g, '') +} + +function parseIsoDate(value) { + const match = normalizeDateText(value).match(/^(20\d{2})-(\d{1,2})-(\d{1,2})$/) + if (!match) return null + const [, year, month, day] = match + const date = new Date(Date.UTC(Number(year), Number(month) - 1, Number(day))) + return Number.isNaN(date.getTime()) ? null : date +} + +function formatIsoDate(date) { + return date.toISOString().slice(0, 10) +} + +function buildEndDateFromDays(startText, daysText = '') { + const days = Number(String(daysText || '').replace(/[^\d]/g, '')) + const start = parseIsoDate(startText) + if (!days || !start) return '' + const end = new Date(start.getTime()) + end.setUTCDate(end.getUTCDate() + days) + return formatIsoDate(end) +} + +function resolveDaysFromDateRange(rangeText) { + const match = String(rangeText || '').match(/(20\d{2}[-/.]\d{1,2}[-/.]\d{1,2})\s*至\s*(20\d{2}[-/.]\d{1,2}[-/.]\d{1,2})/) + if (!match) return '' + const start = parseIsoDate(match[1]) + const end = parseIsoDate(match[2]) + if (!start || !end) return '' + const diffDays = Math.round((end.getTime() - start.getTime()) / 86400000) + return diffDays > 0 ? `${diffDays}天` : '1天' +} + +function resolveApplicationType(text) { + const compact = compactText(text) + if (/差旅|出差|高铁|动车|火车|飞机|机票|航班|酒店|住宿/.test(compact)) return '差旅费用申请' + if (/交通|出租车|的士|网约车|打车|通勤/.test(compact)) return '交通费用申请' + if (/住宿|酒店/.test(compact)) return '住宿费用申请' + if (/会务|会议|发布会|展会/.test(compact)) return '会务费用申请' + if (/采购|办公用品|文具|设备|耗材/.test(compact)) return '采购费用申请' + if (/培训|课程|学习/.test(compact)) return '培训费用申请' + return '费用申请' +} + +function resolveApplicationAmount(text) { + const compact = compactText(text) + const labeled = resolveFirstMatch(text, [ + /(?:用户预估费用|预估费用|申请金额|预计金额|预计费用|预计总费用|预算|金额|费用)\s*[::]?\s*(?\d+(?:\.\d+)?)\s*(?:元|块|人民币)?/u, + /(?\d+(?:\.\d+)?)\s*(?:元|块|人民币)/u + ]) + if (labeled) return `${labeled}元` + if (/不知道预算|预算不清楚|预算待定|待测算/.test(compact)) return '待测算' + return '' +} + +function resolveCurrentUserGrade(currentUser = {}) { + return String( + currentUser.grade + || currentUser.employeeGrade + || currentUser.employee_grade + || currentUser.profileGrade + || '' + ).trim() +} + +function parseApplicationDaysValue(value) { + const match = String(value || '').match(/\d+/) + const days = match ? Number(match[0]) : 0 + return Number.isFinite(days) && days > 0 ? Math.max(1, Math.floor(days)) : 0 +} + +function parseMoneyNumber(value) { + const normalized = String(value ?? '').replace(/[^\d.-]/g, '') + const amount = Number(normalized) + return Number.isFinite(amount) ? amount : null +} + +function formatPolicyMoney(value) { + const amount = parseMoneyNumber(value) + if (amount === null) return String(value || '').trim() + return new Intl.NumberFormat('zh-CN', { + minimumFractionDigits: 0, + maximumFractionDigits: Number.isInteger(amount) ? 0 : 2 + }).format(amount) +} + +function formatDailyPolicyMoney(value) { + const display = formatPolicyMoney(value) + return display ? `${display}元/天` : APPLICATION_POLICY_PENDING_TEXT +} + +function buildTransportPolicyText(transportMode) { + const mode = String(transportMode || '').trim() + if (!mode) return APPLICATION_TRANSPORT_REIMBURSEMENT_TEXT + return `${mode}票据暂无实时价格接口,按真实票据实报实销` +} + +function ensureApplicationPolicyFields(fields = {}) { + const nextFields = { ...fields } + if (!String(nextFields.lodgingDailyCap || '').trim()) { + nextFields.lodgingDailyCap = APPLICATION_POLICY_PENDING_TEXT + } + if (!String(nextFields.subsidyDailyCap || '').trim()) { + nextFields.subsidyDailyCap = APPLICATION_POLICY_PENDING_TEXT + } + if (!String(nextFields.transportPolicy || '').trim() || /实报实销/.test(String(nextFields.transportPolicy || ''))) { + nextFields.transportPolicy = buildTransportPolicyText(nextFields.transportMode) + } + if (!String(nextFields.policyEstimate || '').trim()) { + nextFields.policyEstimate = APPLICATION_POLICY_PENDING_TEXT + } + return nextFields +} + +function resolveApplicationDays(text) { + const value = resolveFirstMatch(text, [ + /(?:出差|申请)?(?\d+)\s*天/u, + /(?\d+)\s*(?:个)?工作日/u + ]) + return value ? `${value}天` : '' +} + +function resolveApplicationTime(text, daysText = '') { + const range = text.match( + /(20\d{2}[-/.]\d{1,2}[-/.]\d{1,2})\s*(?:至|到|~|—|–|--)\s*(20\d{2}[-/.]\d{1,2}[-/.]\d{1,2})/u + ) + if (range) { + return `${normalizeDateText(range[1])} 至 ${normalizeDateText(range[2])}` + } + + const single = resolveFirstMatch(text, [ + /(?:发生时间|业务发生时间|申请时间|时间)\s*[::]\s*(?20\d{2}[-/.]\d{1,2}[-/.]\d{1,2})/u, + /(?20\d{2}[-/.]\d{1,2}[-/.]\d{1,2})/u + ]) + if (!single) return '' + const normalized = normalizeDateText(single) + const endDate = buildEndDateFromDays(normalized, daysText) + return endDate ? `${normalized} 至 ${endDate}` : normalized +} + +function resolveApplicationLocation(text) { + return resolveFirstMatch(text, [ + /(?:地点|业务地点|发生地点|目的地)\s*[::]\s*(?[^。;;\n]+)/u, + /(?:去|到|前往)(?[\u4e00-\u9fa5,,、]{2,24}?)(?:出差|支撑|支持|部署|开会|培训|拜访|验收|项目|客户|。|\s|$)/u + ]) +} + +function resolveApplicationTransportMode(text) { + const compact = compactText(text) + if (/高铁|动车|火车|铁路/.test(compact)) return '火车' + if (/飞机|机票|航班/.test(compact)) return '飞机' + if (/轮船|船票|客轮|渡轮/.test(compact)) return '轮船' + return '' +} + +function stripKnownContextFromReason(value, context = {}) { + const location = String(context.location || '').trim() + let cleaned = String(value || '') + .replace(/(?:发生时间|业务发生时间|申请时间|时间)\s*[::]\s*(?=[,,、。;;\s]|$)/gu, '') + .replace(/(?:地点|业务地点|发生地点|目的地)\s*[::]\s*(?=[,,、。;;\s]|$)/gu, '') + .replace(/20\d{2}[-/.]\d{1,2}[-/.]\d{1,2}\s*(?:至|到|~|—|–|--)\s*20\d{2}[-/.]\d{1,2}[-/.]\d{1,2}/gu, '') + .replace(/20\d{2}[-/.]\d{1,2}[-/.]\d{1,2}/gu, '') + .replace(/(?:出差|申请)?\d+\s*天/gu, '') + .replace(/(?:用户预估费用|预估费用|预计总费用|预计费用|预计金额|申请金额|预算|金额|费用)\s*[::]?\s*\d+(?:\.\d+)?\s*(?:元|块|人民币)?/gu, '') + .replace(/(?:高铁|动车|火车|铁路|飞机|机票|航班|轮船|船票|客轮|渡轮|出租车|的士|网约车|打车|自驾)/gu, '') + .replace(/[,,、。;;]+/g, ',') + .replace(/^\s*(去|到|前往)/u, '') + .replace(/^[,\s]+|[,\s]+$/g, '') + .trim() + + if (location) { + const escapedLocation = location.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + cleaned = cleaned + .replace(new RegExp(`^${escapedLocation}(?:出差)?(?:,|,|、)?`, 'u'), '') + .replace(new RegExp(`^(?:去|到|前往)${escapedLocation}(?:出差)?(?:,|,|、)?`, 'u'), '') + .trim() + } + + return cleaned +} + +function pickBusinessReasonSegment(text) { + const segments = String(text || '') + .split(/[,,、。;;\n]+/u) + .map((item) => item.trim()) + .filter(Boolean) + return segments.find((item) => /服务|支撑|支持|部署|实施|验收|拜访|对接|沟通|培训|会议|采购|安装|维护|上线|调试|项目/.test(item)) || '' +} + +function resolveApplicationReason(text, context = {}) { + const labeled = resolveFirstMatch(text, [ + /(?:事由|申请事由|出差事由|原因|用途)\s*[::]\s*(?[^,。;;\n]+)/u + ]) + if (labeled) return stripKnownContextFromReason(labeled, context) + const cleaned = String(text || '') + .replace(/^\s*(我想|我要|帮我)?\s*(先)?\s*(发起|提交|申请)?\s*(一笔)?\s*(费用申请|申请)\s*/u, '') + const withoutContext = stripKnownContextFromReason(cleaned, context) + const businessSegment = pickBusinessReasonSegment(withoutContext) + if (businessSegment) return stripKnownContextFromReason(businessSegment, context) + return withoutContext +} + +function isApplicationPreviewValueProvided(value) { + const normalized = String(value || '').trim() + return Boolean(normalized) && !['待测算', '待补充', '未知'].includes(normalized) +} + +function resolveProvidedValue(value, fallback = '') { + return isApplicationPreviewValueProvided(value) ? String(value).trim() : fallback +} + +function normalizeApplicationTypeLabel(value, fallback = '') { + const label = String(value || '').trim() + if (!label || label === '其他费用') return fallback || '费用申请' + if (label.endsWith('费用申请') || label.endsWith('申请')) return label + if (label.endsWith('费用')) return `${label}申请` + if (label.endsWith('费')) return `${label.slice(0, -1)}费用申请` + return `${label}申请` +} + +function normalizeTransportModeOption(value, fallback = '') { + const text = String(value || '').trim() + if (/飞机|机票|航班/.test(text)) return '飞机' + if (/轮船|船票|客轮|渡轮|邮轮/.test(text)) return '轮船' + if (/火车|高铁|动车|铁路|列车/.test(text)) return '火车' + return APPLICATION_TRANSPORT_MODE_OPTIONS.includes(text) ? text : fallback +} + +function normalizeAmountFromOntology(fields = {}, fallback = '') { + const numericAmount = Number(fields.amount || 0) + if (Number.isFinite(numericAmount) && numericAmount > 0) { + return `${numericAmount}元` + } + + const display = String(fields.amountDisplay || '').trim() + if (display && display !== '待补充') { + const normalized = display.replace(/^¥\s*/, '').replace(/,/g, '').trim() + return normalized.endsWith('元') ? normalized : `${normalized}元` + } + + return fallback +} + +function buildMissingFields(fields) { + return APPLICATION_PREVIEW_FIELD_DEFINITIONS + .filter((item) => item.key !== 'applicationType' && item.required !== false) + .filter((item) => !isApplicationPreviewValueProvided(fields?.[item.key])) + .map((item) => item.label) +} + +export function buildApplicationPolicyEstimateRequest(preview = {}, currentUser = {}) { + const normalized = normalizeApplicationPreview(preview) + const fields = normalized.fields || {} + const days = parseApplicationDaysValue(fields.days) + const location = String(fields.location || '').trim() + const grade = String(fields.grade || resolveCurrentUserGrade(currentUser)).trim() + const applicationType = String(fields.applicationType || '').trim() + const transportMode = String(fields.transportMode || '').trim() + const shouldEstimate = /差旅|住宿|交通/.test(applicationType) || Boolean(transportMode) + + if (!shouldEstimate || !days || !location) { + return { + canCalculate: false, + reason: '缺少地点或天数', + payload: null + } + } + + return { + canCalculate: true, + reason: '', + payload: { + days, + location, + grade + } + } +} + +export function applyApplicationPolicyEstimateResult(preview = {}, result = {}, currentUser = {}) { + const fields = { ...(preview?.fields || {}) } + const days = Number(result?.days) || parseApplicationDaysValue(fields.days) || 1 + const hotelRate = formatPolicyMoney(result?.hotel_rate) + const hotelAmount = formatPolicyMoney(result?.hotel_amount) + const allowanceRate = formatPolicyMoney(result?.total_allowance_rate) + const allowanceAmount = formatPolicyMoney(result?.allowance_amount) + const totalAmount = formatPolicyMoney(result?.total_amount) + const matchedCity = String(result?.matched_city || fields.location || '').trim() + const grade = String(result?.grade || fields.grade || resolveCurrentUserGrade(currentUser)).trim() + + return normalizeApplicationPreview({ + ...preview, + fields: { + ...fields, + grade, + lodgingDailyCap: formatDailyPolicyMoney(result?.hotel_rate), + subsidyDailyCap: formatDailyPolicyMoney(result?.total_allowance_rate), + transportPolicy: buildTransportPolicyText(fields.transportMode), + policyEstimate: `住宿 ${hotelAmount}元 + 补贴 ${allowanceAmount}元 = ${totalAmount}元(${days}天,不含交通票据)`, + matchedCity, + ruleName: String(result?.rule_name || '').trim(), + ruleVersion: String(result?.rule_version || '').trim(), + hotelAmount: hotelAmount ? `${hotelAmount}元` : '', + allowanceAmount: allowanceAmount ? `${allowanceAmount}元` : '', + policyTotalAmount: totalAmount ? `${totalAmount}元` : '' + }, + policyEstimate: { + ...result, + grade, + matchedCity + }, + policyEstimateStatus: 'completed' + }) +} + +export function applyApplicationPolicyEstimateError(preview = {}, error = null, currentUser = {}) { + const fields = { ...(preview?.fields || {}) } + const message = String(error?.message || error || '').trim() + return normalizeApplicationPreview({ + ...preview, + fields: { + ...fields, + grade: fields.grade || resolveCurrentUserGrade(currentUser), + transportPolicy: buildTransportPolicyText(fields.transportMode), + policyEstimate: message ? `规则中心暂未完成测算:${message}` : APPLICATION_POLICY_PENDING_TEXT + }, + policyEstimateStatus: message ? 'failed' : 'pending' + }) +} + +export function shouldUseLocalApplicationPreview(rawText, options = {}) { + if (String(options.sessionType || '').trim() !== APPLICATION_SESSION_TYPE) return false + if (options.systemGenerated || options.reviewAction || Number(options.attachmentCount || 0) > 0) return false + + const compact = compactText(rawText) + if (!compact || APPLICATION_QUERY_PATTERN.test(compact)) return false + return APPLICATION_CREATE_PATTERN.test(compact) +} + +export function normalizeApplicationPreview(preview = {}) { + const fields = ensureApplicationPolicyFields(preview?.fields || {}) + const missingFields = buildMissingFields(fields) + return { + ...preview, + fields, + missingFields, + readyToSubmit: missingFields.length === 0 + } +} + +export function buildModelRefinedApplicationPreview(localPreview = {}, ontology = {}, rawText = '', currentUser = {}) { + const currentFields = localPreview?.fields || {} + const ontologyFields = buildApplicationFieldsFromOntology(ontology || {}, rawText, currentUser) + const parseStrategy = String(ontology?.parse_strategy || '').trim() + const refinedFields = { + ...currentFields, + applicationType: normalizeApplicationTypeLabel( + ontologyFields.expenseTypeLabel, + currentFields.applicationType + ), + time: resolveProvidedValue(ontologyFields.timeRange, currentFields.time), + location: resolveProvidedValue(ontologyFields.location, currentFields.location), + reason: resolveProvidedValue(ontologyFields.reason, currentFields.reason), + days: resolveProvidedValue(ontologyFields.days, currentFields.days), + transportMode: normalizeTransportModeOption( + ontologyFields.transportMode, + currentFields.transportMode + ), + amount: normalizeAmountFromOntology(ontologyFields, currentFields.amount), + grade: resolveProvidedValue(currentFields.grade, resolveCurrentUserGrade(currentUser)), + applicant: resolveProvidedValue(ontologyFields.applicant, currentFields.applicant), + department: resolveProvidedValue(ontologyFields.department, currentFields.department) + } + + return normalizeApplicationPreview({ + ...localPreview, + sourceText: String(rawText || localPreview.sourceText || '').trim(), + fields: refinedFields, + modelRefined: true, + parseStrategy, + modelReviewStatus: parseStrategy === 'llm_primary' ? 'completed' : 'fallback' + }) +} + +export function buildApplicationPreviewRows(preview = {}) { + const normalized = normalizeApplicationPreview(preview) + const fields = normalized.fields || {} + return APPLICATION_PREVIEW_FIELD_DEFINITIONS.map((item) => { + const rawValue = fields[item.key] + const value = String(rawValue || '').trim() || '待补充' + return { + ...item, + value, + editable: item.editable !== false, + highlight: Boolean(item.highlight), + missing: item.required !== false && !isApplicationPreviewValueProvided(rawValue) + } + }) +} + +export function buildApplicationPreviewSubmitText(preview = {}) { + const rows = buildApplicationPreviewRows(preview) + return [ + '费用申请确认提交', + ...rows.map((row) => `${row.label}:${row.value}`), + '', + '确认提交' + ].join('\n') +} + +export function buildLocalApplicationPreview(rawText, currentUser = {}) { + const sourceText = String(rawText || '').trim() + const explicitDays = resolveApplicationDays(sourceText) + const time = resolveApplicationTime(sourceText, explicitDays) + const days = explicitDays || resolveDaysFromDateRange(time) + const location = resolveApplicationLocation(sourceText) + const fields = { + applicationType: resolveApplicationType(sourceText), + time, + location, + reason: resolveApplicationReason(sourceText, { location }), + days, + transportMode: resolveApplicationTransportMode(sourceText), + amount: resolveApplicationAmount(sourceText), + grade: resolveCurrentUserGrade(currentUser), + applicant: currentUser.name || currentUser.username || '当前用户', + department: currentUser.department || currentUser.departmentName || '待补充' + } + + return normalizeApplicationPreview({ + sourceText, + fields, + modelReviewStatus: 'local' + }) +} + +export function buildApplicationTemplatePreview(currentUser = {}) { + return normalizeApplicationPreview({ + sourceText: '快速发起申请', + fields: { + applicationType: '费用申请', + time: '', + location: '', + reason: '', + days: '', + transportMode: '', + amount: '', + grade: resolveCurrentUserGrade(currentUser), + applicant: currentUser.name || currentUser.username || '当前用户', + department: currentUser.department || currentUser.departmentName || '待补充' + }, + modelReviewStatus: 'template' + }) +} + +export function buildLocalApplicationPreviewMessage(preview) { + const normalized = normalizeApplicationPreview(preview) + const modelReviewStatus = String(normalized.modelReviewStatus || '').trim() + return [ + modelReviewStatus === 'completed' + ? '我已完成模型复核,并整理成下方表格。请核查识别结果;点击对应行即可直接编辑。' + : modelReviewStatus === 'fallback' + ? '模型复核没有返回稳定结果,我已先按规则兜底整理成下方表格。请重点核查识别结果;点击对应行即可直接编辑。' + : modelReviewStatus === 'failed' + ? '模型复核暂时失败,我先保留一份临时核对表,方便您核查和补充信息。点击对应行即可直接编辑。' + : modelReviewStatus === 'template' + ? '我已为你准备好费用申请模板。本步骤不调用大模型,也不会保存草稿;请点击对应行直接填写。' + : '我先整理出下方表格,请核查识别结果。点击对应行即可直接编辑。' + ].join('\n') +} + +export function buildApplicationPreviewFooterMessage(preview) { + const normalized = normalizeApplicationPreview(preview) + const missingFields = Array.isArray(normalized.missingFields) ? normalized.missingFields : [] + const modelReviewStatus = String(normalized.modelReviewStatus || '').trim() + if (missingFields.length) { + return `当前还需要补充:${missingFields.join('、')}。补齐后我再帮您提交申请。` + } + if (modelReviewStatus === 'fallback') { + return '当前结果仅完成规则兜底复核,暂不直接提交。请核查表格内容,或稍后重新发起模型复核。' + } + if (modelReviewStatus === 'failed') { + return '当前结果仅作为临时预览,暂不直接提交。请稍后重试,或补充更明确的信息后再提交。' + } + + return '请核对表格信息无误,确认无误后点击 [确认](#application-submit) 提交至审批流程。' +} diff --git a/web/src/utils/expenseClaimArchive.js b/web/src/utils/expenseClaimArchive.js index a12ada8..5542c0d 100644 --- a/web/src/utils/expenseClaimArchive.js +++ b/web/src/utils/expenseClaimArchive.js @@ -1,3 +1,5 @@ +import { isApplicationRequestLike } from './documentClassification.js' + export function isArchivedExpenseClaim(claim) { const stage = String(claim?.approval_stage || claim?.approvalStage || '').trim() const status = String(claim?.status || '').trim().toLowerCase() @@ -10,5 +12,9 @@ export function isArchivedExpenseClaim(claim) { return false } + if (isApplicationRequestLike(claim) && ['审批完成', '申请归档'].includes(stage)) { + return true + } + return !stage || stage === '归档入账' || stage === 'completed' } diff --git a/web/src/utils/requestViewModel.js b/web/src/utils/requestViewModel.js index 1619e12..447d8c0 100644 --- a/web/src/utils/requestViewModel.js +++ b/web/src/utils/requestViewModel.js @@ -1,3 +1,5 @@ +import { isApplicationRequestLike } from './documentClassification.js' + const REQUEST_TYPE_META = { travel: { label: '差旅费', @@ -250,6 +252,29 @@ function resolveDisplayName(...values) { return '' } +export function isArchivedRequestView(request) { + const status = String(request?.status || '').trim().toLowerCase() + const approvalKey = String(request?.approvalKey || '').trim().toLowerCase() + const rawStage = String(request?.approval_stage || request?.approvalStage || '').trim() + const displayStage = String(request?.workflowNode || request?.node || '').trim() + const stage = rawStage || displayStage + + if (stage === '归档入账' || stage === 'completed' || stage.includes('归档') || stage.includes('入账')) { + return true + } + if ( + isApplicationRequestLike(request) + && ['approved', 'completed', 'paid'].includes(status) + && ['审批完成', '申请归档'].includes(stage) + ) { + return true + } + if (['approved', 'completed', 'paid'].includes(status)) { + return rawStage === '' || rawStage === '归档入账' || rawStage === 'completed' + } + return approvalKey === 'completed' +} + export function normalizeRequestForUi(request) { if (!request) { return null diff --git a/web/src/views/AppShellRouteView.vue b/web/src/views/AppShellRouteView.vue index 4cff484..0f22f23 100644 --- a/web/src/views/AppShellRouteView.vue +++ b/web/src/views/AppShellRouteView.vue @@ -18,13 +18,11 @@
    - - - - - - + + @@ -145,11 +127,12 @@ :initial-prompt="smartEntryContext.prompt" :initial-files="smartEntryContext.files" :initial-conversation="smartEntryContext.conversation" - :entry-source="smartEntryContext.source" - :request-context="smartEntryContext.request" - :invalidated-draft-claim-id="smartEntryInvalidatedDraftClaimId" - @close="closeSmartEntry" - @draft-saved="handleDraftSaved" + :entry-source="smartEntryContext.source" + :request-context="smartEntryContext.request" + :invalidated-draft-claim-id="smartEntryInvalidatedDraftClaimId" + :reopen-token="smartEntryRevealToken" + @close="closeSmartEntry" + @draft-saved="handleDraftSaved" />
@@ -162,13 +145,11 @@ import TopBar from '../components/layout/TopBar.vue' import FilterBar from '../components/layout/FilterBar.vue' import OverviewView from './OverviewView.vue' import PersonalWorkbenchView from './PersonalWorkbenchView.vue' -import TravelReimbursementCreateView from './TravelReimbursementCreateView.vue' -import TravelRequestDetailView from './TravelRequestDetailView.vue' -import DocumentsCenterView from './DocumentsCenterView.vue' -import RequestsView from './RequestsView.vue' -import ApprovalCenterView from './ApprovalCenterView.vue' -import ArchiveCenterView from './ArchiveCenterView.vue' -import PoliciesView from './PoliciesView.vue' +import TravelReimbursementCreateView from './TravelReimbursementCreateView.vue' +import TravelRequestDetailView from './TravelRequestDetailView.vue' +import DocumentsCenterView from './DocumentsCenterView.vue' +import BudgetCenterView from './BudgetCenterView.vue' +import PoliciesView from './PoliciesView.vue' import AuditView from './AuditView.vue' import LogsView from './LogsView.vue' import LogDetailView from './LogDetailView.vue' @@ -222,9 +203,10 @@ const { search, selectedRequest, smartEntryContext, - smartEntryInvalidatedDraftClaimId, - smartEntryOpen, - smartEntrySessionId, + smartEntryInvalidatedDraftClaimId, + smartEntryOpen, + smartEntryRevealToken, + smartEntrySessionId, toast, topBarView } = useAppShell() diff --git a/web/src/views/AuditView.vue b/web/src/views/AuditView.vue index cd085e2..f05099d 100644 --- a/web/src/views/AuditView.vue +++ b/web/src/views/AuditView.vue @@ -1,16 +1,16 @@ -