feat: 添加风险规则及 agent assets 功能增强
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.expense.consecutive_transport_receipts",
|
||||
"name": "连号交通票据",
|
||||
"enabled": true,
|
||||
"risk_dimension": "consecutive_receipts",
|
||||
"ontology_signal": "consecutive_transport_receipts",
|
||||
"evaluator": "consecutive_transport_receipts",
|
||||
"applies_to": {
|
||||
"expense_types": ["transport", "travel"],
|
||||
"min_attachments": 2
|
||||
},
|
||||
"inputs": {
|
||||
"invoice_no": "attachment.invoice_no"
|
||||
},
|
||||
"params": {
|
||||
"min_consecutive_count": 3
|
||||
},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "medium",
|
||||
"action": "manual_review"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 三、车辆交通 / 连号票集中报销",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.expense.entertainment_missing_detail",
|
||||
"name": "招待费事由不完整",
|
||||
"enabled": true,
|
||||
"risk_dimension": "entertainment_detail",
|
||||
"ontology_signal": "entertainment_missing_detail",
|
||||
"evaluator": "entertainment_reason_missing",
|
||||
"applies_to": {
|
||||
"domains": ["meal"]
|
||||
},
|
||||
"inputs": {
|
||||
"reason": "claim.reason_corpus"
|
||||
},
|
||||
"params": {},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "medium",
|
||||
"action": "warn"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 三、餐费招待 / 业务招待无事由对象",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.expense.meal_localized_as_travel",
|
||||
"name": "同城餐饮混入差旅",
|
||||
"enabled": true,
|
||||
"risk_dimension": "meal_travel_mix",
|
||||
"ontology_signal": "meal_as_travel",
|
||||
"evaluator": "meal_as_travel_same_city",
|
||||
"applies_to": {
|
||||
"domains": ["travel"]
|
||||
},
|
||||
"inputs": {
|
||||
"declared": "claim.location",
|
||||
"meal_city": "attachment.cities"
|
||||
},
|
||||
"params": {},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "medium",
|
||||
"action": "warn"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 三、餐费招待 / 同城餐饮归集异地差旅",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
29
server/rules/risk-rules/risk.expense.reason_too_brief.json
Normal file
29
server/rules/risk-rules/risk.expense.reason_too_brief.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.expense.reason_too_brief",
|
||||
"name": "报销事由过短",
|
||||
"enabled": true,
|
||||
"risk_dimension": "reason_quality",
|
||||
"ontology_signal": "reason_too_brief",
|
||||
"evaluator": "reason_too_brief",
|
||||
"applies_to": {},
|
||||
"inputs": {
|
||||
"reason": "claim.reason_corpus"
|
||||
},
|
||||
"params": {
|
||||
"min_reason_length": 6
|
||||
},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "medium",
|
||||
"action": "warn"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 通用 / 事由不足以支撑真实性判断",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.invoice.claimant_buyer_name_match",
|
||||
"name": "报销人与发票抬头一致",
|
||||
"enabled": true,
|
||||
"risk_dimension": "identity_consistency",
|
||||
"ontology_signal": "buyer_name_mismatch",
|
||||
"evaluator": "identity_consistency",
|
||||
"applies_to": {
|
||||
"min_attachments": 1
|
||||
},
|
||||
"inputs": {
|
||||
"claimant": "claim.employee_name",
|
||||
"buyer": "attachment.buyer_name"
|
||||
},
|
||||
"params": {
|
||||
"allow_keywords": ["代报", "集团", "公司", "有限公司"]
|
||||
},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "high",
|
||||
"action": "manual_review"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 二、发票类 / 抬头错误",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
30
server/rules/risk-rules/risk.invoice.cross_year_invoice.json
Normal file
30
server/rules/risk-rules/risk.invoice.cross_year_invoice.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.invoice.cross_year_invoice",
|
||||
"name": "跨年发票入账",
|
||||
"enabled": true,
|
||||
"risk_dimension": "cross_year_invoice",
|
||||
"ontology_signal": "cross_year_invoice",
|
||||
"evaluator": "cross_year_invoice",
|
||||
"applies_to": {
|
||||
"min_attachments": 1
|
||||
},
|
||||
"inputs": {
|
||||
"invoice_date": "attachment.invoice_date",
|
||||
"claim_date": ["claim.occurred_at", "item.item_date"]
|
||||
},
|
||||
"params": {},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "medium",
|
||||
"action": "warn"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 二、发票类 / 跨年发票",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.invoice.document_expense_mismatch",
|
||||
"name": "开票内容与报销场景不符",
|
||||
"enabled": true,
|
||||
"risk_dimension": "document_expense_mismatch",
|
||||
"ontology_signal": "document_expense_mismatch",
|
||||
"evaluator": "document_expense_mismatch",
|
||||
"applies_to": {
|
||||
"min_attachments": 1
|
||||
},
|
||||
"inputs": {
|
||||
"document_type": "attachment.document_type",
|
||||
"expense_type": ["claim.expense_type", "item.item_type"]
|
||||
},
|
||||
"params": {},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "medium",
|
||||
"action": "warn"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 二、发票类 / 开票内容与业务不符",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
29
server/rules/risk-rules/risk.invoice.duplicate_invoice.json
Normal file
29
server/rules/risk-rules/risk.invoice.duplicate_invoice.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.invoice.duplicate_invoice",
|
||||
"name": "发票重复报销",
|
||||
"enabled": true,
|
||||
"risk_dimension": "duplicate_invoice",
|
||||
"ontology_signal": "duplicate_invoice",
|
||||
"evaluator": "duplicate_invoice",
|
||||
"applies_to": {
|
||||
"min_attachments": 1
|
||||
},
|
||||
"inputs": {
|
||||
"invoice_no": "attachment.invoice_no"
|
||||
},
|
||||
"params": {},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "high",
|
||||
"action": "block"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 二、发票类 / 重复报销",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.invoice.vague_goods_description",
|
||||
"name": "发票品名过于笼统",
|
||||
"enabled": true,
|
||||
"risk_dimension": "vague_goods_description",
|
||||
"ontology_signal": "vague_goods_description",
|
||||
"evaluator": "vague_goods_description",
|
||||
"applies_to": {
|
||||
"expense_types": ["office", "other"],
|
||||
"min_attachments": 1
|
||||
},
|
||||
"inputs": {
|
||||
"ocr": "attachment.ocr_text"
|
||||
},
|
||||
"params": {},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "medium",
|
||||
"action": "warn"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 二、发票类 / 品名笼统",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.invoice.void_or_red_invoice",
|
||||
"name": "作废或红冲发票",
|
||||
"enabled": true,
|
||||
"risk_dimension": "void_or_red_invoice",
|
||||
"ontology_signal": "void_or_red_invoice",
|
||||
"evaluator": "invoice_void_or_red",
|
||||
"applies_to": {
|
||||
"min_attachments": 1
|
||||
},
|
||||
"inputs": {
|
||||
"status": "attachment.invoice_status",
|
||||
"ocr": "attachment.ocr_text"
|
||||
},
|
||||
"params": {},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "high",
|
||||
"action": "block"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 二、发票类 / 作废红冲发票",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.travel.base_location_overlap",
|
||||
"name": "常驻地重合出差风险",
|
||||
"enabled": true,
|
||||
"risk_dimension": "base_location_overlap",
|
||||
"ontology_signal": "base_location_overlap",
|
||||
"evaluator": "base_location_overlap",
|
||||
"applies_to": {
|
||||
"domains": ["travel"]
|
||||
},
|
||||
"inputs": {
|
||||
"employee_base": "employee.location",
|
||||
"declared": "claim.location"
|
||||
},
|
||||
"params": {},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "high",
|
||||
"action": "manual_review"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 一、出差类 / 两头在外",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.travel.destination_receipt_location",
|
||||
"name": "申报地点与票据地点一致",
|
||||
"risk_dimension": "location_consistency",
|
||||
"ontology_signal": "location_mismatch",
|
||||
"evaluator": "location_consistency",
|
||||
"inputs": {
|
||||
"declared": "claim.location",
|
||||
"evidence": ["attachment.cities", "item.item_location"]
|
||||
},
|
||||
"params": {
|
||||
"match_mode": "city_fuzzy",
|
||||
"missing_evidence": "warn"
|
||||
},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "high",
|
||||
"action": "manual_review",
|
||||
"message_template": "申报地点 {declared} 与票据识别地点 {evidence} 不一致"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"updated_at": "2026-05-18"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.travel.hotel_without_itinerary",
|
||||
"name": "住宿城市与行程不一致",
|
||||
"enabled": true,
|
||||
"risk_dimension": "hotel_itinerary",
|
||||
"ontology_signal": "hotel_itinerary_mismatch",
|
||||
"evaluator": "hotel_without_itinerary",
|
||||
"applies_to": {
|
||||
"domains": ["travel"],
|
||||
"expense_types": ["hotel", "travel"]
|
||||
},
|
||||
"inputs": {
|
||||
"declared": "claim.location",
|
||||
"hotel": "attachment.hotel_city",
|
||||
"itinerary": "attachment.route_cities"
|
||||
},
|
||||
"params": {},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "high",
|
||||
"action": "manual_review"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 三、住宿费 / 夜间异地住宿、酒店连续多天",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.travel.intracity_travel_claim",
|
||||
"name": "同城虚报差旅补贴",
|
||||
"enabled": true,
|
||||
"risk_dimension": "intracity_travel",
|
||||
"ontology_signal": "intracity_travel",
|
||||
"evaluator": "intracity_travel_claim",
|
||||
"applies_to": {
|
||||
"domains": ["travel"]
|
||||
},
|
||||
"inputs": {
|
||||
"declared": "claim.location",
|
||||
"evidence": ["attachment.route", "attachment.cities"]
|
||||
},
|
||||
"params": {},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "high",
|
||||
"action": "manual_review"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 一、出差类 / 同城虚报差旅",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"rule_code": "risk.travel.multi_city_reason_required",
|
||||
"name": "多城市行程需说明",
|
||||
"enabled": true,
|
||||
"risk_dimension": "multi_city_itinerary",
|
||||
"ontology_signal": "multi_city_itinerary",
|
||||
"evaluator": "multi_city_reason_required",
|
||||
"applies_to": {
|
||||
"domains": ["travel"]
|
||||
},
|
||||
"inputs": {
|
||||
"reason": "claim.reason_corpus",
|
||||
"cities": ["attachment.cities", "item.item_location"]
|
||||
},
|
||||
"params": {},
|
||||
"outcomes": {
|
||||
"pass": { "severity": "none", "action": "continue" },
|
||||
"fail": {
|
||||
"severity": "medium",
|
||||
"action": "warn"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"owner": "风控与审计部",
|
||||
"stability": "platform_builtin",
|
||||
"source_ref": "常用risk.txt / 一、出差类 / 绕道出行、行程不符",
|
||||
"updated_at": "2026-05-19"
|
||||
}
|
||||
}
|
||||
28
server/scripts/sync_platform_risk_rules.py
Normal file
28
server/scripts/sync_platform_risk_rules.py
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Sync platform risk rule assets from server/rules/risk-rules/*.json."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SERVER_SRC = Path(__file__).resolve().parents[1] / "src"
|
||||
if str(SERVER_SRC) not in sys.path:
|
||||
sys.path.insert(0, str(SERVER_SRC))
|
||||
|
||||
from app.db.session import get_session_factory # noqa: E402
|
||||
from app.services.agent_foundation import AgentFoundationService # noqa: E402
|
||||
|
||||
|
||||
def main() -> None:
|
||||
db = get_session_factory()()
|
||||
try:
|
||||
count = AgentFoundationService(db).sync_platform_risk_rules_from_library()
|
||||
db.commit()
|
||||
print(f"Synced {count} risk rule manifest(s) from library.")
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
13
server/scripts/test_rule_json_api.py
Normal file
13
server/scripts/test_rule_json_api.py
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import urllib.request
|
||||
|
||||
base = "http://127.0.0.1:8000/api/v1"
|
||||
items = json.loads(urllib.request.urlopen(f"{base}/agent-assets?asset_type=rule").read())
|
||||
risk = next((i for i in items if str(i.get("code", "")).startswith("risk.")), None)
|
||||
print("risk asset:", risk.get("code") if risk else None)
|
||||
if not risk:
|
||||
raise SystemExit(1)
|
||||
resp = urllib.request.urlopen(f"{base}/agent-assets/{risk['id']}/rule-json")
|
||||
payload = json.loads(resp.read())
|
||||
print("rule-json ok:", payload.get("file_name"), payload.get("evaluator"))
|
||||
@@ -27,7 +27,6 @@ from app.schemas.agent_asset import (
|
||||
AgentAssetRuleJsonWrite,
|
||||
AgentAssetSpreadsheetChangeRecordRead,
|
||||
AgentAssetUpdate,
|
||||
AgentAssetVersionCompareRead,
|
||||
AgentAssetVersionCreate,
|
||||
AgentAssetVersionRead,
|
||||
AgentAssetVersionTimelineItemRead,
|
||||
@@ -167,7 +166,7 @@ def get_agent_asset_spreadsheet_onlyoffice_config(
|
||||
db: DbSession,
|
||||
version: Annotated[
|
||||
str | None,
|
||||
Query(description="可选的规则版本号;不传时默认当前版本。"),
|
||||
Query(description="兼容旧前端的可选参数;表格规则始终打开当前规则表。"),
|
||||
] = None,
|
||||
) -> AgentAssetOnlyOfficeConfigRead:
|
||||
try:
|
||||
@@ -184,7 +183,7 @@ def get_agent_asset_spreadsheet_onlyoffice_config(
|
||||
"/{asset_id}/spreadsheet/content",
|
||||
response_class=FileResponse,
|
||||
summary="下载或预览规则 Excel 文件",
|
||||
description="按版本返回规则的 Excel 快照,用于浏览器预览或下载。",
|
||||
description="返回当前规则 Excel 文件,用于浏览器预览或下载。",
|
||||
)
|
||||
def get_agent_asset_spreadsheet_content(
|
||||
asset_id: str,
|
||||
@@ -192,7 +191,7 @@ def get_agent_asset_spreadsheet_content(
|
||||
db: DbSession,
|
||||
version: Annotated[
|
||||
str | None,
|
||||
Query(description="可选的规则版本号;不传时默认当前版本。"),
|
||||
Query(description="兼容旧前端的可选参数;不传时返回当前规则表。"),
|
||||
] = None,
|
||||
) -> FileResponse:
|
||||
try:
|
||||
@@ -215,18 +214,18 @@ def get_agent_asset_spreadsheet_content(
|
||||
def get_agent_asset_spreadsheet_onlyoffice_content(
|
||||
asset_id: str,
|
||||
db: DbSession,
|
||||
version: Annotated[
|
||||
str,
|
||||
Query(min_length=1, description="规则版本号。"),
|
||||
],
|
||||
access_token: Annotated[
|
||||
str,
|
||||
Query(min_length=1, description="ONLYOFFICE 临时访问令牌。"),
|
||||
],
|
||||
version: Annotated[
|
||||
str | None,
|
||||
Query(description="兼容旧 ONLYOFFICE URL;当前表格模式不再使用。"),
|
||||
] = None,
|
||||
) -> FileResponse:
|
||||
try:
|
||||
service = AgentAssetService(db)
|
||||
service.validate_rule_spreadsheet_access_token(asset_id, version, access_token)
|
||||
service.validate_rule_spreadsheet_access_token(asset_id, access_token)
|
||||
file_path, media_type, filename = service.get_rule_spreadsheet_content(
|
||||
asset_id,
|
||||
version=version,
|
||||
@@ -246,7 +245,7 @@ def get_agent_asset_spreadsheet_onlyoffice_content(
|
||||
response_model=AgentAssetRead,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="上传规则 Excel 文件",
|
||||
description="为指定规则上传新的 Excel 快照,并自动生成新规则版本。",
|
||||
description="为指定规则上传新的 Excel 文件,并记录本次表格修改。",
|
||||
)
|
||||
def upload_agent_asset_spreadsheet(
|
||||
asset_id: str,
|
||||
@@ -311,16 +310,16 @@ def import_agent_asset_spreadsheet_content(
|
||||
"/{asset_id}/spreadsheet/onlyoffice/callback",
|
||||
response_model=AgentAssetOnlyOfficeCallbackRead,
|
||||
summary="接收规则 Excel 的 ONLYOFFICE 回调",
|
||||
description="接收 ONLYOFFICE 回写内容,并自动生成新的规则版本。",
|
||||
description="接收 ONLYOFFICE 回写内容,并记录本次表格修改。",
|
||||
)
|
||||
def handle_agent_asset_spreadsheet_onlyoffice_callback(
|
||||
asset_id: str,
|
||||
payload: AgentAssetOnlyOfficeCallbackWrite,
|
||||
db: DbSession,
|
||||
version: Annotated[
|
||||
str,
|
||||
Query(min_length=1, description="打开编辑器时对应的规则版本号。"),
|
||||
],
|
||||
str | None,
|
||||
Query(description="兼容旧 ONLYOFFICE 回调;当前表格模式不再使用。"),
|
||||
] = None,
|
||||
actor_name: Annotated[
|
||||
str | None,
|
||||
Query(description="发起编辑的用户显示名。"),
|
||||
@@ -601,25 +600,3 @@ def get_agent_asset_version_timeline(
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{asset_id}/versions/compare",
|
||||
response_model=AgentAssetVersionCompareRead,
|
||||
summary="比较两个规则表版本",
|
||||
description="对比两个 Excel 规则表版本的工作表变化与单元格级差异。",
|
||||
)
|
||||
def compare_agent_asset_spreadsheet_versions(
|
||||
asset_id: str,
|
||||
_: CurrentUser,
|
||||
db: DbSession,
|
||||
base_version: Annotated[str, Query(min_length=1, description="基准版本号")],
|
||||
target_version: Annotated[str, Query(min_length=1, description="对比版本号")],
|
||||
) -> AgentAssetVersionCompareRead:
|
||||
try:
|
||||
return AgentAssetService(db).compare_spreadsheet_versions(
|
||||
asset_id,
|
||||
base_version=base_version,
|
||||
target_version=target_version,
|
||||
)
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
@@ -133,22 +133,10 @@ class AgentAssetSpreadsheetDiffSheetRead(BaseModel):
|
||||
change_type: str
|
||||
|
||||
|
||||
class AgentAssetVersionCompareRead(BaseModel):
|
||||
base_version: str
|
||||
target_version: str
|
||||
added_sheet_count: int = 0
|
||||
removed_sheet_count: int = 0
|
||||
changed_sheet_count: int = 0
|
||||
changed_cell_count: int = 0
|
||||
sheet_changes: list[AgentAssetSpreadsheetDiffSheetRead] = Field(default_factory=list)
|
||||
cell_changes: list[AgentAssetSpreadsheetDiffCellRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class AgentAssetSpreadsheetChangeRecordRead(BaseModel):
|
||||
id: str
|
||||
actor: str
|
||||
changed_at: datetime
|
||||
version: str | None = None
|
||||
summary: str
|
||||
sheet_changes: list[AgentAssetSpreadsheetDiffSheetRead] = Field(default_factory=list)
|
||||
cell_changes: list[AgentAssetSpreadsheetDiffCellRead] = Field(default_factory=list)
|
||||
|
||||
@@ -36,7 +36,6 @@ from app.schemas.agent_asset import (
|
||||
AgentAssetSpreadsheetDiffCellRead,
|
||||
AgentAssetSpreadsheetDiffSheetRead,
|
||||
AgentAssetUpdate,
|
||||
AgentAssetVersionCompareRead,
|
||||
AgentAssetVersionCreate,
|
||||
AgentAssetVersionRead,
|
||||
AgentAssetVersionTimelineItemRead,
|
||||
@@ -511,18 +510,16 @@ class AgentAssetService:
|
||||
return self._build_onlyoffice_spreadsheet_config(
|
||||
asset_id=asset_id,
|
||||
current_user=current_user,
|
||||
resolved_version=resolved_version,
|
||||
metadata=metadata,
|
||||
editable=resolved_version == PREVIEW_RULE_CURRENT_VERSION,
|
||||
)
|
||||
|
||||
asset = self._require_spreadsheet_rule(asset_id)
|
||||
resolved_version, metadata = self._resolve_current_spreadsheet_meta(asset)
|
||||
_, metadata = self._resolve_current_spreadsheet_meta(asset)
|
||||
editable = self._can_edit_current_spreadsheet(current_user)
|
||||
return self._build_onlyoffice_spreadsheet_config(
|
||||
asset_id=asset.id,
|
||||
current_user=current_user,
|
||||
resolved_version=resolved_version,
|
||||
metadata=metadata,
|
||||
editable=editable,
|
||||
)
|
||||
@@ -555,7 +552,6 @@ class AgentAssetService:
|
||||
def validate_rule_spreadsheet_access_token(
|
||||
self,
|
||||
asset_id: str,
|
||||
version: str,
|
||||
access_token: str,
|
||||
) -> None:
|
||||
onlyoffice_settings = resolve_onlyoffice_settings()
|
||||
@@ -571,7 +567,6 @@ class AgentAssetService:
|
||||
if (
|
||||
payload.get("scope") != "agent-asset-spreadsheet"
|
||||
or payload.get("asset_id") != asset_id
|
||||
or payload.get("version") != version
|
||||
):
|
||||
raise ValueError("ONLYOFFICE 文件访问令牌无效。")
|
||||
|
||||
@@ -604,7 +599,6 @@ class AgentAssetService:
|
||||
)
|
||||
changed_sheet_count = self._count_changed_sheets(sheet_changes, cell_changes)
|
||||
changed_cell_count = len(cell_changes)
|
||||
next_version = self._next_available_version(asset)
|
||||
|
||||
metadata = self._store_current_rule_spreadsheet(
|
||||
asset,
|
||||
@@ -613,45 +607,10 @@ class AgentAssetService:
|
||||
actor=actor,
|
||||
source=source,
|
||||
)
|
||||
snapshot_metadata = self.spreadsheet_manager.store_rule_library_spreadsheet_snapshot(
|
||||
library=self._resolve_spreadsheet_rule_library(asset),
|
||||
asset_id=asset.id,
|
||||
version=next_version,
|
||||
file_name=file_name,
|
||||
content=content,
|
||||
actor_name=actor,
|
||||
source=source,
|
||||
)
|
||||
operation_label = (
|
||||
change_note
|
||||
or (
|
||||
"ONLYOFFICE 在线编辑"
|
||||
if source == "onlyoffice"
|
||||
else f"上传并覆盖当前规则表:{normalized_name}"
|
||||
)
|
||||
)
|
||||
summary = self._build_spreadsheet_change_summary(
|
||||
operation_label,
|
||||
sheet_changes,
|
||||
cell_changes,
|
||||
)
|
||||
version_content = self.spreadsheet_manager.build_version_markdown(
|
||||
rule_name=asset.name,
|
||||
version=next_version,
|
||||
metadata=snapshot_metadata,
|
||||
)
|
||||
self.create_version(
|
||||
asset.id,
|
||||
AgentAssetVersionCreate(
|
||||
version=next_version,
|
||||
content=version_content,
|
||||
content_type=AgentAssetContentType.MARKDOWN,
|
||||
change_note=summary,
|
||||
created_by=actor,
|
||||
),
|
||||
actor=actor,
|
||||
request_id=request_id,
|
||||
)
|
||||
self.audit_service.log_action(
|
||||
actor=actor,
|
||||
action="edit_rule_spreadsheet",
|
||||
@@ -660,13 +619,11 @@ class AgentAssetService:
|
||||
before_json={"storage_key": current_metadata.storage_key},
|
||||
after_json={
|
||||
"summary": summary,
|
||||
"version": next_version,
|
||||
"changed_sheet_count": changed_sheet_count,
|
||||
"changed_cell_count": changed_cell_count,
|
||||
"sheet_changes": [item.model_dump() for item in sheet_changes],
|
||||
"cell_changes": [item.model_dump() for item in cell_changes[:500]],
|
||||
"storage_key": metadata.storage_key,
|
||||
"snapshot_storage_key": snapshot_metadata.storage_key,
|
||||
},
|
||||
request_id=request_id,
|
||||
)
|
||||
@@ -705,7 +662,7 @@ class AgentAssetService:
|
||||
self,
|
||||
asset_id: str,
|
||||
*,
|
||||
version: str,
|
||||
version: str | None = None,
|
||||
payload: dict[str, Any],
|
||||
actor_name: str | None = None,
|
||||
) -> None:
|
||||
@@ -721,8 +678,6 @@ class AgentAssetService:
|
||||
callback = self._parse_onlyoffice_callback(payload)
|
||||
if callback.status not in {2, 6} or not callback.download_url:
|
||||
return
|
||||
if str(version or "").strip() not in {"", "current", self._resolve_working_version(asset)}:
|
||||
return
|
||||
|
||||
_, current_metadata = self._resolve_current_spreadsheet_meta(asset)
|
||||
request = Request(
|
||||
@@ -924,44 +879,6 @@ class AgentAssetService:
|
||||
|
||||
return sorted(events, key=lambda item: item.event_time)
|
||||
|
||||
def compare_spreadsheet_versions(
|
||||
self,
|
||||
asset_id: str,
|
||||
*,
|
||||
base_version: str,
|
||||
target_version: str,
|
||||
) -> AgentAssetVersionCompareRead:
|
||||
self._ensure_ready()
|
||||
asset = self._require_spreadsheet_rule(asset_id)
|
||||
resolved_base, base_meta = self._resolve_spreadsheet_version_meta(
|
||||
asset,
|
||||
version=base_version,
|
||||
)
|
||||
resolved_target, target_meta = self._resolve_spreadsheet_version_meta(
|
||||
asset,
|
||||
version=target_version,
|
||||
)
|
||||
|
||||
base_workbook = self._load_spreadsheet_for_compare(base_meta)
|
||||
target_workbook = self._load_spreadsheet_for_compare(target_meta)
|
||||
sheet_changes, cell_changes = self._collect_workbook_changes(
|
||||
base_workbook,
|
||||
target_workbook,
|
||||
)
|
||||
added_sheet_count = sum(1 for item in sheet_changes if item.change_type == "added")
|
||||
removed_sheet_count = sum(1 for item in sheet_changes if item.change_type == "removed")
|
||||
|
||||
return AgentAssetVersionCompareRead(
|
||||
base_version=resolved_base,
|
||||
target_version=resolved_target,
|
||||
added_sheet_count=added_sheet_count,
|
||||
removed_sheet_count=removed_sheet_count,
|
||||
changed_sheet_count=self._count_changed_sheets(sheet_changes, cell_changes),
|
||||
changed_cell_count=len(cell_changes),
|
||||
sheet_changes=sheet_changes,
|
||||
cell_changes=cell_changes[:500],
|
||||
)
|
||||
|
||||
def list_spreadsheet_change_records(
|
||||
self,
|
||||
asset_id: str,
|
||||
@@ -981,8 +898,7 @@ class AgentAssetService:
|
||||
id=log.id,
|
||||
actor=log.actor,
|
||||
changed_at=log.created_at,
|
||||
version=str((log.after_json or {}).get("version") or "").strip() or None,
|
||||
summary=str((log.after_json or {}).get("summary") or "ONLYOFFICE 在线编辑保存。"),
|
||||
summary=str((log.after_json or {}).get("summary") or "表格内容已保存。"),
|
||||
sheet_changes=[
|
||||
AgentAssetSpreadsheetDiffSheetRead.model_validate(item)
|
||||
for item in ((log.after_json or {}).get("sheet_changes") or [])
|
||||
@@ -1292,7 +1208,6 @@ class AgentAssetService:
|
||||
*,
|
||||
asset_id: str,
|
||||
current_user: CurrentUserContext,
|
||||
resolved_version: str,
|
||||
metadata: RuleSpreadsheetMeta,
|
||||
editable: bool,
|
||||
) -> AgentAssetOnlyOfficeConfigRead:
|
||||
@@ -1307,21 +1222,21 @@ class AgentAssetService:
|
||||
|
||||
backend_base_url = onlyoffice_settings.backend_url.rstrip("/")
|
||||
public_url = onlyoffice_settings.public_url.rstrip("/")
|
||||
access_token = self._build_onlyoffice_access_token(asset_id, resolved_version)
|
||||
access_token = self._build_onlyoffice_access_token(asset_id)
|
||||
document_url = (
|
||||
f"{backend_base_url}{settings.api_v1_prefix}/agent-assets/{asset_id}/spreadsheet/onlyoffice/content"
|
||||
f"?version={resolved_version}&access_token={access_token}"
|
||||
f"?access_token={access_token}"
|
||||
)
|
||||
callback_url = (
|
||||
f"{backend_base_url}{settings.api_v1_prefix}/agent-assets/{asset_id}/spreadsheet/onlyoffice/callback"
|
||||
f"?version={resolved_version}&actor_name={quote(current_user.name)}"
|
||||
f"?actor_name={quote(current_user.name)}"
|
||||
)
|
||||
|
||||
config: dict[str, Any] = {
|
||||
"documentType": "cell",
|
||||
"document": {
|
||||
"fileType": Path(metadata.file_name).suffix.lstrip(".").lower() or "xlsx",
|
||||
"key": self._build_onlyoffice_document_key(asset_id, resolved_version, metadata),
|
||||
"key": self._build_onlyoffice_document_key(asset_id, metadata),
|
||||
"title": metadata.file_name,
|
||||
"url": document_url,
|
||||
"permissions": {
|
||||
@@ -1462,19 +1377,6 @@ class AgentAssetService:
|
||||
major, minor, patch = [int(item) for item in parts]
|
||||
return f"v{major}.{minor}.{patch + 1}"
|
||||
|
||||
@staticmethod
|
||||
def _can_edit_spreadsheet_version(
|
||||
asset: AgentAsset,
|
||||
current_user: CurrentUserContext,
|
||||
version: str,
|
||||
) -> bool:
|
||||
role_codes = {str(item).strip() for item in current_user.role_codes}
|
||||
can_edit = current_user.is_admin or "manager" in role_codes or "finance" in role_codes
|
||||
return (
|
||||
can_edit
|
||||
and AgentAssetService._resolve_working_version(asset) == str(version or "").strip()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _can_edit_current_spreadsheet(current_user: CurrentUserContext) -> bool:
|
||||
role_codes = {str(item).strip() for item in current_user.role_codes}
|
||||
@@ -1483,23 +1385,21 @@ class AgentAssetService:
|
||||
@staticmethod
|
||||
def _build_onlyoffice_document_key(
|
||||
asset_id: str,
|
||||
version: str,
|
||||
metadata: RuleSpreadsheetMeta,
|
||||
) -> str:
|
||||
fingerprint = metadata.checksum or metadata.updated_at or metadata.file_name
|
||||
raw_key = f"{asset_id}-{version}-{fingerprint}"
|
||||
raw_key = f"{asset_id}-{fingerprint}"
|
||||
return "".join(
|
||||
character if character.isalnum() or character in {"-", "_", ".", "="} else "_"
|
||||
for character in raw_key
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _build_onlyoffice_access_token(asset_id: str, version: str) -> str:
|
||||
def _build_onlyoffice_access_token(asset_id: str) -> str:
|
||||
onlyoffice_settings = resolve_onlyoffice_settings()
|
||||
payload = {
|
||||
"scope": "agent-asset-spreadsheet",
|
||||
"asset_id": asset_id,
|
||||
"version": version,
|
||||
}
|
||||
return jwt.encode(payload, onlyoffice_settings.jwt_secret, algorithm="HS256")
|
||||
|
||||
@@ -1646,7 +1546,6 @@ class AgentAssetService:
|
||||
|
||||
@staticmethod
|
||||
def _build_spreadsheet_change_summary(
|
||||
operation_label: str,
|
||||
sheet_changes: list[AgentAssetSpreadsheetDiffSheetRead],
|
||||
cell_changes: list[AgentAssetSpreadsheetDiffCellRead],
|
||||
) -> str:
|
||||
@@ -1655,15 +1554,15 @@ class AgentAssetService:
|
||||
| {item.sheet_name for item in cell_changes}
|
||||
)
|
||||
if not sheet_names:
|
||||
return f"{operation_label}:文件内容已保存,未发现单元格级差异。"
|
||||
return "文件内容已保存,未发现单元格级差异。"
|
||||
|
||||
preview = "、".join(sheet_names[:3])
|
||||
if len(sheet_names) > 3:
|
||||
preview = f"{preview} 等"
|
||||
sheet_text = f"涉及 {len(sheet_names)} 个工作表({preview})"
|
||||
if cell_changes:
|
||||
return f"{operation_label}:{sheet_text},共 {len(cell_changes)} 处单元格改动。"
|
||||
return f"{operation_label}:{sheet_text},工作表结构发生变化。"
|
||||
return f"{sheet_text},共 {len(cell_changes)} 处单元格改动。"
|
||||
return f"{sheet_text},工作表结构发生变化。"
|
||||
|
||||
def _next_available_version(self, asset: AgentAsset) -> str:
|
||||
candidate = self._increment_version(self._resolve_working_version(asset))
|
||||
|
||||
@@ -1,70 +1,70 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.logging import get_logger
|
||||
from app.models.audit_log import AuditLog
|
||||
from app.repositories.audit_log import AuditLogRepository
|
||||
from app.schemas.audit_log import AuditLogRead
|
||||
from app.services.agent_foundation import AgentFoundationService
|
||||
|
||||
logger = get_logger("app.services.audit")
|
||||
|
||||
|
||||
class AuditLogService:
|
||||
def __init__(self, db: Session) -> None:
|
||||
self.db = db
|
||||
self.repository = AuditLogRepository(db)
|
||||
|
||||
def list_logs(
|
||||
self,
|
||||
*,
|
||||
resource_type: str | None = None,
|
||||
resource_id: str | None = None,
|
||||
action: str | None = None,
|
||||
limit: int = 50,
|
||||
) -> list[AuditLogRead]:
|
||||
self._ensure_ready()
|
||||
items = self.repository.list(
|
||||
resource_type=resource_type,
|
||||
resource_id=resource_id,
|
||||
action=action,
|
||||
limit=limit,
|
||||
)
|
||||
return [AuditLogRead.model_validate(item) for item in items]
|
||||
|
||||
def log_action(
|
||||
self,
|
||||
*,
|
||||
actor: str,
|
||||
action: str,
|
||||
resource_type: str,
|
||||
resource_id: str,
|
||||
before_json: dict[str, Any] | None = None,
|
||||
after_json: dict[str, Any] | None = None,
|
||||
request_id: str | None = None,
|
||||
) -> AuditLog:
|
||||
log = AuditLog(
|
||||
actor=actor,
|
||||
action=action,
|
||||
resource_type=resource_type,
|
||||
resource_id=resource_id,
|
||||
before_json=before_json,
|
||||
after_json=after_json,
|
||||
request_id=request_id or uuid.uuid4().hex,
|
||||
)
|
||||
created = self.repository.create(log)
|
||||
logger.info(
|
||||
"Created audit log id=%s action=%s resource=%s:%s",
|
||||
created.id,
|
||||
created.action,
|
||||
created.resource_type,
|
||||
created.resource_id,
|
||||
)
|
||||
return created
|
||||
|
||||
def _ensure_ready(self) -> None:
|
||||
AgentFoundationService(self.db).ensure_foundation_ready()
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.logging import get_logger
|
||||
from app.models.audit_log import AuditLog
|
||||
from app.repositories.audit_log import AuditLogRepository
|
||||
from app.schemas.audit_log import AuditLogRead
|
||||
from app.services.agent_foundation import AgentFoundationService
|
||||
|
||||
logger = get_logger("app.services.audit")
|
||||
|
||||
|
||||
class AuditLogService:
|
||||
def __init__(self, db: Session) -> None:
|
||||
self.db = db
|
||||
self.repository = AuditLogRepository(db)
|
||||
|
||||
def list_logs(
|
||||
self,
|
||||
*,
|
||||
resource_type: str | None = None,
|
||||
resource_id: str | None = None,
|
||||
action: str | None = None,
|
||||
limit: int = 50,
|
||||
) -> list[AuditLogRead]:
|
||||
self._ensure_ready()
|
||||
items = self.repository.list(
|
||||
resource_type=resource_type,
|
||||
resource_id=resource_id,
|
||||
action=action,
|
||||
limit=limit,
|
||||
)
|
||||
return [AuditLogRead.model_validate(item) for item in items]
|
||||
|
||||
def log_action(
|
||||
self,
|
||||
*,
|
||||
actor: str,
|
||||
action: str,
|
||||
resource_type: str,
|
||||
resource_id: str,
|
||||
before_json: dict[str, Any] | None = None,
|
||||
after_json: dict[str, Any] | None = None,
|
||||
request_id: str | None = None,
|
||||
) -> AuditLog:
|
||||
log = AuditLog(
|
||||
actor=actor,
|
||||
action=action,
|
||||
resource_type=resource_type,
|
||||
resource_id=resource_id,
|
||||
before_json=before_json,
|
||||
after_json=after_json,
|
||||
request_id=request_id or uuid.uuid4().hex,
|
||||
)
|
||||
created = self.repository.create(log)
|
||||
logger.info(
|
||||
"Created audit log id=%s action=%s resource=%s:%s",
|
||||
created.id,
|
||||
created.action,
|
||||
created.resource_type,
|
||||
created.resource_id,
|
||||
)
|
||||
return created
|
||||
|
||||
def _ensure_ready(self) -> None:
|
||||
AgentFoundationService(self.db).ensure_foundation_ready()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -177,16 +177,16 @@ SLOT_LABELS = {
|
||||
}
|
||||
|
||||
DATE_TEXT_PATTERN = re.compile(r"(\d{4}[年/-]\d{1,2}[月/-]\d{1,2}日?)")
|
||||
AMOUNT_TEXT_PATTERN = re.compile(
|
||||
r"(\d+(?:\.\d+)?)\s*(?:万元|万员|万圆|万园|万块|万元整|元整|块钱|块|元|员|圆|园|万)"
|
||||
)
|
||||
AMOUNT_TEXT_PATTERN = re.compile(
|
||||
r"(\d+(?:\.\d+)?)\s*(?:万元|万员|万圆|万园|万块|万元整|元整|块钱|块|元|员|圆|园|万)"
|
||||
)
|
||||
DOCUMENT_AMOUNT_PATTERN = re.compile(
|
||||
r"(?:价税合计|合计金额|费用合计|订单(?:总)?金额|支付(?:金额)?|实付(?:金额)?|实收(?:金额)?|总(?:额|计|价)|票价|金额|车费|消费金额)"
|
||||
r"[::\s¥¥人民币]*([0-9]+(?:[.,][0-9]{1,2})?)"
|
||||
)
|
||||
DOCUMENT_CURRENCY_AMOUNT_PATTERN = re.compile(r"[¥¥]\s*([0-9]+(?:[.,][0-9]{1,2})?)")
|
||||
|
||||
SOURCE_LABELS = {
|
||||
SOURCE_LABELS = {
|
||||
"user_text": "用户描述",
|
||||
"user_form": "用户修改",
|
||||
"ocr": "票据识别",
|
||||
@@ -215,7 +215,7 @@ INFERRED_REASON_LABELS = {
|
||||
"welfare": "员工福利",
|
||||
"other": "其他费用",
|
||||
}
|
||||
SYSTEM_GENERATED_REASON_PREFIXES = (
|
||||
SYSTEM_GENERATED_REASON_PREFIXES = (
|
||||
"我上传了",
|
||||
"请按当前已识别信息",
|
||||
"请把当前上传的票据",
|
||||
@@ -225,20 +225,20 @@ SYSTEM_GENERATED_REASON_PREFIXES = (
|
||||
"我已修改识别信息",
|
||||
"查看报销草稿",
|
||||
"请解释一下当前这笔报销的合规风险和待补充项",
|
||||
)
|
||||
AMOUNT_UNIT_ALIASES = {
|
||||
"员": "元",
|
||||
"圆": "元",
|
||||
"园": "元",
|
||||
"块": "元",
|
||||
"块钱": "元",
|
||||
"元整": "元",
|
||||
"万员": "万元",
|
||||
"万圆": "万元",
|
||||
"万园": "万元",
|
||||
"万块": "万元",
|
||||
"万元整": "万元",
|
||||
}
|
||||
)
|
||||
AMOUNT_UNIT_ALIASES = {
|
||||
"员": "元",
|
||||
"圆": "元",
|
||||
"园": "元",
|
||||
"块": "元",
|
||||
"块钱": "元",
|
||||
"元整": "元",
|
||||
"万员": "万元",
|
||||
"万圆": "万元",
|
||||
"万园": "万元",
|
||||
"万块": "万元",
|
||||
"万元整": "万元",
|
||||
}
|
||||
|
||||
|
||||
class UserAgentService:
|
||||
@@ -1742,7 +1742,7 @@ class UserAgentService:
|
||||
if is_submitted:
|
||||
body = (
|
||||
f"主题:{subject}\n"
|
||||
f"结论:报销单已提交,当前节点为 {approval_stage or '审批中'}。\n"
|
||||
f"结论:报销单已提交,当前节点为 {approval_stage or '审批中'}。\n"
|
||||
"建议:后续可在个人报销列表中跟踪审批进度,必要时再补充说明或附件。\n"
|
||||
f"原始问题:{payload.message}"
|
||||
)
|
||||
@@ -2381,7 +2381,7 @@ class UserAgentService:
|
||||
if review_action == "next_step":
|
||||
if draft_payload is not None and draft_payload.status == "submitted":
|
||||
stage_text = draft_payload.approval_stage or "审批中"
|
||||
return f"报销单 {draft_payload.claim_no or ''} 已提交,当前节点为 {stage_text}。".strip()
|
||||
return f"报销单 {draft_payload.claim_no or ''} 已提交,当前节点为 {stage_text}。".strip()
|
||||
if payload.tool_payload.get("submission_blocked"):
|
||||
return str(payload.tool_payload.get("message") or "").strip() or "当前报销单暂时还不能提交审批。"
|
||||
return (
|
||||
@@ -2947,19 +2947,19 @@ class UserAgentService:
|
||||
"expense_type_code": "",
|
||||
}
|
||||
participants: list[str] = []
|
||||
for item in payload.ontology.entities:
|
||||
if item.type == "employee" and not values["employee_name"]:
|
||||
values["employee_name"] = item.value
|
||||
elif item.type == "customer" and not values["customer"]:
|
||||
values["customer"] = item.value
|
||||
elif item.type == "amount" and item.role != "threshold" and not values["amount"]:
|
||||
normalized_amount = str(item.normalized_value or "").strip()
|
||||
values["amount"] = f"{normalized_amount}元" if normalized_amount else item.value
|
||||
elif item.type == "expense_type" and not values["expense_type_code"]:
|
||||
values["expense_type_code"] = item.normalized_value
|
||||
values["expense_type"] = EXPENSE_TYPE_LABELS.get(
|
||||
item.normalized_value,
|
||||
item.value,
|
||||
for item in payload.ontology.entities:
|
||||
if item.type == "employee" and not values["employee_name"]:
|
||||
values["employee_name"] = item.value
|
||||
elif item.type == "customer" and not values["customer"]:
|
||||
values["customer"] = item.value
|
||||
elif item.type == "amount" and item.role != "threshold" and not values["amount"]:
|
||||
normalized_amount = str(item.normalized_value or "").strip()
|
||||
values["amount"] = f"{normalized_amount}元" if normalized_amount else item.value
|
||||
elif item.type == "expense_type" and not values["expense_type_code"]:
|
||||
values["expense_type_code"] = item.normalized_value
|
||||
values["expense_type"] = EXPENSE_TYPE_LABELS.get(
|
||||
item.normalized_value,
|
||||
item.value,
|
||||
)
|
||||
elif item.type in {"participant", "person"} and item.value.strip():
|
||||
participants.append(item.value.strip())
|
||||
@@ -3189,7 +3189,24 @@ class UserAgentService:
|
||||
evidence="来源于用户修改后的结构化表单。",
|
||||
)
|
||||
|
||||
inferred_reason = self._infer_reason_from_claim_groups(
|
||||
claim_groups=claim_groups,
|
||||
)
|
||||
reason_value = self._resolve_reason_text(self._resolve_reason_source_text(payload))
|
||||
if inferred_reason:
|
||||
return self._build_slot_value(
|
||||
value=inferred_reason,
|
||||
raw_value=reason_value or inferred_reason,
|
||||
normalized_value=inferred_reason,
|
||||
source="ocr",
|
||||
confidence=0.82,
|
||||
evidence=(
|
||||
"系统已根据票据识别结果预置场景类型;原始描述仍保留为补充说明。"
|
||||
if reason_value
|
||||
else "系统已根据票据识别场景补全通用事由,若需更具体说明可继续修改。"
|
||||
),
|
||||
)
|
||||
|
||||
if reason_value:
|
||||
return self._build_slot_value(
|
||||
value=reason_value,
|
||||
@@ -3199,19 +3216,6 @@ class UserAgentService:
|
||||
confidence=0.76,
|
||||
evidence="系统从用户原始描述中提取了本次费用事由,建议继续核对。",
|
||||
)
|
||||
|
||||
inferred_reason = self._infer_reason_from_claim_groups(
|
||||
claim_groups=claim_groups,
|
||||
)
|
||||
if inferred_reason:
|
||||
return self._build_slot_value(
|
||||
value=inferred_reason,
|
||||
raw_value=inferred_reason,
|
||||
normalized_value=inferred_reason,
|
||||
source="ocr",
|
||||
confidence=0.68,
|
||||
evidence="系统已根据票据识别场景补全通用事由,若需更具体说明可继续修改。",
|
||||
)
|
||||
return self._build_slot_value()
|
||||
|
||||
def _build_amount_slot(
|
||||
@@ -3358,17 +3362,17 @@ class UserAgentService:
|
||||
return self._build_slot_value()
|
||||
|
||||
@staticmethod
|
||||
def _normalize_amount_text(value: str) -> str:
|
||||
cleaned = str(value or "").strip()
|
||||
if not cleaned:
|
||||
return ""
|
||||
for alias, canonical in sorted(AMOUNT_UNIT_ALIASES.items(), key=lambda item: len(item[0]), reverse=True):
|
||||
cleaned = cleaned.replace(alias, canonical)
|
||||
match = AMOUNT_TEXT_PATTERN.search(cleaned)
|
||||
if not match:
|
||||
return cleaned
|
||||
number = float(match.group(1))
|
||||
return f"{number:.2f}元"
|
||||
def _normalize_amount_text(value: str) -> str:
|
||||
cleaned = str(value or "").strip()
|
||||
if not cleaned:
|
||||
return ""
|
||||
for alias, canonical in sorted(AMOUNT_UNIT_ALIASES.items(), key=lambda item: len(item[0]), reverse=True):
|
||||
cleaned = cleaned.replace(alias, canonical)
|
||||
match = AMOUNT_TEXT_PATTERN.search(cleaned)
|
||||
if not match:
|
||||
return cleaned
|
||||
number = float(match.group(1))
|
||||
return f"{number:.2f}元"
|
||||
|
||||
@staticmethod
|
||||
def _normalize_expense_type_input(value: str) -> tuple[str, str]:
|
||||
|
||||
@@ -1,139 +1,139 @@
|
||||
README.md
|
||||
pyproject.toml
|
||||
src/app/__init__.py
|
||||
src/app/main.py
|
||||
src/app/api/__init__.py
|
||||
src/app/api/deps.py
|
||||
src/app/api/router.py
|
||||
src/app/api/v1/__init__.py
|
||||
src/app/api/v1/router.py
|
||||
src/app/api/v1/endpoints/__init__.py
|
||||
src/app/api/v1/endpoints/agent_assets.py
|
||||
src/app/api/v1/endpoints/agent_runs.py
|
||||
src/app/api/v1/endpoints/audit_logs.py
|
||||
src/app/api/v1/endpoints/auth.py
|
||||
src/app/api/v1/endpoints/bootstrap.py
|
||||
src/app/api/v1/endpoints/employees.py
|
||||
src/app/api/v1/endpoints/health.py
|
||||
src/app/api/v1/endpoints/knowledge.py
|
||||
src/app/api/v1/endpoints/ocr.py
|
||||
src/app/api/v1/endpoints/ontology.py
|
||||
src/app/api/v1/endpoints/orchestrator.py
|
||||
src/app/api/v1/endpoints/reimbursements.py
|
||||
src/app/api/v1/endpoints/settings.py
|
||||
src/app/api/v1/endpoints/system_logs.py
|
||||
src/app/core/__init__.py
|
||||
src/app/core/admin_secret.py
|
||||
src/app/core/agent_enums.py
|
||||
src/app/core/bootstrap.py
|
||||
src/app/core/config.py
|
||||
src/app/core/logging.py
|
||||
src/app/core/openapi.py
|
||||
src/app/core/secret_box.py
|
||||
src/app/core/security.py
|
||||
src/app/db/__init__.py
|
||||
src/app/db/base.py
|
||||
src/app/db/base_class.py
|
||||
src/app/db/session.py
|
||||
src/app/middleware/__init__.py
|
||||
src/app/middleware/logging.py
|
||||
src/app/models/__init__.py
|
||||
src/app/models/agent_asset.py
|
||||
src/app/models/agent_conversation.py
|
||||
src/app/models/agent_run.py
|
||||
src/app/models/approval.py
|
||||
src/app/models/audit_log.py
|
||||
src/app/models/employee.py
|
||||
src/app/models/employee_change_log.py
|
||||
src/app/models/financial_record.py
|
||||
src/app/models/organization.py
|
||||
src/app/models/reimbursement.py
|
||||
src/app/models/role.py
|
||||
src/app/models/system_model_setting.py
|
||||
src/app/models/system_setting.py
|
||||
src/app/models/system_setting_secret.py
|
||||
src/app/repositories/__init__.py
|
||||
src/app/repositories/agent_asset.py
|
||||
src/app/repositories/agent_run.py
|
||||
src/app/repositories/audit_log.py
|
||||
src/app/repositories/employee.py
|
||||
src/app/repositories/reimbursement.py
|
||||
src/app/repositories/settings.py
|
||||
src/app/schemas/__init__.py
|
||||
src/app/schemas/agent_asset.py
|
||||
src/app/schemas/agent_run.py
|
||||
src/app/schemas/audit_log.py
|
||||
src/app/schemas/auth.py
|
||||
src/app/schemas/bootstrap.py
|
||||
src/app/schemas/common.py
|
||||
src/app/schemas/employee.py
|
||||
src/app/schemas/knowledge.py
|
||||
src/app/schemas/ocr.py
|
||||
src/app/schemas/ontology.py
|
||||
src/app/schemas/orchestrator.py
|
||||
src/app/schemas/reimbursement.py
|
||||
src/app/schemas/settings.py
|
||||
src/app/schemas/system_log.py
|
||||
src/app/schemas/user_agent.py
|
||||
src/app/services/__init__.py
|
||||
src/app/services/agent_asset_spreadsheet.py
|
||||
src/app/services/agent_assets.py
|
||||
src/app/services/agent_conversations.py
|
||||
src/app/services/agent_foundation.py
|
||||
src/app/services/agent_runs.py
|
||||
src/app/services/audit.py
|
||||
src/app/services/auth.py
|
||||
src/app/services/document_intelligence.py
|
||||
src/app/services/employee.py
|
||||
src/app/services/employee_seed.py
|
||||
src/app/services/expense_claims.py
|
||||
src/app/services/expense_rule_runtime.py
|
||||
src/app/services/hermes_sync.py
|
||||
src/app/services/knowledge.py
|
||||
src/app/services/knowledge_index_tasks.py
|
||||
src/app/services/knowledge_normalizer.py
|
||||
src/app/services/knowledge_rag.py
|
||||
src/app/services/knowledge_scheduler.py
|
||||
src/app/services/knowledge_sync.py
|
||||
src/app/services/model_connectivity.py
|
||||
src/app/services/ocr.py
|
||||
src/app/services/ontology.py
|
||||
src/app/services/orchestrator.py
|
||||
src/app/services/reimbursement.py
|
||||
src/app/services/runtime_chat.py
|
||||
src/app/services/settings.py
|
||||
src/app/services/system_hermes.py
|
||||
src/app/services/system_logs.py
|
||||
src/app/services/user_agent.py
|
||||
src/x_financial_server.egg-info/PKG-INFO
|
||||
src/x_financial_server.egg-info/SOURCES.txt
|
||||
src/x_financial_server.egg-info/dependency_links.txt
|
||||
src/x_financial_server.egg-info/requires.txt
|
||||
src/x_financial_server.egg-info/top_level.txt
|
||||
tests/test_agent_asset_onlyoffice_key.py
|
||||
tests/test_agent_asset_service.py
|
||||
tests/test_agent_asset_spreadsheet_import.py
|
||||
tests/test_agent_foundation_endpoints.py
|
||||
tests/test_agent_runs_service.py
|
||||
tests/test_auth_service.py
|
||||
tests/test_config_settings_reload.py
|
||||
tests/test_document_intelligence.py
|
||||
tests/test_employee_service.py
|
||||
tests/test_env_file_precedence.py
|
||||
tests/test_expense_claim_service.py
|
||||
tests/test_imports.py
|
||||
tests/test_knowledge_normalizer.py
|
||||
tests/test_knowledge_onlyoffice_config.py
|
||||
tests/test_knowledge_rag_service.py
|
||||
tests/test_knowledge_service.py
|
||||
tests/test_ocr_endpoints.py
|
||||
tests/test_ocr_service.py
|
||||
tests/test_ontology_service.py
|
||||
tests/test_openapi_schema.py
|
||||
tests/test_reimbursement_endpoints.py
|
||||
tests/test_runtime_chat_service.py
|
||||
tests/test_server_start_dependencies.py
|
||||
tests/test_settings_persistence.py
|
||||
tests/test_settings_service.py
|
||||
tests/test_system_logs_service.py
|
||||
README.md
|
||||
pyproject.toml
|
||||
src/app/__init__.py
|
||||
src/app/main.py
|
||||
src/app/api/__init__.py
|
||||
src/app/api/deps.py
|
||||
src/app/api/router.py
|
||||
src/app/api/v1/__init__.py
|
||||
src/app/api/v1/router.py
|
||||
src/app/api/v1/endpoints/__init__.py
|
||||
src/app/api/v1/endpoints/agent_assets.py
|
||||
src/app/api/v1/endpoints/agent_runs.py
|
||||
src/app/api/v1/endpoints/audit_logs.py
|
||||
src/app/api/v1/endpoints/auth.py
|
||||
src/app/api/v1/endpoints/bootstrap.py
|
||||
src/app/api/v1/endpoints/employees.py
|
||||
src/app/api/v1/endpoints/health.py
|
||||
src/app/api/v1/endpoints/knowledge.py
|
||||
src/app/api/v1/endpoints/ocr.py
|
||||
src/app/api/v1/endpoints/ontology.py
|
||||
src/app/api/v1/endpoints/orchestrator.py
|
||||
src/app/api/v1/endpoints/reimbursements.py
|
||||
src/app/api/v1/endpoints/settings.py
|
||||
src/app/api/v1/endpoints/system_logs.py
|
||||
src/app/core/__init__.py
|
||||
src/app/core/admin_secret.py
|
||||
src/app/core/agent_enums.py
|
||||
src/app/core/bootstrap.py
|
||||
src/app/core/config.py
|
||||
src/app/core/logging.py
|
||||
src/app/core/openapi.py
|
||||
src/app/core/secret_box.py
|
||||
src/app/core/security.py
|
||||
src/app/db/__init__.py
|
||||
src/app/db/base.py
|
||||
src/app/db/base_class.py
|
||||
src/app/db/session.py
|
||||
src/app/middleware/__init__.py
|
||||
src/app/middleware/logging.py
|
||||
src/app/models/__init__.py
|
||||
src/app/models/agent_asset.py
|
||||
src/app/models/agent_conversation.py
|
||||
src/app/models/agent_run.py
|
||||
src/app/models/approval.py
|
||||
src/app/models/audit_log.py
|
||||
src/app/models/employee.py
|
||||
src/app/models/employee_change_log.py
|
||||
src/app/models/financial_record.py
|
||||
src/app/models/organization.py
|
||||
src/app/models/reimbursement.py
|
||||
src/app/models/role.py
|
||||
src/app/models/system_model_setting.py
|
||||
src/app/models/system_setting.py
|
||||
src/app/models/system_setting_secret.py
|
||||
src/app/repositories/__init__.py
|
||||
src/app/repositories/agent_asset.py
|
||||
src/app/repositories/agent_run.py
|
||||
src/app/repositories/audit_log.py
|
||||
src/app/repositories/employee.py
|
||||
src/app/repositories/reimbursement.py
|
||||
src/app/repositories/settings.py
|
||||
src/app/schemas/__init__.py
|
||||
src/app/schemas/agent_asset.py
|
||||
src/app/schemas/agent_run.py
|
||||
src/app/schemas/audit_log.py
|
||||
src/app/schemas/auth.py
|
||||
src/app/schemas/bootstrap.py
|
||||
src/app/schemas/common.py
|
||||
src/app/schemas/employee.py
|
||||
src/app/schemas/knowledge.py
|
||||
src/app/schemas/ocr.py
|
||||
src/app/schemas/ontology.py
|
||||
src/app/schemas/orchestrator.py
|
||||
src/app/schemas/reimbursement.py
|
||||
src/app/schemas/settings.py
|
||||
src/app/schemas/system_log.py
|
||||
src/app/schemas/user_agent.py
|
||||
src/app/services/__init__.py
|
||||
src/app/services/agent_asset_spreadsheet.py
|
||||
src/app/services/agent_assets.py
|
||||
src/app/services/agent_conversations.py
|
||||
src/app/services/agent_foundation.py
|
||||
src/app/services/agent_runs.py
|
||||
src/app/services/audit.py
|
||||
src/app/services/auth.py
|
||||
src/app/services/document_intelligence.py
|
||||
src/app/services/employee.py
|
||||
src/app/services/employee_seed.py
|
||||
src/app/services/expense_claims.py
|
||||
src/app/services/expense_rule_runtime.py
|
||||
src/app/services/hermes_sync.py
|
||||
src/app/services/knowledge.py
|
||||
src/app/services/knowledge_index_tasks.py
|
||||
src/app/services/knowledge_normalizer.py
|
||||
src/app/services/knowledge_rag.py
|
||||
src/app/services/knowledge_scheduler.py
|
||||
src/app/services/knowledge_sync.py
|
||||
src/app/services/model_connectivity.py
|
||||
src/app/services/ocr.py
|
||||
src/app/services/ontology.py
|
||||
src/app/services/orchestrator.py
|
||||
src/app/services/reimbursement.py
|
||||
src/app/services/runtime_chat.py
|
||||
src/app/services/settings.py
|
||||
src/app/services/system_hermes.py
|
||||
src/app/services/system_logs.py
|
||||
src/app/services/user_agent.py
|
||||
src/x_financial_server.egg-info/PKG-INFO
|
||||
src/x_financial_server.egg-info/SOURCES.txt
|
||||
src/x_financial_server.egg-info/dependency_links.txt
|
||||
src/x_financial_server.egg-info/requires.txt
|
||||
src/x_financial_server.egg-info/top_level.txt
|
||||
tests/test_agent_asset_onlyoffice_key.py
|
||||
tests/test_agent_asset_service.py
|
||||
tests/test_agent_asset_spreadsheet_import.py
|
||||
tests/test_agent_foundation_endpoints.py
|
||||
tests/test_agent_runs_service.py
|
||||
tests/test_auth_service.py
|
||||
tests/test_config_settings_reload.py
|
||||
tests/test_document_intelligence.py
|
||||
tests/test_employee_service.py
|
||||
tests/test_env_file_precedence.py
|
||||
tests/test_expense_claim_service.py
|
||||
tests/test_imports.py
|
||||
tests/test_knowledge_normalizer.py
|
||||
tests/test_knowledge_onlyoffice_config.py
|
||||
tests/test_knowledge_rag_service.py
|
||||
tests/test_knowledge_service.py
|
||||
tests/test_ocr_endpoints.py
|
||||
tests/test_ocr_service.py
|
||||
tests/test_ontology_service.py
|
||||
tests/test_openapi_schema.py
|
||||
tests/test_reimbursement_endpoints.py
|
||||
tests/test_runtime_chat_service.py
|
||||
tests/test_server_start_dependencies.py
|
||||
tests/test_settings_persistence.py
|
||||
tests/test_settings_service.py
|
||||
tests/test_system_logs_service.py
|
||||
tests/test_user_agent_service.py
|
||||
@@ -1,84 +1,84 @@
|
||||
{
|
||||
"file_name": "行程单_2_鄂AX9877.pdf",
|
||||
"storage_key": "0d3102fe-a458-42cf-b30c-4cffeeb74668/c99de539-23cb-4f1a-a21f-a40bce93d54e/行程单_2_鄂AX9877.pdf",
|
||||
"media_type": "application/pdf",
|
||||
"size_bytes": 32459,
|
||||
"uploaded_at": "2026-05-16T08:41:42.540134+00:00",
|
||||
"previewable": true,
|
||||
"preview_kind": "image",
|
||||
"preview_storage_key": "0d3102fe-a458-42cf-b30c-4cffeeb74668/c99de539-23cb-4f1a-a21f-a40bce93d54e/行程单_2_鄂AX9877.preview.png",
|
||||
"preview_media_type": "image/png",
|
||||
"preview_file_name": "行程单_2_鄂AX9877.preview.png",
|
||||
"analysis": {
|
||||
"severity": "pass",
|
||||
"label": "AI提示符合条件",
|
||||
"headline": "AI提示:附件符合基础校验条件",
|
||||
"summary": "已识别到票据类型和关键字段,且符合当前费用场景的附件要求。",
|
||||
"points": [
|
||||
"票据类型:已识别为出租车/网约车票据。",
|
||||
"附件类型要求:当前费用项目为交通费,已识别为出租车/网约车票据,符合当前交通费场景的附件要求。",
|
||||
"金额字段:已识别到与当前明细接近的金额 35.53 元。"
|
||||
],
|
||||
"suggestion": "建议继续核对报销分类、费用说明和业务场景是否一致。"
|
||||
},
|
||||
"document_info": {
|
||||
"document_type": "taxi_receipt",
|
||||
"document_type_label": "出租车/网约车票据",
|
||||
"scene_code": "transport",
|
||||
"scene_label": "交通票据",
|
||||
"fields": [
|
||||
{
|
||||
"key": "amount",
|
||||
"label": "金额",
|
||||
"value": "35.53元"
|
||||
},
|
||||
{
|
||||
"key": "date",
|
||||
"label": "日期",
|
||||
"value": "2026-03-04"
|
||||
},
|
||||
{
|
||||
"key": "merchant_name",
|
||||
"label": "商户",
|
||||
"value": "全季酒店"
|
||||
}
|
||||
]
|
||||
},
|
||||
"requirement_check": {
|
||||
"matches": true,
|
||||
"current_expense_type": "transport",
|
||||
"current_expense_type_label": "交通费",
|
||||
"allowed_scene_labels": [
|
||||
"交通"
|
||||
],
|
||||
"allowed_document_type_labels": [
|
||||
"停车/通行费票据",
|
||||
"一般收据/凭证",
|
||||
"出租车/网约车票据",
|
||||
"增值税发票"
|
||||
],
|
||||
"recognized_scene_code": "transport",
|
||||
"recognized_scene_label": "交通票据",
|
||||
"recognized_document_type": "taxi_receipt",
|
||||
"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行程单\nAMAP ITINERARY\n1申请时间:2026-03-04\n【行程时间:2026-03-0407:05至2026-03-0407:33\n|行程人手机号:18602700270\n1共计1单行程,合计35.53元\n序号\n服务商\n车型\n上车时间\n城市\n起点\n终点\n金额\n经济型\n2026-03-04\n1\n滴滴出行\n武汉市\n全季酒店武汉工程大学店\n武汉站\n35.53元\n07:05\n页码:1/1",
|
||||
"ocr_summary": "高德地图一打车;行程单;AMAP ITINERARY",
|
||||
"ocr_avg_score": 0.9819406509399414,
|
||||
"ocr_line_count": 25,
|
||||
"ocr_classification_source": "rule",
|
||||
"ocr_classification_confidence": 0.88,
|
||||
"ocr_classification_evidence": [
|
||||
"滴滴出行",
|
||||
"滴滴",
|
||||
"打车",
|
||||
"上车"
|
||||
],
|
||||
"ocr_warnings": []
|
||||
{
|
||||
"file_name": "行程单_2_鄂AX9877.pdf",
|
||||
"storage_key": "0d3102fe-a458-42cf-b30c-4cffeeb74668/c99de539-23cb-4f1a-a21f-a40bce93d54e/行程单_2_鄂AX9877.pdf",
|
||||
"media_type": "application/pdf",
|
||||
"size_bytes": 32459,
|
||||
"uploaded_at": "2026-05-16T08:41:42.540134+00:00",
|
||||
"previewable": true,
|
||||
"preview_kind": "image",
|
||||
"preview_storage_key": "0d3102fe-a458-42cf-b30c-4cffeeb74668/c99de539-23cb-4f1a-a21f-a40bce93d54e/行程单_2_鄂AX9877.preview.png",
|
||||
"preview_media_type": "image/png",
|
||||
"preview_file_name": "行程单_2_鄂AX9877.preview.png",
|
||||
"analysis": {
|
||||
"severity": "pass",
|
||||
"label": "AI提示符合条件",
|
||||
"headline": "AI提示:附件符合基础校验条件",
|
||||
"summary": "已识别到票据类型和关键字段,且符合当前费用场景的附件要求。",
|
||||
"points": [
|
||||
"票据类型:已识别为出租车/网约车票据。",
|
||||
"附件类型要求:当前费用项目为交通费,已识别为出租车/网约车票据,符合当前交通费场景的附件要求。",
|
||||
"金额字段:已识别到与当前明细接近的金额 35.53 元。"
|
||||
],
|
||||
"suggestion": "建议继续核对报销分类、费用说明和业务场景是否一致。"
|
||||
},
|
||||
"document_info": {
|
||||
"document_type": "taxi_receipt",
|
||||
"document_type_label": "出租车/网约车票据",
|
||||
"scene_code": "transport",
|
||||
"scene_label": "交通票据",
|
||||
"fields": [
|
||||
{
|
||||
"key": "amount",
|
||||
"label": "金额",
|
||||
"value": "35.53元"
|
||||
},
|
||||
{
|
||||
"key": "date",
|
||||
"label": "日期",
|
||||
"value": "2026-03-04"
|
||||
},
|
||||
{
|
||||
"key": "merchant_name",
|
||||
"label": "商户",
|
||||
"value": "全季酒店"
|
||||
}
|
||||
]
|
||||
},
|
||||
"requirement_check": {
|
||||
"matches": true,
|
||||
"current_expense_type": "transport",
|
||||
"current_expense_type_label": "交通费",
|
||||
"allowed_scene_labels": [
|
||||
"交通"
|
||||
],
|
||||
"allowed_document_type_labels": [
|
||||
"停车/通行费票据",
|
||||
"一般收据/凭证",
|
||||
"出租车/网约车票据",
|
||||
"增值税发票"
|
||||
],
|
||||
"recognized_scene_code": "transport",
|
||||
"recognized_scene_label": "交通票据",
|
||||
"recognized_document_type": "taxi_receipt",
|
||||
"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行程单\nAMAP ITINERARY\n1申请时间:2026-03-04\n【行程时间:2026-03-0407:05至2026-03-0407:33\n|行程人手机号:18602700270\n1共计1单行程,合计35.53元\n序号\n服务商\n车型\n上车时间\n城市\n起点\n终点\n金额\n经济型\n2026-03-04\n1\n滴滴出行\n武汉市\n全季酒店武汉工程大学店\n武汉站\n35.53元\n07:05\n页码:1/1",
|
||||
"ocr_summary": "高德地图一打车;行程单;AMAP ITINERARY",
|
||||
"ocr_avg_score": 0.9819406509399414,
|
||||
"ocr_line_count": 25,
|
||||
"ocr_classification_source": "rule",
|
||||
"ocr_classification_confidence": 0.88,
|
||||
"ocr_classification_evidence": [
|
||||
"滴滴出行",
|
||||
"滴滴",
|
||||
"打车",
|
||||
"上车"
|
||||
],
|
||||
"ocr_warnings": []
|
||||
}
|
||||
@@ -1,84 +1,84 @@
|
||||
{
|
||||
"file_name": "行程单_1_鄂A1S987.pdf",
|
||||
"storage_key": "281095c5-d85b-428e-924f-250bdd6e0261/c676b663-8851-4b35-be4e-1ba13d46d35e/行程单_1_鄂A1S987.pdf",
|
||||
"media_type": "application/pdf",
|
||||
"size_bytes": 34880,
|
||||
"uploaded_at": "2026-05-16T08:17:53.656595+00:00",
|
||||
"previewable": true,
|
||||
"preview_kind": "image",
|
||||
"preview_storage_key": "281095c5-d85b-428e-924f-250bdd6e0261/c676b663-8851-4b35-be4e-1ba13d46d35e/行程单_1_鄂A1S987.preview.png",
|
||||
"preview_media_type": "image/png",
|
||||
"preview_file_name": "行程单_1_鄂A1S987.preview.png",
|
||||
"analysis": {
|
||||
"severity": "pass",
|
||||
"label": "AI提示符合条件",
|
||||
"headline": "AI提示:附件符合基础校验条件",
|
||||
"summary": "已识别到票据类型和关键字段,且符合当前费用场景的附件要求。",
|
||||
"points": [
|
||||
"票据类型:已识别为出租车/网约车票据。",
|
||||
"附件类型要求:当前费用项目为交通费,已识别为出租车/网约车票据,符合当前交通费场景的附件要求。",
|
||||
"金额字段:已识别到与当前明细接近的金额 10.30 元。"
|
||||
],
|
||||
"suggestion": "建议继续核对报销分类、费用说明和业务场景是否一致。"
|
||||
},
|
||||
"document_info": {
|
||||
"document_type": "taxi_receipt",
|
||||
"document_type_label": "出租车/网约车票据",
|
||||
"scene_code": "transport",
|
||||
"scene_label": "交通票据",
|
||||
"fields": [
|
||||
{
|
||||
"key": "amount",
|
||||
"label": "金额",
|
||||
"value": "10.3元"
|
||||
},
|
||||
{
|
||||
"key": "date",
|
||||
"label": "日期",
|
||||
"value": "2026-03-01"
|
||||
},
|
||||
{
|
||||
"key": "merchant_name",
|
||||
"label": "商户",
|
||||
"value": "全季酒店"
|
||||
}
|
||||
]
|
||||
},
|
||||
"requirement_check": {
|
||||
"matches": true,
|
||||
"current_expense_type": "transport",
|
||||
"current_expense_type_label": "交通费",
|
||||
"allowed_scene_labels": [
|
||||
"交通"
|
||||
],
|
||||
"allowed_document_type_labels": [
|
||||
"停车/通行费票据",
|
||||
"一般收据/凭证",
|
||||
"出租车/网约车票据",
|
||||
"增值税发票"
|
||||
],
|
||||
"recognized_scene_code": "transport",
|
||||
"recognized_scene_label": "交通票据",
|
||||
"recognized_document_type": "taxi_receipt",
|
||||
"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行程单\nAMAP ITINERARY\n1申请时间:2026-03-01\n【行程时间:2026-03-0113:23至2026-03-0113:40\n行程人手机号:18602700270\n|共计1单行程,合计10.30元\n序号\n服务商\n车型\n上车时间\n城市\n起点\n终点\n金额\n经济型\n2026-03-01\n1\n滴滴出行\n13:23\n武汉市\n金融港北地铁站\n全季酒店武汉工程大学店\n10.30元\n页码:1/1",
|
||||
"ocr_summary": "高德地图一打车;行程单;AMAP ITINERARY",
|
||||
"ocr_avg_score": 0.9844024634361267,
|
||||
"ocr_line_count": 25,
|
||||
"ocr_classification_source": "rule",
|
||||
"ocr_classification_confidence": 0.88,
|
||||
"ocr_classification_evidence": [
|
||||
"滴滴出行",
|
||||
"滴滴",
|
||||
"打车",
|
||||
"上车"
|
||||
],
|
||||
"ocr_warnings": []
|
||||
{
|
||||
"file_name": "行程单_1_鄂A1S987.pdf",
|
||||
"storage_key": "281095c5-d85b-428e-924f-250bdd6e0261/c676b663-8851-4b35-be4e-1ba13d46d35e/行程单_1_鄂A1S987.pdf",
|
||||
"media_type": "application/pdf",
|
||||
"size_bytes": 34880,
|
||||
"uploaded_at": "2026-05-16T08:17:53.656595+00:00",
|
||||
"previewable": true,
|
||||
"preview_kind": "image",
|
||||
"preview_storage_key": "281095c5-d85b-428e-924f-250bdd6e0261/c676b663-8851-4b35-be4e-1ba13d46d35e/行程单_1_鄂A1S987.preview.png",
|
||||
"preview_media_type": "image/png",
|
||||
"preview_file_name": "行程单_1_鄂A1S987.preview.png",
|
||||
"analysis": {
|
||||
"severity": "pass",
|
||||
"label": "AI提示符合条件",
|
||||
"headline": "AI提示:附件符合基础校验条件",
|
||||
"summary": "已识别到票据类型和关键字段,且符合当前费用场景的附件要求。",
|
||||
"points": [
|
||||
"票据类型:已识别为出租车/网约车票据。",
|
||||
"附件类型要求:当前费用项目为交通费,已识别为出租车/网约车票据,符合当前交通费场景的附件要求。",
|
||||
"金额字段:已识别到与当前明细接近的金额 10.30 元。"
|
||||
],
|
||||
"suggestion": "建议继续核对报销分类、费用说明和业务场景是否一致。"
|
||||
},
|
||||
"document_info": {
|
||||
"document_type": "taxi_receipt",
|
||||
"document_type_label": "出租车/网约车票据",
|
||||
"scene_code": "transport",
|
||||
"scene_label": "交通票据",
|
||||
"fields": [
|
||||
{
|
||||
"key": "amount",
|
||||
"label": "金额",
|
||||
"value": "10.3元"
|
||||
},
|
||||
{
|
||||
"key": "date",
|
||||
"label": "日期",
|
||||
"value": "2026-03-01"
|
||||
},
|
||||
{
|
||||
"key": "merchant_name",
|
||||
"label": "商户",
|
||||
"value": "全季酒店"
|
||||
}
|
||||
]
|
||||
},
|
||||
"requirement_check": {
|
||||
"matches": true,
|
||||
"current_expense_type": "transport",
|
||||
"current_expense_type_label": "交通费",
|
||||
"allowed_scene_labels": [
|
||||
"交通"
|
||||
],
|
||||
"allowed_document_type_labels": [
|
||||
"停车/通行费票据",
|
||||
"一般收据/凭证",
|
||||
"出租车/网约车票据",
|
||||
"增值税发票"
|
||||
],
|
||||
"recognized_scene_code": "transport",
|
||||
"recognized_scene_label": "交通票据",
|
||||
"recognized_document_type": "taxi_receipt",
|
||||
"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行程单\nAMAP ITINERARY\n1申请时间:2026-03-01\n【行程时间:2026-03-0113:23至2026-03-0113:40\n行程人手机号:18602700270\n|共计1单行程,合计10.30元\n序号\n服务商\n车型\n上车时间\n城市\n起点\n终点\n金额\n经济型\n2026-03-01\n1\n滴滴出行\n13:23\n武汉市\n金融港北地铁站\n全季酒店武汉工程大学店\n10.30元\n页码:1/1",
|
||||
"ocr_summary": "高德地图一打车;行程单;AMAP ITINERARY",
|
||||
"ocr_avg_score": 0.9844024634361267,
|
||||
"ocr_line_count": 25,
|
||||
"ocr_classification_source": "rule",
|
||||
"ocr_classification_confidence": 0.88,
|
||||
"ocr_classification_evidence": [
|
||||
"滴滴出行",
|
||||
"滴滴",
|
||||
"打车",
|
||||
"上车"
|
||||
],
|
||||
"ocr_warnings": []
|
||||
}
|
||||
@@ -35,13 +35,13 @@
|
||||
"updated_at": "2026-05-17T13:00:09.485818+00:00",
|
||||
"uploaded_by": "admin",
|
||||
"version_number": 1,
|
||||
"ingest_status": 1,
|
||||
"ingest_status_updated_at": "2026-05-17T13:00:09.485818+00:00",
|
||||
"ingest_status": 4,
|
||||
"ingest_status_updated_at": "2026-05-19T16:00:57.418443+00:00",
|
||||
"ingest_completed_at": "",
|
||||
"ingest_document_name": "",
|
||||
"ingest_document_updated_at": "",
|
||||
"ingest_document_sha256": "",
|
||||
"ingest_agent_run_id": ""
|
||||
"ingest_agent_run_id": "run_57f2d8727aaa4374"
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,5 +24,28 @@
|
||||
"processing_start_time": 1779011842,
|
||||
"processing_end_time": 1779012093
|
||||
}
|
||||
},
|
||||
"a8f8465df08e455ebe133351721d49f8": {
|
||||
"status": "failed",
|
||||
"error_msg": "Embedding func: Worker execution timeout after 60s",
|
||||
"chunks_count": 6,
|
||||
"chunks_list": [
|
||||
"chunk-07de6ea74f60535b689f977295770273",
|
||||
"chunk-99c6f377dff2b9a37a7214b7b05ea9a8",
|
||||
"chunk-1746bd83138e85e66a78e0cb9ad79272",
|
||||
"chunk-ce44e4483e4119265b43eacb72e0326a",
|
||||
"chunk-2187fa0609874bdda339c9850da45a26",
|
||||
"chunk-2224d777c0b72d0b2dab622c79096c2c"
|
||||
],
|
||||
"content_summary": "# 产品需求文档\n## 文档信息\n| 项目 | 内容 |\n|------|------|\n| 项目名称 |\n无单报销\n|\n| 版本 | V1.0 |\n| 日期 | 2026-05-06 |\n| 状态 | 正式版 |\n---\n## 1. 项目概述\n### 1.1 项目背景\n面向\n大型企业,\n从业务人员视角出发,解决现有ERP使用体验不佳的问题。\n在ERP的发展历程中,“单据化”曾是财务合规的一大进步,它确保了每笔支出都有据可查。但不可否认,传统的人工填单确实\n也制造了很多\n“枷锁”。在AI时代,解...",
|
||||
"content_length": 9088,
|
||||
"created_at": "2026-05-19T15:59:57.283110+00:00",
|
||||
"updated_at": "2026-05-19T16:00:57.323299+00:00",
|
||||
"file_path": "/app/server/storage/knowledge/报销制度/a8f8465df08e455ebe133351721d49f8__无单需求文档0506.docx",
|
||||
"track_id": "insert_20260519_155957_88c49850",
|
||||
"metadata": {
|
||||
"processing_start_time": 1779206397,
|
||||
"processing_end_time": 1779206457
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,268 +1,268 @@
|
||||
{
|
||||
"2c1cb358f08d44ceb0e4d287133206ec": {
|
||||
"entity_names": [
|
||||
"工会委员会",
|
||||
"Business Original Documents",
|
||||
"First Approver",
|
||||
"P8",
|
||||
"一级部门总经理",
|
||||
"组织人事部",
|
||||
"业务原始凭据",
|
||||
"营销中心",
|
||||
"保证金",
|
||||
"投标保证金",
|
||||
"餐补",
|
||||
"第十四条业务招待费",
|
||||
"Chief Engineer",
|
||||
"业务招待",
|
||||
"Employee Welfare",
|
||||
"经济舱",
|
||||
"2024年4月17日",
|
||||
"三等舱",
|
||||
"财务信息化系统",
|
||||
"分管领导",
|
||||
"重点支出管理规定",
|
||||
"备用金借款",
|
||||
"Financial Review",
|
||||
"第五章附则",
|
||||
"Company Leadership",
|
||||
"第十九条",
|
||||
"经办人",
|
||||
"预算内支出",
|
||||
"Current Account Payment",
|
||||
"Business Entertainment",
|
||||
"Tax Control System Details",
|
||||
"第二十一条",
|
||||
"成本中心归属",
|
||||
"岗位支出报销审批权限表",
|
||||
"工会经费管理办法",
|
||||
"商旅系统",
|
||||
"Special Subsidy",
|
||||
"中国银行外汇折算价",
|
||||
"因公借款",
|
||||
"资产采购",
|
||||
"广告费",
|
||||
"First-Level Department General Manager",
|
||||
"正式员工",
|
||||
"一万元",
|
||||
"公司员工教育培训管理办法",
|
||||
"责任原则",
|
||||
"第二章职责分工",
|
||||
"预算先行",
|
||||
"Planning and Finance Department",
|
||||
"Accommodation Cost Reimbursement",
|
||||
"Official Vehicle Subsidy",
|
||||
"第四条归口管理部门主要职责",
|
||||
"Personal Service Compensation",
|
||||
"邮递费",
|
||||
"附表3:支出归口管理部门与归口业务范围",
|
||||
"员工",
|
||||
"第二条目的",
|
||||
"Director",
|
||||
"支出归口管理部门与归口业务范围",
|
||||
"其他支出(员工)",
|
||||
"报销标准",
|
||||
"5000000 Yuan Approval Limit",
|
||||
"第十一条备用金借款",
|
||||
"会议费",
|
||||
"第十七条",
|
||||
"第七条各级管理人员主要职责",
|
||||
"50000 Yuan Approval Limit",
|
||||
"全资子公司",
|
||||
"涉外业务汇率标准",
|
||||
"总监",
|
||||
"第十三条差旅费",
|
||||
"审批权限表",
|
||||
"商旅订票规范",
|
||||
"Final Approval Position",
|
||||
"报销资格",
|
||||
"新增报销规定",
|
||||
"公司支出管理办法",
|
||||
"Institution General Manager",
|
||||
"房屋租金",
|
||||
"Staff Activities",
|
||||
"分包外包(内部单位)",
|
||||
"报销申请时限",
|
||||
"Financial Information System",
|
||||
"Expenditure Authorization Approval Scope",
|
||||
"直辖市",
|
||||
"培训费",
|
||||
"第十二条市内交通费",
|
||||
"第十五条",
|
||||
"终审岗",
|
||||
"Remote Work Housing",
|
||||
"Centralized Management department",
|
||||
"第二十条",
|
||||
"办公室(党委办公室)",
|
||||
"Three Flows Consistency Principle",
|
||||
"审批权限",
|
||||
"VAT Special Invoice",
|
||||
"后勤服务部",
|
||||
"员工支出报销审批权限表",
|
||||
"公司总裁",
|
||||
"出差补贴",
|
||||
"Basic Level Managers",
|
||||
"预付款项",
|
||||
"附表1:员工支出报销审批权限表",
|
||||
"经办部门",
|
||||
"信息管理部",
|
||||
"通信费",
|
||||
"第十六条",
|
||||
"增值税发票",
|
||||
"财务入账条件",
|
||||
"Hotel Accommodation Standards",
|
||||
"审批流转程序",
|
||||
"Self-Driving Travel Provisions",
|
||||
"交通费",
|
||||
"第九条支出报销审批",
|
||||
"薪酬福利支出分配计划",
|
||||
"产品规划设计部",
|
||||
"因公用车补贴",
|
||||
"Committee Chairpersons",
|
||||
"Business Division General Manager",
|
||||
"组织安排",
|
||||
"1 Yuan Per Person Per Kilometer Reimbursement",
|
||||
"Separation of Approval and Processing Principle",
|
||||
"第五条计划财务部主要职责",
|
||||
"200000 Yuan Approval Limit",
|
||||
"公司各部门",
|
||||
"第十四条",
|
||||
"Other Areas",
|
||||
"分支机构",
|
||||
"Departments And Units",
|
||||
"计划财务部",
|
||||
"Other Employees",
|
||||
"第二十三条",
|
||||
"公司团建管理办法",
|
||||
"火车硬席",
|
||||
"税控系统明细清单",
|
||||
"Trade Union Fund",
|
||||
"报销标准变化情况",
|
||||
"薪酬福利支出",
|
||||
"Hong Kong, Macau, And Taiwan Region",
|
||||
"对外捐赠支出",
|
||||
"Multi-Level Approval Rule",
|
||||
"Three Working Days Deadline",
|
||||
"Employee Remuneration",
|
||||
"销售退款",
|
||||
"股权投资、兼并收购",
|
||||
"控股子公司",
|
||||
"取消报销规定",
|
||||
"Procurement Management Regulations",
|
||||
"Middle Managers",
|
||||
"差旅费",
|
||||
"批办分离",
|
||||
"住宿费",
|
||||
"Travel Allowance Standards",
|
||||
"第二十三条本办法的归口与实施",
|
||||
"Senior Vice President",
|
||||
"供应商",
|
||||
"人事归口管理部门",
|
||||
"Management Personnel At All Levels",
|
||||
"效益优先",
|
||||
"Operating Department Individual",
|
||||
"Remote Work Housing Rental Expenses",
|
||||
"取消报销规定内容",
|
||||
"Company",
|
||||
"修订说明",
|
||||
"国网数科公司",
|
||||
"Vice President",
|
||||
"分级授权",
|
||||
"Expenditure Reimbursement Application",
|
||||
"第二十四条附件",
|
||||
"第二十二条",
|
||||
"出租车",
|
||||
"Night High-Speed Rail Provision",
|
||||
"各级管理人员",
|
||||
"受益原则",
|
||||
"公司员工因公通讯费用实施细则",
|
||||
"公司支出管理办法(2024)",
|
||||
"出差补贴标准",
|
||||
"Bid Security Deposit Approval Limits Table",
|
||||
"第二条范围",
|
||||
"Company Property Rental Management",
|
||||
"调动工作",
|
||||
"远光软件股份有限公司",
|
||||
"市内交通费",
|
||||
"交通工具等级标准",
|
||||
"Operator",
|
||||
"第八条支出报销申请",
|
||||
"Directly-Controlled Municipalities And Special Administrative Regions",
|
||||
"出差规定",
|
||||
"业务招待费",
|
||||
"Senior Managers",
|
||||
"逐级审批规则",
|
||||
"Company Business Travel System",
|
||||
"广告宣传费",
|
||||
"Transportation Cost Reimbursement",
|
||||
"财务",
|
||||
"第一章总则",
|
||||
"材料采购",
|
||||
"人力资源服务部",
|
||||
"证券与法律事务部",
|
||||
"Transportation Level Standards",
|
||||
"归口管理部门",
|
||||
"商旅客服",
|
||||
"第四章重点支出管理规定",
|
||||
"出差审批程序",
|
||||
"Business Trip Approval",
|
||||
"西藏",
|
||||
"附表2:岗位支出报销审批权限表",
|
||||
"第十八条",
|
||||
"第二十四条",
|
||||
"Company Hotel Accommodation Limit Standards",
|
||||
"办法",
|
||||
"DAP研发中心",
|
||||
"新增规定内容",
|
||||
"基本补助",
|
||||
"Travel Allowance",
|
||||
"异地挂职锻炼补贴标准",
|
||||
"部门负责人",
|
||||
"Provincial Capitals",
|
||||
"特区",
|
||||
"Transportation Tickets",
|
||||
"第三章支出报销申请与审批",
|
||||
"品牌及市场运营中心",
|
||||
"分包外包(外部单位)",
|
||||
"探亲路费",
|
||||
"President",
|
||||
"凭据报销",
|
||||
"基本出差补贴",
|
||||
"Taxi Usage Regulations",
|
||||
"Government Fees",
|
||||
"Commercial Travel System",
|
||||
"远光制度〔2024〕14号",
|
||||
"审批权限变化情况",
|
||||
"基建工程",
|
||||
"支出报销申请与审批",
|
||||
"中国外汇交易中心参考汇率",
|
||||
"Department Manager",
|
||||
"支出报销审批",
|
||||
"预算调整决策程序",
|
||||
"公司1号文",
|
||||
"External Conference Accommodation",
|
||||
"厉行节约",
|
||||
"Commercial Insurance",
|
||||
"公司",
|
||||
"第三条管理原则",
|
||||
"捐赠申请",
|
||||
"分类控制",
|
||||
"业务宣传费",
|
||||
"产业投资部",
|
||||
"公司员工探亲管理办法",
|
||||
"Subsequent Approver",
|
||||
"100000 Yuan Approval Limit",
|
||||
"Tax Authority Recognized Invoice",
|
||||
"国家电网公司",
|
||||
"业务佐证材料",
|
||||
"第六条经办部门(个人)主要职责",
|
||||
"结算起点",
|
||||
"第十条支出成本中心归属",
|
||||
"母公司"
|
||||
],
|
||||
"count": 258,
|
||||
"create_time": 1779012093,
|
||||
"update_time": 1779012093,
|
||||
"_id": "2c1cb358f08d44ceb0e4d287133206ec"
|
||||
}
|
||||
{
|
||||
"2c1cb358f08d44ceb0e4d287133206ec": {
|
||||
"entity_names": [
|
||||
"工会委员会",
|
||||
"Business Original Documents",
|
||||
"First Approver",
|
||||
"P8",
|
||||
"一级部门总经理",
|
||||
"组织人事部",
|
||||
"业务原始凭据",
|
||||
"营销中心",
|
||||
"保证金",
|
||||
"投标保证金",
|
||||
"餐补",
|
||||
"第十四条业务招待费",
|
||||
"Chief Engineer",
|
||||
"业务招待",
|
||||
"Employee Welfare",
|
||||
"经济舱",
|
||||
"2024年4月17日",
|
||||
"三等舱",
|
||||
"财务信息化系统",
|
||||
"分管领导",
|
||||
"重点支出管理规定",
|
||||
"备用金借款",
|
||||
"Financial Review",
|
||||
"第五章附则",
|
||||
"Company Leadership",
|
||||
"第十九条",
|
||||
"经办人",
|
||||
"预算内支出",
|
||||
"Current Account Payment",
|
||||
"Business Entertainment",
|
||||
"Tax Control System Details",
|
||||
"第二十一条",
|
||||
"成本中心归属",
|
||||
"岗位支出报销审批权限表",
|
||||
"工会经费管理办法",
|
||||
"商旅系统",
|
||||
"Special Subsidy",
|
||||
"中国银行外汇折算价",
|
||||
"因公借款",
|
||||
"资产采购",
|
||||
"广告费",
|
||||
"First-Level Department General Manager",
|
||||
"正式员工",
|
||||
"一万元",
|
||||
"公司员工教育培训管理办法",
|
||||
"责任原则",
|
||||
"第二章职责分工",
|
||||
"预算先行",
|
||||
"Planning and Finance Department",
|
||||
"Accommodation Cost Reimbursement",
|
||||
"Official Vehicle Subsidy",
|
||||
"第四条归口管理部门主要职责",
|
||||
"Personal Service Compensation",
|
||||
"邮递费",
|
||||
"附表3:支出归口管理部门与归口业务范围",
|
||||
"员工",
|
||||
"第二条目的",
|
||||
"Director",
|
||||
"支出归口管理部门与归口业务范围",
|
||||
"其他支出(员工)",
|
||||
"报销标准",
|
||||
"5000000 Yuan Approval Limit",
|
||||
"第十一条备用金借款",
|
||||
"会议费",
|
||||
"第十七条",
|
||||
"第七条各级管理人员主要职责",
|
||||
"50000 Yuan Approval Limit",
|
||||
"全资子公司",
|
||||
"涉外业务汇率标准",
|
||||
"总监",
|
||||
"第十三条差旅费",
|
||||
"审批权限表",
|
||||
"商旅订票规范",
|
||||
"Final Approval Position",
|
||||
"报销资格",
|
||||
"新增报销规定",
|
||||
"公司支出管理办法",
|
||||
"Institution General Manager",
|
||||
"房屋租金",
|
||||
"Staff Activities",
|
||||
"分包外包(内部单位)",
|
||||
"报销申请时限",
|
||||
"Financial Information System",
|
||||
"Expenditure Authorization Approval Scope",
|
||||
"直辖市",
|
||||
"培训费",
|
||||
"第十二条市内交通费",
|
||||
"第十五条",
|
||||
"终审岗",
|
||||
"Remote Work Housing",
|
||||
"Centralized Management department",
|
||||
"第二十条",
|
||||
"办公室(党委办公室)",
|
||||
"Three Flows Consistency Principle",
|
||||
"审批权限",
|
||||
"VAT Special Invoice",
|
||||
"后勤服务部",
|
||||
"员工支出报销审批权限表",
|
||||
"公司总裁",
|
||||
"出差补贴",
|
||||
"Basic Level Managers",
|
||||
"预付款项",
|
||||
"附表1:员工支出报销审批权限表",
|
||||
"经办部门",
|
||||
"信息管理部",
|
||||
"通信费",
|
||||
"第十六条",
|
||||
"增值税发票",
|
||||
"财务入账条件",
|
||||
"Hotel Accommodation Standards",
|
||||
"审批流转程序",
|
||||
"Self-Driving Travel Provisions",
|
||||
"交通费",
|
||||
"第九条支出报销审批",
|
||||
"薪酬福利支出分配计划",
|
||||
"产品规划设计部",
|
||||
"因公用车补贴",
|
||||
"Committee Chairpersons",
|
||||
"Business Division General Manager",
|
||||
"组织安排",
|
||||
"1 Yuan Per Person Per Kilometer Reimbursement",
|
||||
"Separation of Approval and Processing Principle",
|
||||
"第五条计划财务部主要职责",
|
||||
"200000 Yuan Approval Limit",
|
||||
"公司各部门",
|
||||
"第十四条",
|
||||
"Other Areas",
|
||||
"分支机构",
|
||||
"Departments And Units",
|
||||
"计划财务部",
|
||||
"Other Employees",
|
||||
"第二十三条",
|
||||
"公司团建管理办法",
|
||||
"火车硬席",
|
||||
"税控系统明细清单",
|
||||
"Trade Union Fund",
|
||||
"报销标准变化情况",
|
||||
"薪酬福利支出",
|
||||
"Hong Kong, Macau, And Taiwan Region",
|
||||
"对外捐赠支出",
|
||||
"Multi-Level Approval Rule",
|
||||
"Three Working Days Deadline",
|
||||
"Employee Remuneration",
|
||||
"销售退款",
|
||||
"股权投资、兼并收购",
|
||||
"控股子公司",
|
||||
"取消报销规定",
|
||||
"Procurement Management Regulations",
|
||||
"Middle Managers",
|
||||
"差旅费",
|
||||
"批办分离",
|
||||
"住宿费",
|
||||
"Travel Allowance Standards",
|
||||
"第二十三条本办法的归口与实施",
|
||||
"Senior Vice President",
|
||||
"供应商",
|
||||
"人事归口管理部门",
|
||||
"Management Personnel At All Levels",
|
||||
"效益优先",
|
||||
"Operating Department Individual",
|
||||
"Remote Work Housing Rental Expenses",
|
||||
"取消报销规定内容",
|
||||
"Company",
|
||||
"修订说明",
|
||||
"国网数科公司",
|
||||
"Vice President",
|
||||
"分级授权",
|
||||
"Expenditure Reimbursement Application",
|
||||
"第二十四条附件",
|
||||
"第二十二条",
|
||||
"出租车",
|
||||
"Night High-Speed Rail Provision",
|
||||
"各级管理人员",
|
||||
"受益原则",
|
||||
"公司员工因公通讯费用实施细则",
|
||||
"公司支出管理办法(2024)",
|
||||
"出差补贴标准",
|
||||
"Bid Security Deposit Approval Limits Table",
|
||||
"第二条范围",
|
||||
"Company Property Rental Management",
|
||||
"调动工作",
|
||||
"远光软件股份有限公司",
|
||||
"市内交通费",
|
||||
"交通工具等级标准",
|
||||
"Operator",
|
||||
"第八条支出报销申请",
|
||||
"Directly-Controlled Municipalities And Special Administrative Regions",
|
||||
"出差规定",
|
||||
"业务招待费",
|
||||
"Senior Managers",
|
||||
"逐级审批规则",
|
||||
"Company Business Travel System",
|
||||
"广告宣传费",
|
||||
"Transportation Cost Reimbursement",
|
||||
"财务",
|
||||
"第一章总则",
|
||||
"材料采购",
|
||||
"人力资源服务部",
|
||||
"证券与法律事务部",
|
||||
"Transportation Level Standards",
|
||||
"归口管理部门",
|
||||
"商旅客服",
|
||||
"第四章重点支出管理规定",
|
||||
"出差审批程序",
|
||||
"Business Trip Approval",
|
||||
"西藏",
|
||||
"附表2:岗位支出报销审批权限表",
|
||||
"第十八条",
|
||||
"第二十四条",
|
||||
"Company Hotel Accommodation Limit Standards",
|
||||
"办法",
|
||||
"DAP研发中心",
|
||||
"新增规定内容",
|
||||
"基本补助",
|
||||
"Travel Allowance",
|
||||
"异地挂职锻炼补贴标准",
|
||||
"部门负责人",
|
||||
"Provincial Capitals",
|
||||
"特区",
|
||||
"Transportation Tickets",
|
||||
"第三章支出报销申请与审批",
|
||||
"品牌及市场运营中心",
|
||||
"分包外包(外部单位)",
|
||||
"探亲路费",
|
||||
"President",
|
||||
"凭据报销",
|
||||
"基本出差补贴",
|
||||
"Taxi Usage Regulations",
|
||||
"Government Fees",
|
||||
"Commercial Travel System",
|
||||
"远光制度〔2024〕14号",
|
||||
"审批权限变化情况",
|
||||
"基建工程",
|
||||
"支出报销申请与审批",
|
||||
"中国外汇交易中心参考汇率",
|
||||
"Department Manager",
|
||||
"支出报销审批",
|
||||
"预算调整决策程序",
|
||||
"公司1号文",
|
||||
"External Conference Accommodation",
|
||||
"厉行节约",
|
||||
"Commercial Insurance",
|
||||
"公司",
|
||||
"第三条管理原则",
|
||||
"捐赠申请",
|
||||
"分类控制",
|
||||
"业务宣传费",
|
||||
"产业投资部",
|
||||
"公司员工探亲管理办法",
|
||||
"Subsequent Approver",
|
||||
"100000 Yuan Approval Limit",
|
||||
"Tax Authority Recognized Invoice",
|
||||
"国家电网公司",
|
||||
"业务佐证材料",
|
||||
"第六条经办部门(个人)主要职责",
|
||||
"结算起点",
|
||||
"第十条支出成本中心归属",
|
||||
"母公司"
|
||||
],
|
||||
"count": 258,
|
||||
"create_time": 1779012093,
|
||||
"update_time": 1779012093,
|
||||
"_id": "2c1cb358f08d44ceb0e4d287133206ec"
|
||||
}
|
||||
}
|
||||
@@ -1,166 +1,166 @@
|
||||
{
|
||||
"2c1cb358f08d44ceb0e4d287133206ec": {
|
||||
"relation_pairs": [
|
||||
[
|
||||
"Departments And Units",
|
||||
"Taxi Usage Regulations"
|
||||
],
|
||||
[
|
||||
"取消报销规定内容",
|
||||
"报销标准变化情况"
|
||||
],
|
||||
[
|
||||
"业务招待费",
|
||||
"第十四条"
|
||||
],
|
||||
[
|
||||
"控股子公司",
|
||||
"计划财务部"
|
||||
],
|
||||
[
|
||||
"公司支出管理办法",
|
||||
"工会委员会"
|
||||
],
|
||||
[
|
||||
"第一章总则",
|
||||
"第三条管理原则"
|
||||
],
|
||||
[
|
||||
"广告宣传费",
|
||||
"第十六条"
|
||||
],
|
||||
[
|
||||
"Tax Control System Details",
|
||||
"VAT Special Invoice"
|
||||
],
|
||||
[
|
||||
"Expenditure Reimbursement Application",
|
||||
"Tax Authority Recognized Invoice"
|
||||
],
|
||||
[
|
||||
"远光制度〔2024〕14号",
|
||||
"远光软件股份有限公司"
|
||||
],
|
||||
[
|
||||
"Financial Review",
|
||||
"Operator"
|
||||
],
|
||||
[
|
||||
"Operating Department Individual",
|
||||
"Procurement Management Regulations"
|
||||
],
|
||||
[
|
||||
"会议费",
|
||||
"第十五条"
|
||||
],
|
||||
[
|
||||
"Company",
|
||||
"Management Personnel At All Levels"
|
||||
],
|
||||
[
|
||||
"公司",
|
||||
"第十七条"
|
||||
],
|
||||
[
|
||||
"公司",
|
||||
"第十八条"
|
||||
],
|
||||
[
|
||||
"Operator",
|
||||
"Three Working Days Deadline"
|
||||
],
|
||||
[
|
||||
"第十一条备用金借款",
|
||||
"第四章重点支出管理规定"
|
||||
],
|
||||
[
|
||||
"Expenditure Reimbursement Application",
|
||||
"Operator"
|
||||
],
|
||||
[
|
||||
"业务招待费",
|
||||
"差旅费"
|
||||
],
|
||||
[
|
||||
"公司",
|
||||
"第二十一条"
|
||||
],
|
||||
[
|
||||
"公司支出管理办法(2024)",
|
||||
"远光软件股份有限公司"
|
||||
],
|
||||
[
|
||||
"第四条归口管理部门主要职责",
|
||||
"计划财务部"
|
||||
],
|
||||
[
|
||||
"会议费",
|
||||
"差旅费"
|
||||
],
|
||||
[
|
||||
"Company",
|
||||
"Operating Department Individual"
|
||||
],
|
||||
[
|
||||
"商旅系统",
|
||||
"差旅费"
|
||||
],
|
||||
[
|
||||
"会议费",
|
||||
"公司总裁"
|
||||
],
|
||||
[
|
||||
"计划财务部",
|
||||
"远光软件股份有限公司"
|
||||
],
|
||||
[
|
||||
"公司",
|
||||
"第十九条"
|
||||
],
|
||||
[
|
||||
"公司",
|
||||
"第二十条"
|
||||
],
|
||||
[
|
||||
"Company",
|
||||
"Planning and Finance Department"
|
||||
],
|
||||
[
|
||||
"公司支出管理办法",
|
||||
"营销中心"
|
||||
],
|
||||
[
|
||||
"Business Original Documents",
|
||||
"Operator"
|
||||
],
|
||||
[
|
||||
"公司支出管理办法",
|
||||
"办公室(党委办公室)"
|
||||
],
|
||||
[
|
||||
"Departments And Units",
|
||||
"Night High-Speed Rail Provision"
|
||||
],
|
||||
[
|
||||
"Centralized Management department",
|
||||
"Company"
|
||||
],
|
||||
[
|
||||
"组织人事部",
|
||||
"调动工作"
|
||||
],
|
||||
[
|
||||
"报销标准变化情况",
|
||||
"远光软件股份有限公司"
|
||||
],
|
||||
[
|
||||
"第一章总则",
|
||||
"远光软件股份有限公司"
|
||||
]
|
||||
],
|
||||
"count": 39,
|
||||
"create_time": 1779012093,
|
||||
"update_time": 1779012093,
|
||||
"_id": "2c1cb358f08d44ceb0e4d287133206ec"
|
||||
}
|
||||
{
|
||||
"2c1cb358f08d44ceb0e4d287133206ec": {
|
||||
"relation_pairs": [
|
||||
[
|
||||
"Departments And Units",
|
||||
"Taxi Usage Regulations"
|
||||
],
|
||||
[
|
||||
"取消报销规定内容",
|
||||
"报销标准变化情况"
|
||||
],
|
||||
[
|
||||
"业务招待费",
|
||||
"第十四条"
|
||||
],
|
||||
[
|
||||
"控股子公司",
|
||||
"计划财务部"
|
||||
],
|
||||
[
|
||||
"公司支出管理办法",
|
||||
"工会委员会"
|
||||
],
|
||||
[
|
||||
"第一章总则",
|
||||
"第三条管理原则"
|
||||
],
|
||||
[
|
||||
"广告宣传费",
|
||||
"第十六条"
|
||||
],
|
||||
[
|
||||
"Tax Control System Details",
|
||||
"VAT Special Invoice"
|
||||
],
|
||||
[
|
||||
"Expenditure Reimbursement Application",
|
||||
"Tax Authority Recognized Invoice"
|
||||
],
|
||||
[
|
||||
"远光制度〔2024〕14号",
|
||||
"远光软件股份有限公司"
|
||||
],
|
||||
[
|
||||
"Financial Review",
|
||||
"Operator"
|
||||
],
|
||||
[
|
||||
"Operating Department Individual",
|
||||
"Procurement Management Regulations"
|
||||
],
|
||||
[
|
||||
"会议费",
|
||||
"第十五条"
|
||||
],
|
||||
[
|
||||
"Company",
|
||||
"Management Personnel At All Levels"
|
||||
],
|
||||
[
|
||||
"公司",
|
||||
"第十七条"
|
||||
],
|
||||
[
|
||||
"公司",
|
||||
"第十八条"
|
||||
],
|
||||
[
|
||||
"Operator",
|
||||
"Three Working Days Deadline"
|
||||
],
|
||||
[
|
||||
"第十一条备用金借款",
|
||||
"第四章重点支出管理规定"
|
||||
],
|
||||
[
|
||||
"Expenditure Reimbursement Application",
|
||||
"Operator"
|
||||
],
|
||||
[
|
||||
"业务招待费",
|
||||
"差旅费"
|
||||
],
|
||||
[
|
||||
"公司",
|
||||
"第二十一条"
|
||||
],
|
||||
[
|
||||
"公司支出管理办法(2024)",
|
||||
"远光软件股份有限公司"
|
||||
],
|
||||
[
|
||||
"第四条归口管理部门主要职责",
|
||||
"计划财务部"
|
||||
],
|
||||
[
|
||||
"会议费",
|
||||
"差旅费"
|
||||
],
|
||||
[
|
||||
"Company",
|
||||
"Operating Department Individual"
|
||||
],
|
||||
[
|
||||
"商旅系统",
|
||||
"差旅费"
|
||||
],
|
||||
[
|
||||
"会议费",
|
||||
"公司总裁"
|
||||
],
|
||||
[
|
||||
"计划财务部",
|
||||
"远光软件股份有限公司"
|
||||
],
|
||||
[
|
||||
"公司",
|
||||
"第十九条"
|
||||
],
|
||||
[
|
||||
"公司",
|
||||
"第二十条"
|
||||
],
|
||||
[
|
||||
"Company",
|
||||
"Planning and Finance Department"
|
||||
],
|
||||
[
|
||||
"公司支出管理办法",
|
||||
"营销中心"
|
||||
],
|
||||
[
|
||||
"Business Original Documents",
|
||||
"Operator"
|
||||
],
|
||||
[
|
||||
"公司支出管理办法",
|
||||
"办公室(党委办公室)"
|
||||
],
|
||||
[
|
||||
"Departments And Units",
|
||||
"Night High-Speed Rail Provision"
|
||||
],
|
||||
[
|
||||
"Centralized Management department",
|
||||
"Company"
|
||||
],
|
||||
[
|
||||
"组织人事部",
|
||||
"调动工作"
|
||||
],
|
||||
[
|
||||
"报销标准变化情况",
|
||||
"远光软件股份有限公司"
|
||||
],
|
||||
[
|
||||
"第一章总则",
|
||||
"远光软件股份有限公司"
|
||||
]
|
||||
],
|
||||
"count": 39,
|
||||
"create_time": 1779012093,
|
||||
"update_time": 1779012093,
|
||||
"_id": "2c1cb358f08d44ceb0e4d287133206ec"
|
||||
}
|
||||
}
|
||||
@@ -1,353 +1,353 @@
|
||||
{
|
||||
"第一章总则<SEP>远光软件股份有限公司": {
|
||||
"chunk_ids": [
|
||||
"chunk-aa5435156b829944c173fa1d2d7a93d4"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012088,
|
||||
"update_time": 1779012088,
|
||||
"_id": "第一章总则<SEP>远光软件股份有限公司"
|
||||
},
|
||||
"第十一条备用金借款<SEP>第四章重点支出管理规定": {
|
||||
"chunk_ids": [
|
||||
"chunk-aa5435156b829944c173fa1d2d7a93d4"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012088,
|
||||
"update_time": 1779012088,
|
||||
"_id": "第十一条备用金借款<SEP>第四章重点支出管理规定"
|
||||
},
|
||||
"公司支出管理办法<SEP>办公室(党委办公室)": {
|
||||
"chunk_ids": [
|
||||
"chunk-afc57a0e9548d1f484da6df6c182676b"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012088,
|
||||
"update_time": 1779012088,
|
||||
"_id": "公司支出管理办法<SEP>办公室(党委办公室)"
|
||||
},
|
||||
"计划财务部<SEP>远光软件股份有限公司": {
|
||||
"chunk_ids": [
|
||||
"chunk-aa5435156b829944c173fa1d2d7a93d4"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012076,
|
||||
"update_time": 1779012076,
|
||||
"_id": "计划财务部<SEP>远光软件股份有限公司"
|
||||
},
|
||||
"第一章总则<SEP>第三条管理原则": {
|
||||
"chunk_ids": [
|
||||
"chunk-aa5435156b829944c173fa1d2d7a93d4"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012076,
|
||||
"update_time": 1779012076,
|
||||
"_id": "第一章总则<SEP>第三条管理原则"
|
||||
},
|
||||
"Company<SEP>Management Personnel At All Levels": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012076,
|
||||
"update_time": 1779012076,
|
||||
"_id": "Company<SEP>Management Personnel At All Levels"
|
||||
},
|
||||
"Centralized Management department<SEP>Company": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012077,
|
||||
"update_time": 1779012077,
|
||||
"_id": "Centralized Management department<SEP>Company"
|
||||
},
|
||||
"Company<SEP>Planning and Finance Department": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012077,
|
||||
"update_time": 1779012077,
|
||||
"_id": "Company<SEP>Planning and Finance Department"
|
||||
},
|
||||
"Company<SEP>Operating Department Individual": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012078,
|
||||
"update_time": 1779012078,
|
||||
"_id": "Company<SEP>Operating Department Individual"
|
||||
},
|
||||
"公司支出管理办法<SEP>工会委员会": {
|
||||
"chunk_ids": [
|
||||
"chunk-afc57a0e9548d1f484da6df6c182676b"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012079,
|
||||
"update_time": 1779012079,
|
||||
"_id": "公司支出管理办法<SEP>工会委员会"
|
||||
},
|
||||
"Expenditure Reimbursement Application<SEP>Operator": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012079,
|
||||
"update_time": 1779012079,
|
||||
"_id": "Expenditure Reimbursement Application<SEP>Operator"
|
||||
},
|
||||
"公司支出管理办法<SEP>营销中心": {
|
||||
"chunk_ids": [
|
||||
"chunk-afc57a0e9548d1f484da6df6c182676b"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012079,
|
||||
"update_time": 1779012079,
|
||||
"_id": "公司支出管理办法<SEP>营销中心"
|
||||
},
|
||||
"第四条归口管理部门主要职责<SEP>计划财务部": {
|
||||
"chunk_ids": [
|
||||
"chunk-aa5435156b829944c173fa1d2d7a93d4"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012079,
|
||||
"update_time": 1779012079,
|
||||
"_id": "第四条归口管理部门主要职责<SEP>计划财务部"
|
||||
},
|
||||
"Tax Control System Details<SEP>VAT Special Invoice": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012079,
|
||||
"update_time": 1779012079,
|
||||
"_id": "Tax Control System Details<SEP>VAT Special Invoice"
|
||||
},
|
||||
"Operating Department Individual<SEP>Procurement Management Regulations": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012081,
|
||||
"update_time": 1779012081,
|
||||
"_id": "Operating Department Individual<SEP>Procurement Management Regulations"
|
||||
},
|
||||
"Business Original Documents<SEP>Operator": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012094,
|
||||
"update_time": 1779012094,
|
||||
"_id": "Business Original Documents<SEP>Operator"
|
||||
},
|
||||
"Expenditure Reimbursement Application<SEP>Tax Authority Recognized Invoice": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012094,
|
||||
"update_time": 1779012094,
|
||||
"_id": "Expenditure Reimbursement Application<SEP>Tax Authority Recognized Invoice"
|
||||
},
|
||||
"公司<SEP>第十七条": {
|
||||
"chunk_ids": [
|
||||
"chunk-e9438f69c9e221d9f0f00a05ad84eac6"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012094,
|
||||
"update_time": 1779012094,
|
||||
"_id": "公司<SEP>第十七条"
|
||||
},
|
||||
"Operator<SEP>Three Working Days Deadline": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012083,
|
||||
"update_time": 1779012083,
|
||||
"_id": "Operator<SEP>Three Working Days Deadline"
|
||||
},
|
||||
"Departments And Units<SEP>Night High-Speed Rail Provision": {
|
||||
"chunk_ids": [
|
||||
"chunk-613d6dfd4c5e9c807229a3147f96b584"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012084,
|
||||
"update_time": 1779012084,
|
||||
"_id": "Departments And Units<SEP>Night High-Speed Rail Provision"
|
||||
},
|
||||
"公司<SEP>第十八条": {
|
||||
"chunk_ids": [
|
||||
"chunk-e9438f69c9e221d9f0f00a05ad84eac6"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012084,
|
||||
"update_time": 1779012084,
|
||||
"_id": "公司<SEP>第十八条"
|
||||
},
|
||||
"公司<SEP>第十九条": {
|
||||
"chunk_ids": [
|
||||
"chunk-e9438f69c9e221d9f0f00a05ad84eac6"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012084,
|
||||
"update_time": 1779012084,
|
||||
"_id": "公司<SEP>第十九条"
|
||||
},
|
||||
"报销标准变化情况<SEP>远光软件股份有限公司": {
|
||||
"chunk_ids": [
|
||||
"chunk-18d968b78afe916b419c1b5973421ebe"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012084,
|
||||
"update_time": 1779012084,
|
||||
"_id": "报销标准变化情况<SEP>远光软件股份有限公司"
|
||||
},
|
||||
"取消报销规定内容<SEP>报销标准变化情况": {
|
||||
"chunk_ids": [
|
||||
"chunk-18d968b78afe916b419c1b5973421ebe"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012085,
|
||||
"update_time": 1779012085,
|
||||
"_id": "取消报销规定内容<SEP>报销标准变化情况"
|
||||
},
|
||||
"Financial Review<SEP>Operator": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012085,
|
||||
"update_time": 1779012085,
|
||||
"_id": "Financial Review<SEP>Operator"
|
||||
},
|
||||
"公司支出管理办法(2024)<SEP>远光软件股份有限公司": {
|
||||
"chunk_ids": [
|
||||
"chunk-dd87aa5bc62cc9587ecb4c26d35a5263"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012085,
|
||||
"update_time": 1779012085,
|
||||
"_id": "公司支出管理办法(2024)<SEP>远光软件股份有限公司"
|
||||
},
|
||||
"远光制度〔2024〕14号<SEP>远光软件股份有限公司": {
|
||||
"chunk_ids": [
|
||||
"chunk-dd87aa5bc62cc9587ecb4c26d35a5263"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012086,
|
||||
"update_time": 1779012086,
|
||||
"_id": "远光制度〔2024〕14号<SEP>远光软件股份有限公司"
|
||||
},
|
||||
"Departments And Units<SEP>Taxi Usage Regulations": {
|
||||
"chunk_ids": [
|
||||
"chunk-613d6dfd4c5e9c807229a3147f96b584"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012099,
|
||||
"update_time": 1779012099,
|
||||
"_id": "Departments And Units<SEP>Taxi Usage Regulations"
|
||||
},
|
||||
"控股子公司<SEP>计划财务部": {
|
||||
"chunk_ids": [
|
||||
"chunk-dd87aa5bc62cc9587ecb4c26d35a5263"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012099,
|
||||
"update_time": 1779012099,
|
||||
"_id": "控股子公司<SEP>计划财务部"
|
||||
},
|
||||
"公司<SEP>第二十条": {
|
||||
"chunk_ids": [
|
||||
"chunk-e9438f69c9e221d9f0f00a05ad84eac6"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012086,
|
||||
"update_time": 1779012086,
|
||||
"_id": "公司<SEP>第二十条"
|
||||
},
|
||||
"商旅系统<SEP>差旅费": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012086,
|
||||
"update_time": 1779012086,
|
||||
"_id": "商旅系统<SEP>差旅费"
|
||||
},
|
||||
"业务招待费<SEP>差旅费": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012089,
|
||||
"update_time": 1779012089,
|
||||
"_id": "业务招待费<SEP>差旅费"
|
||||
},
|
||||
"公司<SEP>第二十一条": {
|
||||
"chunk_ids": [
|
||||
"chunk-e9438f69c9e221d9f0f00a05ad84eac6"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012089,
|
||||
"update_time": 1779012089,
|
||||
"_id": "公司<SEP>第二十一条"
|
||||
},
|
||||
"广告宣传费<SEP>第十六条": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012089,
|
||||
"update_time": 1779012089,
|
||||
"_id": "广告宣传费<SEP>第十六条"
|
||||
},
|
||||
"组织人事部<SEP>调动工作": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012090,
|
||||
"update_time": 1779012090,
|
||||
"_id": "组织人事部<SEP>调动工作"
|
||||
},
|
||||
"会议费<SEP>差旅费": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012092,
|
||||
"update_time": 1779012092,
|
||||
"_id": "会议费<SEP>差旅费"
|
||||
},
|
||||
"业务招待费<SEP>第十四条": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012092,
|
||||
"update_time": 1779012092,
|
||||
"_id": "业务招待费<SEP>第十四条"
|
||||
},
|
||||
"会议费<SEP>第十五条": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012092,
|
||||
"update_time": 1779012092,
|
||||
"_id": "会议费<SEP>第十五条"
|
||||
},
|
||||
"会议费<SEP>公司总裁": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012093,
|
||||
"update_time": 1779012093,
|
||||
"_id": "会议费<SEP>公司总裁"
|
||||
}
|
||||
{
|
||||
"第一章总则<SEP>远光软件股份有限公司": {
|
||||
"chunk_ids": [
|
||||
"chunk-aa5435156b829944c173fa1d2d7a93d4"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012088,
|
||||
"update_time": 1779012088,
|
||||
"_id": "第一章总则<SEP>远光软件股份有限公司"
|
||||
},
|
||||
"第十一条备用金借款<SEP>第四章重点支出管理规定": {
|
||||
"chunk_ids": [
|
||||
"chunk-aa5435156b829944c173fa1d2d7a93d4"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012088,
|
||||
"update_time": 1779012088,
|
||||
"_id": "第十一条备用金借款<SEP>第四章重点支出管理规定"
|
||||
},
|
||||
"公司支出管理办法<SEP>办公室(党委办公室)": {
|
||||
"chunk_ids": [
|
||||
"chunk-afc57a0e9548d1f484da6df6c182676b"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012088,
|
||||
"update_time": 1779012088,
|
||||
"_id": "公司支出管理办法<SEP>办公室(党委办公室)"
|
||||
},
|
||||
"计划财务部<SEP>远光软件股份有限公司": {
|
||||
"chunk_ids": [
|
||||
"chunk-aa5435156b829944c173fa1d2d7a93d4"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012076,
|
||||
"update_time": 1779012076,
|
||||
"_id": "计划财务部<SEP>远光软件股份有限公司"
|
||||
},
|
||||
"第一章总则<SEP>第三条管理原则": {
|
||||
"chunk_ids": [
|
||||
"chunk-aa5435156b829944c173fa1d2d7a93d4"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012076,
|
||||
"update_time": 1779012076,
|
||||
"_id": "第一章总则<SEP>第三条管理原则"
|
||||
},
|
||||
"Company<SEP>Management Personnel At All Levels": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012076,
|
||||
"update_time": 1779012076,
|
||||
"_id": "Company<SEP>Management Personnel At All Levels"
|
||||
},
|
||||
"Centralized Management department<SEP>Company": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012077,
|
||||
"update_time": 1779012077,
|
||||
"_id": "Centralized Management department<SEP>Company"
|
||||
},
|
||||
"Company<SEP>Planning and Finance Department": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012077,
|
||||
"update_time": 1779012077,
|
||||
"_id": "Company<SEP>Planning and Finance Department"
|
||||
},
|
||||
"Company<SEP>Operating Department Individual": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012078,
|
||||
"update_time": 1779012078,
|
||||
"_id": "Company<SEP>Operating Department Individual"
|
||||
},
|
||||
"公司支出管理办法<SEP>工会委员会": {
|
||||
"chunk_ids": [
|
||||
"chunk-afc57a0e9548d1f484da6df6c182676b"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012079,
|
||||
"update_time": 1779012079,
|
||||
"_id": "公司支出管理办法<SEP>工会委员会"
|
||||
},
|
||||
"Expenditure Reimbursement Application<SEP>Operator": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012079,
|
||||
"update_time": 1779012079,
|
||||
"_id": "Expenditure Reimbursement Application<SEP>Operator"
|
||||
},
|
||||
"公司支出管理办法<SEP>营销中心": {
|
||||
"chunk_ids": [
|
||||
"chunk-afc57a0e9548d1f484da6df6c182676b"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012079,
|
||||
"update_time": 1779012079,
|
||||
"_id": "公司支出管理办法<SEP>营销中心"
|
||||
},
|
||||
"第四条归口管理部门主要职责<SEP>计划财务部": {
|
||||
"chunk_ids": [
|
||||
"chunk-aa5435156b829944c173fa1d2d7a93d4"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012079,
|
||||
"update_time": 1779012079,
|
||||
"_id": "第四条归口管理部门主要职责<SEP>计划财务部"
|
||||
},
|
||||
"Tax Control System Details<SEP>VAT Special Invoice": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012079,
|
||||
"update_time": 1779012079,
|
||||
"_id": "Tax Control System Details<SEP>VAT Special Invoice"
|
||||
},
|
||||
"Operating Department Individual<SEP>Procurement Management Regulations": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012081,
|
||||
"update_time": 1779012081,
|
||||
"_id": "Operating Department Individual<SEP>Procurement Management Regulations"
|
||||
},
|
||||
"Business Original Documents<SEP>Operator": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012094,
|
||||
"update_time": 1779012094,
|
||||
"_id": "Business Original Documents<SEP>Operator"
|
||||
},
|
||||
"Expenditure Reimbursement Application<SEP>Tax Authority Recognized Invoice": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012094,
|
||||
"update_time": 1779012094,
|
||||
"_id": "Expenditure Reimbursement Application<SEP>Tax Authority Recognized Invoice"
|
||||
},
|
||||
"公司<SEP>第十七条": {
|
||||
"chunk_ids": [
|
||||
"chunk-e9438f69c9e221d9f0f00a05ad84eac6"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012094,
|
||||
"update_time": 1779012094,
|
||||
"_id": "公司<SEP>第十七条"
|
||||
},
|
||||
"Operator<SEP>Three Working Days Deadline": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012083,
|
||||
"update_time": 1779012083,
|
||||
"_id": "Operator<SEP>Three Working Days Deadline"
|
||||
},
|
||||
"Departments And Units<SEP>Night High-Speed Rail Provision": {
|
||||
"chunk_ids": [
|
||||
"chunk-613d6dfd4c5e9c807229a3147f96b584"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012084,
|
||||
"update_time": 1779012084,
|
||||
"_id": "Departments And Units<SEP>Night High-Speed Rail Provision"
|
||||
},
|
||||
"公司<SEP>第十八条": {
|
||||
"chunk_ids": [
|
||||
"chunk-e9438f69c9e221d9f0f00a05ad84eac6"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012084,
|
||||
"update_time": 1779012084,
|
||||
"_id": "公司<SEP>第十八条"
|
||||
},
|
||||
"公司<SEP>第十九条": {
|
||||
"chunk_ids": [
|
||||
"chunk-e9438f69c9e221d9f0f00a05ad84eac6"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012084,
|
||||
"update_time": 1779012084,
|
||||
"_id": "公司<SEP>第十九条"
|
||||
},
|
||||
"报销标准变化情况<SEP>远光软件股份有限公司": {
|
||||
"chunk_ids": [
|
||||
"chunk-18d968b78afe916b419c1b5973421ebe"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012084,
|
||||
"update_time": 1779012084,
|
||||
"_id": "报销标准变化情况<SEP>远光软件股份有限公司"
|
||||
},
|
||||
"取消报销规定内容<SEP>报销标准变化情况": {
|
||||
"chunk_ids": [
|
||||
"chunk-18d968b78afe916b419c1b5973421ebe"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012085,
|
||||
"update_time": 1779012085,
|
||||
"_id": "取消报销规定内容<SEP>报销标准变化情况"
|
||||
},
|
||||
"Financial Review<SEP>Operator": {
|
||||
"chunk_ids": [
|
||||
"chunk-74c01decac4a10cd40a491786743b0ee"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012085,
|
||||
"update_time": 1779012085,
|
||||
"_id": "Financial Review<SEP>Operator"
|
||||
},
|
||||
"公司支出管理办法(2024)<SEP>远光软件股份有限公司": {
|
||||
"chunk_ids": [
|
||||
"chunk-dd87aa5bc62cc9587ecb4c26d35a5263"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012085,
|
||||
"update_time": 1779012085,
|
||||
"_id": "公司支出管理办法(2024)<SEP>远光软件股份有限公司"
|
||||
},
|
||||
"远光制度〔2024〕14号<SEP>远光软件股份有限公司": {
|
||||
"chunk_ids": [
|
||||
"chunk-dd87aa5bc62cc9587ecb4c26d35a5263"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012086,
|
||||
"update_time": 1779012086,
|
||||
"_id": "远光制度〔2024〕14号<SEP>远光软件股份有限公司"
|
||||
},
|
||||
"Departments And Units<SEP>Taxi Usage Regulations": {
|
||||
"chunk_ids": [
|
||||
"chunk-613d6dfd4c5e9c807229a3147f96b584"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012099,
|
||||
"update_time": 1779012099,
|
||||
"_id": "Departments And Units<SEP>Taxi Usage Regulations"
|
||||
},
|
||||
"控股子公司<SEP>计划财务部": {
|
||||
"chunk_ids": [
|
||||
"chunk-dd87aa5bc62cc9587ecb4c26d35a5263"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012099,
|
||||
"update_time": 1779012099,
|
||||
"_id": "控股子公司<SEP>计划财务部"
|
||||
},
|
||||
"公司<SEP>第二十条": {
|
||||
"chunk_ids": [
|
||||
"chunk-e9438f69c9e221d9f0f00a05ad84eac6"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012086,
|
||||
"update_time": 1779012086,
|
||||
"_id": "公司<SEP>第二十条"
|
||||
},
|
||||
"商旅系统<SEP>差旅费": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012086,
|
||||
"update_time": 1779012086,
|
||||
"_id": "商旅系统<SEP>差旅费"
|
||||
},
|
||||
"业务招待费<SEP>差旅费": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012089,
|
||||
"update_time": 1779012089,
|
||||
"_id": "业务招待费<SEP>差旅费"
|
||||
},
|
||||
"公司<SEP>第二十一条": {
|
||||
"chunk_ids": [
|
||||
"chunk-e9438f69c9e221d9f0f00a05ad84eac6"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012089,
|
||||
"update_time": 1779012089,
|
||||
"_id": "公司<SEP>第二十一条"
|
||||
},
|
||||
"广告宣传费<SEP>第十六条": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012089,
|
||||
"update_time": 1779012089,
|
||||
"_id": "广告宣传费<SEP>第十六条"
|
||||
},
|
||||
"组织人事部<SEP>调动工作": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012090,
|
||||
"update_time": 1779012090,
|
||||
"_id": "组织人事部<SEP>调动工作"
|
||||
},
|
||||
"会议费<SEP>差旅费": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012092,
|
||||
"update_time": 1779012092,
|
||||
"_id": "会议费<SEP>差旅费"
|
||||
},
|
||||
"业务招待费<SEP>第十四条": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012092,
|
||||
"update_time": 1779012092,
|
||||
"_id": "业务招待费<SEP>第十四条"
|
||||
},
|
||||
"会议费<SEP>第十五条": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012092,
|
||||
"update_time": 1779012092,
|
||||
"_id": "会议费<SEP>第十五条"
|
||||
},
|
||||
"会议费<SEP>公司总裁": {
|
||||
"chunk_ids": [
|
||||
"chunk-d26b288ed4001dc5c504dce0eb841362"
|
||||
],
|
||||
"count": 1,
|
||||
"create_time": 1779012093,
|
||||
"update_time": 1779012093,
|
||||
"_id": "会议费<SEP>公司总裁"
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -15,9 +15,8 @@ def test_rule_spreadsheet_onlyoffice_key_uses_safe_characters() -> None:
|
||||
|
||||
key = AgentAssetService._build_onlyoffice_document_key(
|
||||
"asset:id",
|
||||
"v1.0.0",
|
||||
metadata,
|
||||
)
|
||||
|
||||
assert key == "asset_id-v1.0.0-abc123"
|
||||
assert key == "asset_id-abc123"
|
||||
assert ":" not in key
|
||||
|
||||
@@ -310,7 +310,7 @@ def test_restore_version_creates_new_working_copy_without_rewriting_published_ve
|
||||
assert restored.current_version_change_note == "基于历史版本 v1.0.0 恢复生成工作稿"
|
||||
|
||||
|
||||
def test_spreadsheet_version_compare_returns_sheet_and_cell_changes() -> None:
|
||||
def test_spreadsheet_upload_records_sheet_and_cell_changes_without_versions() -> None:
|
||||
with build_session() as db:
|
||||
service = AgentAssetService(db)
|
||||
rule = next(
|
||||
@@ -322,34 +322,33 @@ def test_spreadsheet_version_compare_returns_sheet_and_cell_changes() -> None:
|
||||
service.upload_rule_spreadsheet(
|
||||
rule.id,
|
||||
filename="公司差旅费报销规则.xlsx",
|
||||
content=build_workbook_bytes([["城市", "住宿"], ["北京", 500]]),
|
||||
content=build_workbook_bytes([["城市", "住宿"], ["北京", 500]]),
|
||||
actor="finance_user",
|
||||
)
|
||||
base_version = service.get_asset(rule.id).working_version # type: ignore[union-attr]
|
||||
service.upload_rule_spreadsheet(
|
||||
rule.id,
|
||||
filename="公司差旅费报销规则.xlsx",
|
||||
content=build_workbook_bytes([["城市", "住宿"], ["北京", 550], ["武汉", 450]]),
|
||||
actor="finance_user",
|
||||
)
|
||||
target_version = service.get_asset(rule.id).working_version # type: ignore[union-attr]
|
||||
|
||||
diff = service.compare_spreadsheet_versions(
|
||||
rule.id,
|
||||
base_version=base_version or "",
|
||||
target_version=target_version or "",
|
||||
)
|
||||
records = service.list_spreadsheet_change_records(rule.id)
|
||||
latest = records[0]
|
||||
|
||||
assert diff.changed_sheet_count == 1
|
||||
assert diff.changed_cell_count == 3
|
||||
assert latest.changed_sheet_count == 1
|
||||
assert latest.changed_cell_count == 3
|
||||
assert any(
|
||||
item.cell == "B2" and item.change_type == "modified"
|
||||
for item in diff.cell_changes
|
||||
for item in latest.cell_changes
|
||||
)
|
||||
assert any(item.cell == "A3" and item.change_type == "added" for item in diff.cell_changes)
|
||||
assert any(
|
||||
item.cell == "A3" and item.change_type == "added"
|
||||
for item in latest.cell_changes
|
||||
)
|
||||
assert not hasattr(latest, "version")
|
||||
|
||||
|
||||
def test_working_spreadsheet_version_reads_immutable_snapshot_instead_of_live_copy() -> None:
|
||||
def test_spreadsheet_content_reads_current_rule_file_without_version_snapshot() -> None:
|
||||
with build_session() as db:
|
||||
service = AgentAssetService(db)
|
||||
rule = next(
|
||||
@@ -366,7 +365,6 @@ def test_working_spreadsheet_version_reads_immutable_snapshot_instead_of_live_co
|
||||
)
|
||||
detail = service.get_asset(rule.id)
|
||||
assert detail is not None
|
||||
working_version = detail.working_version or ""
|
||||
|
||||
current_asset = service.repository.get(rule.id)
|
||||
assert current_asset is not None
|
||||
@@ -375,23 +373,13 @@ def test_working_spreadsheet_version_reads_immutable_snapshot_instead_of_live_co
|
||||
assert "agent_assets" not in live_storage_key
|
||||
live_path = service.spreadsheet_manager.resolve_storage_path(live_storage_key)
|
||||
assert not service.spreadsheet_manager.asset_root.exists()
|
||||
original_live_bytes = live_path.read_bytes()
|
||||
try:
|
||||
live_path.write_bytes(build_workbook_bytes([["城市", "住宿"], ["北京", 999]]))
|
||||
|
||||
snapshot_path, _, _ = service.get_rule_spreadsheet_content(
|
||||
rule.id,
|
||||
version=working_version,
|
||||
)
|
||||
current_path, _, _ = service.get_rule_spreadsheet_content(rule.id)
|
||||
|
||||
assert snapshot_path != live_path
|
||||
assert FINANCE_RULES_LIBRARY in snapshot_path.parts
|
||||
assert ".versions" in snapshot_path.parts
|
||||
assert "agent_assets" not in snapshot_path.parts
|
||||
workbook = load_workbook(snapshot_path, data_only=False)
|
||||
assert workbook.active["B2"].value == 500
|
||||
finally:
|
||||
live_path.write_bytes(original_live_bytes)
|
||||
assert current_path == live_path
|
||||
assert ".versions" not in current_path.parts
|
||||
workbook = load_workbook(current_path, data_only=False)
|
||||
assert workbook.active["B2"].value == 500
|
||||
|
||||
|
||||
def test_spreadsheet_change_records_return_recent_edit_details() -> None:
|
||||
@@ -454,7 +442,6 @@ def test_spreadsheet_change_records_include_all_modified_sheets() -> None:
|
||||
)
|
||||
detail = service.get_asset(rule.id)
|
||||
assert detail is not None
|
||||
first_version = detail.working_version
|
||||
|
||||
service.upload_rule_spreadsheet(
|
||||
rule.id,
|
||||
@@ -473,7 +460,7 @@ def test_spreadsheet_change_records_include_all_modified_sheets() -> None:
|
||||
changed_sheets = {item.sheet_name for item in latest.sheet_changes}
|
||||
changed_cell_sheets = {item.sheet_name for item in latest.cell_changes}
|
||||
|
||||
assert latest.version != first_version
|
||||
assert not hasattr(latest, "version")
|
||||
assert latest.changed_sheet_count == 2
|
||||
assert {"差旅标准", "填表说明"}.issubset(changed_sheets)
|
||||
assert {"差旅标准", "填表说明"}.issubset(changed_cell_sheets)
|
||||
@@ -513,6 +500,8 @@ def test_editable_spreadsheet_onlyoffice_config_enables_forcesave(monkeypatch) -
|
||||
customization = config.config["editorConfig"]["customization"]
|
||||
assert config.config["editorConfig"]["mode"] == "edit"
|
||||
assert customization["forcesave"] is True
|
||||
assert "version=" not in config.config["document"]["url"]
|
||||
assert "version=" not in config.config["editorConfig"]["callbackUrl"]
|
||||
|
||||
|
||||
def test_version_timeline_contains_created_review_and_publish_events() -> None:
|
||||
|
||||
@@ -1,98 +1,98 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.api.deps import get_db
|
||||
from app.core.agent_enums import AgentAssetStatus
|
||||
from app.db.base import Base
|
||||
from app.main import create_app
|
||||
from app.services.agent_assets import AgentAssetService
|
||||
|
||||
|
||||
def build_client() -> tuple[TestClient, sessionmaker[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)
|
||||
|
||||
app = create_app()
|
||||
|
||||
def override_db() -> Generator[Session, None, None]:
|
||||
db = session_factory()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
app.dependency_overrides[get_db] = override_db
|
||||
return TestClient(app), session_factory
|
||||
|
||||
|
||||
def test_list_agent_assets_endpoint_returns_seeded_items() -> None:
|
||||
client, _ = build_client()
|
||||
|
||||
response = client.get("/api/v1/agent-assets", params={"asset_type": "rule"})
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload
|
||||
assert all(item["asset_type"] == "rule" for item in payload)
|
||||
assert any(item["code"] == "rule.expense.travel_risk_control_standard" for item in payload)
|
||||
|
||||
|
||||
def test_get_agent_asset_detail_endpoint_returns_version_history() -> None:
|
||||
client, _ = build_client()
|
||||
|
||||
list_response = client.get("/api/v1/agent-assets", params={"asset_type": "rule"})
|
||||
asset_id = next(
|
||||
item["id"]
|
||||
for item in list_response.json()
|
||||
if item["code"] == "rule.expense.travel_risk_control_standard"
|
||||
)
|
||||
|
||||
response = client.get(f"/api/v1/agent-assets/{asset_id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["recent_versions"]
|
||||
assert payload["current_version_content_type"] == "markdown"
|
||||
assert payload["current_version"] == "v1.1.0"
|
||||
assert "行程闭环" in payload["current_version_content"]
|
||||
|
||||
|
||||
def test_activate_pending_rule_endpoint_is_blocked() -> None:
|
||||
client, session_factory = build_client()
|
||||
|
||||
with session_factory() as db:
|
||||
pending_rule = next(
|
||||
item
|
||||
for item in AgentAssetService(db).list_assets(asset_type="rule")
|
||||
if item.status == AgentAssetStatus.REVIEW.value
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
f"/api/v1/agent-assets/{pending_rule.id}/activate",
|
||||
headers={"x-actor": "pytest"},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "审核" in response.json()["detail"]
|
||||
|
||||
|
||||
def test_list_audit_logs_endpoint_returns_seeded_logs() -> None:
|
||||
client, _ = build_client()
|
||||
|
||||
response = client.get("/api/v1/audit-logs")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload
|
||||
assert any(item["action"] == "review_rule" for item in payload)
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.api.deps import get_db
|
||||
from app.core.agent_enums import AgentAssetStatus
|
||||
from app.db.base import Base
|
||||
from app.main import create_app
|
||||
from app.services.agent_assets import AgentAssetService
|
||||
|
||||
|
||||
def build_client() -> tuple[TestClient, sessionmaker[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)
|
||||
|
||||
app = create_app()
|
||||
|
||||
def override_db() -> Generator[Session, None, None]:
|
||||
db = session_factory()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
app.dependency_overrides[get_db] = override_db
|
||||
return TestClient(app), session_factory
|
||||
|
||||
|
||||
def test_list_agent_assets_endpoint_returns_seeded_items() -> None:
|
||||
client, _ = build_client()
|
||||
|
||||
response = client.get("/api/v1/agent-assets", params={"asset_type": "rule"})
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload
|
||||
assert all(item["asset_type"] == "rule" for item in payload)
|
||||
assert any(item["code"] == "rule.expense.travel_risk_control_standard" for item in payload)
|
||||
|
||||
|
||||
def test_get_agent_asset_detail_endpoint_returns_version_history() -> None:
|
||||
client, _ = build_client()
|
||||
|
||||
list_response = client.get("/api/v1/agent-assets", params={"asset_type": "rule"})
|
||||
asset_id = next(
|
||||
item["id"]
|
||||
for item in list_response.json()
|
||||
if item["code"] == "rule.expense.travel_risk_control_standard"
|
||||
)
|
||||
|
||||
response = client.get(f"/api/v1/agent-assets/{asset_id}")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["recent_versions"]
|
||||
assert payload["current_version_content_type"] == "markdown"
|
||||
assert payload["current_version"] == "v1.1.0"
|
||||
assert "行程闭环" in payload["current_version_content"]
|
||||
|
||||
|
||||
def test_activate_pending_rule_endpoint_is_blocked() -> None:
|
||||
client, session_factory = build_client()
|
||||
|
||||
with session_factory() as db:
|
||||
pending_rule = next(
|
||||
item
|
||||
for item in AgentAssetService(db).list_assets(asset_type="rule")
|
||||
if item.status == AgentAssetStatus.REVIEW.value
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
f"/api/v1/agent-assets/{pending_rule.id}/activate",
|
||||
headers={"x-actor": "pytest"},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "审核" in response.json()["detail"]
|
||||
|
||||
|
||||
def test_list_audit_logs_endpoint_returns_seeded_logs() -> None:
|
||||
client, _ = build_client()
|
||||
|
||||
response = client.get("/api/v1/audit-logs")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload
|
||||
assert any(item["action"] == "review_rule" for item in payload)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user