From 5b388d08c010f6f3c6181d43c400ebd7e170937c Mon Sep 17 00:00:00 2001 From: caoxiaozhu Date: Fri, 22 May 2026 23:47:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E5=BA=93=E7=B4=A2=E5=BC=95=E4=B8=8E=E8=AE=BE=E7=BD=AE=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=A8=A1=E5=9D=97=E5=8C=96=E6=8B=86=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 扩展知识库索引任务和 RAG 检索支持增量入库和文档去重,优 化本体检测和规则匹配精度,前端设置页面拆分为 LLM、邮件 和 Hermes 员工同步子面板并重构样式,新增日志详情组件和 知识入库日志模型,补充单元测试覆盖。 --- server/src/app/schemas/user_agent.py | 2 +- .../app/services/expense_claim_constants.py | 55 +- .../expense_claim_ontology_resolvers.py | 22 +- .../src/app/services/expense_rule_runtime.py | 6 +- .../services/expense_rule_runtime_defaults.py | 13 +- .../src/app/services/expense_type_keywords.py | 245 +++ .../src/app/services/knowledge_index_tasks.py | 549 ++++++- .../src/app/services/knowledge_ingest_log.py | 224 +++ server/src/app/services/knowledge_rag.py | 145 +- server/src/app/services/ontology.py | 45 + server/src/app/services/ontology_detection.py | 76 +- .../src/app/services/ontology_extraction.py | 41 +- server/src/app/services/ontology_rules.py | 40 +- .../services/orchestrator_expense_query.py | 6 +- .../src/app/services/user_agent_constants.py | 20 +- .../src/app/services/user_agent_documents.py | 98 +- .../app/services/user_agent_review_core.py | 24 +- .../app/services/user_agent_review_profile.py | 83 +- .../app/services/user_agent_review_slots.py | 25 +- .../user_agent_review_travel_policy.py | 36 +- server/storage/knowledge/.index.json | 42 +- .../graph_chunk_entity_relation.graphml | 1292 +++++++++++++++- .../kv_store_doc_status.json | 70 + .../kv_store_entity_chunks.json | 920 +++++++++++- .../kv_store_full_docs.json | 28 + .../kv_store_full_entities.json | 148 ++ .../kv_store_full_relations.json | 200 +++ .../kv_store_relation_chunks.json | 378 +++++ .../kv_store_text_chunks.json | 66 + server/tests/test_expense_claim_service.py | 43 + server/tests/test_knowledge_rag_service.py | 146 +- server/tests/test_ontology_service.py | 41 +- server/tests/test_user_agent_service.py | 83 +- .../styles/views/settings-view-form.css | 227 +++ .../styles/views/settings-view-hermes.css | 228 +++ web/src/assets/styles/views/settings-view.css | 1315 ++++++++--------- ...travel-reimbursement-create-view-part2.css | 24 +- ...travel-reimbursement-create-view-part3.css | 10 + ...travel-reimbursement-create-view-part4.css | 54 +- .../travel-reimbursement-create-view.css | 46 + web/src/components/layout/SidebarRail.vue | 22 +- .../logs/KnowledgeIngestRunPanel.vue | 727 +++++++++ web/src/composables/useNavigation.js | 45 +- web/src/composables/useRequests.js | 4 +- web/src/composables/useSettings.js | 486 ++++++ web/src/composables/useSystemState.js | 6 +- web/src/data/requests.js | 4 +- web/src/utils/expenseAssistantActions.js | 8 +- web/src/utils/hermesEmployeeSettingsModel.js | 150 ++ web/src/utils/knowledgeIngestLogModel.js | 315 ++++ web/src/utils/markdown.js | 28 +- web/src/utils/reimbursementTextInference.js | 51 +- web/src/utils/requestViewModel.js | 4 +- web/src/utils/settingsModelHelper.js | 461 ++++++ web/src/views/AppShellRouteView.vue | 1 + web/src/views/HermesEmployeeSettingsPanel.vue | 136 ++ web/src/views/LlmSettingsPanel.vue | 309 ++++ web/src/views/LogDetailView.vue | 14 +- web/src/views/MailSettingsPanel.vue | 163 ++ web/src/views/SettingsView.vue | 648 +++----- .../scripts/HermesEmployeeSettingsPanel.js | 59 + web/src/views/scripts/LlmSettingsPanel.js | 194 +++ web/src/views/scripts/MailSettingsPanel.js | 20 + web/src/views/scripts/SettingsView.js | 923 +----------- .../scripts/TravelReimbursementCreateView.js | 148 +- .../views/scripts/TravelRequestDetailView.js | 5 +- .../travelReimbursementAttachmentModel.js | 53 +- .../travelReimbursementConversationModel.js | 6 + .../travelReimbursementExpenseQueryModel.js | 2 +- .../travelReimbursementReviewConstants.js | 13 +- .../scripts/travelReimbursementReviewModel.js | 58 +- .../travelRequestDetailExpenseModel.js | 14 +- .../scripts/travelRequestDetailInsights.js | 1 + .../useTravelReimbursementAttachments.js | 8 +- .../scripts/useTravelReimbursementFlow.js | 5 +- .../useTravelReimbursementReviewDrawer.js | 10 +- .../useTravelReimbursementSessionState.js | 9 +- .../useTravelReimbursementSubmitComposer.js | 107 +- ...tachment-association-confirmation.test.mjs | 77 +- web/tests/knowledge-ingest-log-model.test.mjs | 117 ++ .../navigation-route-resolution.test.mjs | 32 + web/tests/reimbursementTextInference.test.mjs | 13 + ...eimbursement-review-drawer-switch.test.mjs | 187 ++- ...travel-request-detail-risk-advice.test.mjs | 10 +- 84 files changed, 10170 insertions(+), 2599 deletions(-) create mode 100644 server/src/app/services/expense_type_keywords.py create mode 100644 server/src/app/services/knowledge_ingest_log.py create mode 100644 web/src/assets/styles/views/settings-view-form.css create mode 100644 web/src/assets/styles/views/settings-view-hermes.css create mode 100644 web/src/components/logs/KnowledgeIngestRunPanel.vue create mode 100644 web/src/composables/useSettings.js create mode 100644 web/src/utils/hermesEmployeeSettingsModel.js create mode 100644 web/src/utils/knowledgeIngestLogModel.js create mode 100644 web/src/utils/settingsModelHelper.js create mode 100644 web/src/views/HermesEmployeeSettingsPanel.vue create mode 100644 web/src/views/LlmSettingsPanel.vue create mode 100644 web/src/views/MailSettingsPanel.vue create mode 100644 web/src/views/scripts/HermesEmployeeSettingsPanel.js create mode 100644 web/src/views/scripts/LlmSettingsPanel.js create mode 100644 web/src/views/scripts/MailSettingsPanel.js create mode 100644 web/tests/knowledge-ingest-log-model.test.mjs create mode 100644 web/tests/navigation-route-resolution.test.mjs diff --git a/server/src/app/schemas/user_agent.py b/server/src/app/schemas/user_agent.py index f11b5dc..ca2a4f2 100644 --- a/server/src/app/schemas/user_agent.py +++ b/server/src/app/schemas/user_agent.py @@ -120,7 +120,7 @@ class UserAgentReviewDocumentCard(BaseModel): filename: str = Field(description="原始文件名。") document_type: str = Field(default="other", description="票据候选类型。") suggested_expense_type: str = Field(default="other", description="建议归属费用类型。") - scene_label: str = Field(default="", description="面向用户展示的场景标签。") + scene_label: str = Field(default="", description="面向用户展示的票据类型标签。") summary: str = Field(default="", description="逐票据摘要。") avg_score: float = Field(default=0.0, ge=0.0, le=1.0, description="OCR 平均得分。") preview_kind: str = Field(default="", description="票据预览类型,例如 image。") diff --git a/server/src/app/services/expense_claim_constants.py b/server/src/app/services/expense_claim_constants.py index 51039e0..745b118 100644 --- a/server/src/app/services/expense_claim_constants.py +++ b/server/src/app/services/expense_claim_constants.py @@ -3,6 +3,8 @@ from __future__ import annotations import re from decimal import Decimal +from app.services.expense_type_keywords import iter_expense_keywords + EXPENSE_TYPE_LABELS = { "travel": "差旅", "train_ticket": "火车票", @@ -12,10 +14,10 @@ EXPENSE_TYPE_LABELS = { "travel_allowance": "出差补贴", "hotel": "住宿", "transport": "交通", - "meal": "餐费", + "meal": "业务招待", "meeting": "会务", "entertainment": "招待", - "office": "办公", + "office": "办公用品", "training": "培训", "communication": "通讯", "welfare": "福利", @@ -131,40 +133,19 @@ DOCUMENT_ROUTE_DESTINATION_LABELS = { GENERIC_ATTACHMENT_BACKFILL_ITEM_TYPES = {"", "other", "travel", "transport", "hotel"} LOCATION_REQUIRED_EXPENSE_TYPES = {"travel", "meeting", "entertainment"} EXPENSE_SCENE_KEYWORDS = { - "travel": ("差旅", "出差", "行程"), - "hotel": ("酒店", "住宿", "房费", "客房", "入住", "离店"), - "transport": ( - "交通", - "打车", - "出租车", - "网约车", - "滴滴", - "出行", - "乘车", - "用车", - "叫车", - "车费", - "车资", - "的士", - "高铁", - "动车", - "火车", - "机票", - "航班", - "行程单", - "登机", - "客票", - "公交", - "地铁", - "过路费", - "通行费", - "停车", - ), - "meal": ("餐饮", "餐费", "用餐", "外卖", "快餐", "酒楼", "饭店", "饭馆", "食品", "咖啡"), - "entertainment": ("招待", "宴请", "接待", "客户餐", "商务餐", "业务招待"), - "office": ("办公", "办公用品", "文具", "耗材", "打印", "纸张", "硒鼓", "墨盒", "鼠标", "键盘", "电脑"), - "meeting": ("会议", "会务", "会展", "会议室", "会场", "场地费", "论坛"), - "training": ("培训", "课程", "讲师", "教材", "学费", "认证"), + code: tuple(iter_expense_keywords(code)) + for code in ( + "travel", + "hotel", + "transport", + "meal", + "entertainment", + "office", + "meeting", + "training", + "communication", + "welfare", + ) } EXPENSE_TYPE_ALLOWED_DOCUMENT_SCENES = { "travel": {"travel", "hotel", "transport", "meal"}, @@ -185,7 +166,7 @@ DOCUMENT_SCENE_LABELS = { "travel": "差旅", "hotel": "住宿", "transport": "交通", - "meal": "餐饮", + "meal": "业务招待", "entertainment": "业务招待", "office": "办公用品", "meeting": "会务", diff --git a/server/src/app/services/expense_claim_ontology_resolvers.py b/server/src/app/services/expense_claim_ontology_resolvers.py index 340d6fa..7d5abea 100644 --- a/server/src/app/services/expense_claim_ontology_resolvers.py +++ b/server/src/app/services/expense_claim_ontology_resolvers.py @@ -87,6 +87,7 @@ from app.services.expense_claim_constants import ( TRAVEL_POLICY_TRAIN_CLASS_PATTERNS, TRAVEL_POLICY_HOTEL_NIGHT_PATTERN, ) +from app.services.expense_type_keywords import resolve_expense_type_code_from_text from app.services.expense_claim_risk_review import ExpenseClaimRiskReviewMixin from app.services.expense_amounts import ( extract_amount_candidates, @@ -209,26 +210,7 @@ class ExpenseClaimOntologyResolverMixin: or "" ).replace(" ", "") if compact: - if "招待" in compact or ("客户" in compact and any(word in compact for word in ("吃饭", "宴请", "请客", "用餐"))): - return "entertainment" - if any(word in compact for word in ("差旅", "出差", "机票", "行程")): - return "travel" - if any(word in compact for word in ("住宿", "酒店", "宾馆")): - return "hotel" - if any(word in compact for word in ("交通", "打车", "网约车", "出租车", "乘车", "用车", "叫车", "车费", "车资", "的士", "停车")): - return "transport" - if any(word in compact for word in ("餐费", "用餐", "午餐", "晚餐", "早餐", "伙食")): - return "meal" - if "会务" in compact: - return "meeting" - if any(word in compact for word in ("办公费", "办公用品", "文具", "耗材", "办公耗材", "打印纸", "办公设备", "键盘", "鼠标", "白板")): - return "office" - if any(word in compact for word in ("培训费", "培训", "讲师费", "课时费", "课程费")): - return "training" - if any(word in compact for word in ("通讯费", "话费", "流量费", "宽带费")): - return "communication" - if any(word in compact for word in ("福利费", "团建", "慰问", "节日福利", "体检费")): - return "welfare" + return resolve_expense_type_code_from_text(compact) return None @staticmethod diff --git a/server/src/app/services/expense_rule_runtime.py b/server/src/app/services/expense_rule_runtime.py index 539bc82..441d3fc 100644 --- a/server/src/app/services/expense_rule_runtime.py +++ b/server/src/app/services/expense_rule_runtime.py @@ -538,8 +538,8 @@ class ExpenseRuleRuntimeService: if any(keyword in normalized for keyword in ("市内交通", "打车", "网约车", "出租车")): return "transport" if "招待" in normalized and "餐" in normalized: - return "entertainment" - if "餐补" in normalized or normalized == "餐费": + return "meal" + if "餐补" in normalized or normalized in {"餐费", "业务招待费"}: return "meal" return "" @@ -547,7 +547,7 @@ class ExpenseRuleRuntimeService: def _spreadsheet_metric_label(expense_type: str) -> str: return { "transport": "单笔交通金额", - "meal": "差旅餐补金额", + "meal": "业务招待费金额", "entertainment": "人均招待餐费", }.get(expense_type, "金额") diff --git a/server/src/app/services/expense_rule_runtime_defaults.py b/server/src/app/services/expense_rule_runtime_defaults.py index c6d78b5..3e4a481 100644 --- a/server/src/app/services/expense_rule_runtime_defaults.py +++ b/server/src/app/services/expense_rule_runtime_defaults.py @@ -8,6 +8,7 @@ EXPENSE_RULE_CODE_BLOCK_PATTERN = re.compile(r"```expense-rule\s*(\{.*?\})\s*``` DOCUMENT_TYPE_LABELS = { "flight_itinerary": "机票/航班行程单", "train_ticket": "火车/高铁票", + "ship_ticket": "轮船票", "hotel_invoice": "酒店住宿票据", "taxi_receipt": "出租车/网约车票据", "parking_toll_receipt": "停车/通行费票据", @@ -24,9 +25,9 @@ SCENE_LABELS = { "travel": "差旅", "hotel": "住宿", "transport": "交通", - "meal": "餐饮", + "meal": "业务招待", "entertainment": "业务招待", - "office": "办公", + "office": "办公用品", "meeting": "会务", "training": "培训", "communication": "通讯", @@ -73,7 +74,7 @@ DEFAULT_SCENE_MATRIX_CONFIG: dict[str, Any] = { }, }, "meal": { - "label": "餐费", + "label": "业务招待费", "location_required": False, "min_attachment_count": 1, "allowed_scene_codes": ["meal"], @@ -84,7 +85,7 @@ DEFAULT_SCENE_MATRIX_CONFIG: dict[str, Any] = { "warn_amount": "300.00", "block_amount": "800.00", "exception_keywords": ["客户接待", "团队活动", "加班", "展会", "超标说明"], - "metric_label": "餐费合计", + "metric_label": "业务招待费合计", }, }, "entertainment": { @@ -103,7 +104,7 @@ DEFAULT_SCENE_MATRIX_CONFIG: dict[str, Any] = { }, }, "office": { - "label": "办公费", + "label": "办公用品费", "location_required": False, "min_attachment_count": 1, "allowed_scene_codes": ["office"], @@ -114,7 +115,7 @@ DEFAULT_SCENE_MATRIX_CONFIG: dict[str, Any] = { "warn_amount": "1500.00", "block_amount": "5000.00", "exception_keywords": ["批量采购", "固定资产", "部门集中采购", "超标说明"], - "metric_label": "办公费合计", + "metric_label": "办公用品费合计", }, }, "meeting": { diff --git a/server/src/app/services/expense_type_keywords.py b/server/src/app/services/expense_type_keywords.py new file mode 100644 index 0000000..657ff53 --- /dev/null +++ b/server/src/app/services/expense_type_keywords.py @@ -0,0 +1,245 @@ +from __future__ import annotations + +from typing import Iterable + + +EXPENSE_TYPE_KEYWORD_GROUPS: tuple[tuple[str, str, tuple[str, ...]], ...] = ( + ( + "travel", + "差旅费", + ( + "差旅费", + "差旅", + "出差", + "外地出差", + "跨城交通", + "往返车票", + "机票", + "飞机票", + "航班", + "登机牌", + "行程单", + "火车票", + "高铁票", + "动车票", + "铁路客票", + "客票", + ), + ), + ( + "hotel", + "住宿费", + ( + "住宿费", + "住宿", + "酒店发票", + "酒店", + "宾馆", + "民宿", + "房费", + "客房", + "住店", + "入住", + "离店", + "住宿清单", + ), + ), + ( + "transport", + "交通费", + ( + "交通费", + "交通", + "市内交通", + "打车", + "网约车", + "出租车票", + "出租车", + "的士票", + "的士", + "滴滴", + "曹操出行", + "T3出行", + "出行", + "乘车费", + "乘车", + "用车", + "叫车", + "车费", + "车资", + "公交", + "地铁", + "停车费", + "停车", + "过路费", + "通行费", + "高速费", + "燃油费", + "油费", + ), + ), + ( + "meal", + "业务招待费", + ( + "业务招待费", + "业务招待", + "招待费", + "招待", + "客户招待", + "客户接待", + "商务接待", + "商务宴请", + "宴请", + "请客", + "请客户", + "客户用餐", + "客户餐", + "客户吃饭", + "陪同用餐", + "接待餐", + "餐费", + "伙食费", + "伙食", + "工作餐", + "餐饮", + "用餐", + "早餐", + "午餐", + "晚餐", + "夜宵", + "盒饭", + "茶歇", + "餐票", + "饭票", + ), + ), + ( + "meeting", + "会务费", + ( + "会务费", + "会务", + "会议费", + "会议", + "参会", + "会场", + "场地费", + "论坛", + "展会", + "研讨会", + "峰会", + "布展", + ), + ), + ( + "office", + "办公用品费", + ( + "办公用品费", + "办公费", + "办公用品", + "办公耗材", + "办公设备", + "办公", + "文具", + "耗材", + "打印纸", + "打印", + "纸张", + "硒鼓", + "墨盒", + "键盘", + "鼠标", + "白板", + "电脑配件", + ), + ), + ( + "training", + "培训费", + ( + "培训费", + "培训", + "讲师费", + "讲师", + "课时费", + "课程费", + "课程", + "教材", + "学费", + "考试费", + "认证费", + "认证", + ), + ), + ( + "communication", + "通讯费", + ( + "通讯费", + "通讯", + "话费", + "电话费", + "手机费", + "流量费", + "流量", + "宽带费", + "宽带", + "网络费", + ), + ), + ( + "welfare", + "福利费", + ( + "福利费", + "福利", + "团建", + "慰问", + "节日福利", + "体检费", + "体检", + "员工关怀", + ), + ), +) + + +EXPENSE_TYPE_LABEL_BY_CODE = { + code: label for code, label, _keywords in EXPENSE_TYPE_KEYWORD_GROUPS +} +EXPENSE_TYPE_LABEL_BY_CODE.setdefault("entertainment", "业务招待费") + + +def build_expense_type_keyword_map() -> dict[str, str]: + mapping: dict[str, str] = {} + for code, _label, keywords in EXPENSE_TYPE_KEYWORD_GROUPS: + for keyword in keywords: + mapping.setdefault(keyword, code) + return mapping + + +def iter_expense_keywords(*codes: str) -> Iterable[str]: + allowed_codes = {str(code or "").strip() for code in codes if str(code or "").strip()} + for code, _label, keywords in EXPENSE_TYPE_KEYWORD_GROUPS: + if allowed_codes and code not in allowed_codes: + continue + yield from keywords + + +def resolve_expense_type_code_from_text(value: str) -> str | None: + compact = str(value or "").replace(" ", "") + if not compact: + return None + for code, _label, keywords in EXPENSE_TYPE_KEYWORD_GROUPS: + if any(keyword in compact for keyword in keywords): + return code + return None + + +def resolve_expense_type_label_from_text(value: str) -> tuple[str, str] | None: + code = resolve_expense_type_code_from_text(value) + if not code: + return None + return code, EXPENSE_TYPE_LABEL_BY_CODE.get(code, str(value or "").strip()) diff --git a/server/src/app/services/knowledge_index_tasks.py b/server/src/app/services/knowledge_index_tasks.py index b0eeaf5..f5c4288 100644 --- a/server/src/app/services/knowledge_index_tasks.py +++ b/server/src/app/services/knowledge_index_tasks.py @@ -63,6 +63,7 @@ class KnowledgeIndexTaskManager: heartbeat_stop = threading.Event() heartbeat_thread: threading.Thread | None = None tool_call_id = "" + knowledge_ingest: dict[str, Any] | None = None tool_request_json = { "agent": AgentName.HERMES.value, "folder": folder, @@ -74,6 +75,10 @@ class KnowledgeIndexTaskManager: run_service = AgentRunService(db) knowledge_service = KnowledgeService(db=db) rag_service = KnowledgeRagService(db=db) + knowledge_ingest = _build_initial_knowledge_ingest_state( + knowledge_service, + document_ids=document_ids, + ) run_service.merge_route_json( agent_run_id, @@ -93,7 +98,18 @@ class KnowledgeIndexTaskManager: "skipped_documents": 0, "percent": 10 if document_ids else 100, }, + "knowledge_ingest": knowledge_ingest, }, + result_summary=_build_ingest_running_summary( + knowledge_ingest, + { + "total_documents": len(document_ids), + "completed_documents": 0, + "failed_documents": 0, + "skipped_documents": 0, + "percent": 10 if document_ids else 100, + }, + ), ) tool_call = run_service.record_tool_call( run_id=agent_run_id, @@ -134,44 +150,159 @@ class KnowledgeIndexTaskManager: ) heartbeat_thread.start() - response = rag_service.index_documents(document_ids=document_ids, force=force) - succeeded_document_ids = [ - str(item).strip() - for item in list(response.get("succeeded_document_ids") or []) - if str(item).strip() - ] - failed_documents = [ - item - for item in list(response.get("failed_documents") or []) - if isinstance(item, dict) - ] + responses: list[dict[str, Any]] = [] + succeeded_document_ids: list[str] = [] + failed_documents: list[dict[str, str]] = [] + total_documents = len(document_ids) + + for index, document_id in enumerate(document_ids, start=1): + _patch_ingest_document( + knowledge_ingest, + document_id, + { + "status": "running", + "phase": "indexing", + "started_at": datetime.now(UTC).isoformat(), + }, + event=f"开始处理第 {index}/{total_documents} 个文件,正在写入 LightRAG。", + ) + knowledge_ingest["current_document_id"] = document_id + _sync_ingest_route_json( + run_service, + agent_run_id, + knowledge_ingest, + progress=_build_ingest_progress(knowledge_ingest, total_documents), + ) + + try: + response = rag_service.index_documents(document_ids=[document_id], force=force) + except Exception as exc: + logger.exception( + "Knowledge document index failed run_id=%s doc_id=%s", + agent_run_id, + document_id, + ) + failed_documents.append( + { + "document_id": document_id, + "status": "exception", + "error": str(exc), + } + ) + _patch_ingest_document( + knowledge_ingest, + document_id, + { + "status": "failed", + "phase": "failed", + "finished_at": datetime.now(UTC).isoformat(), + "error": str(exc), + }, + event=f"归集失败:{exc}", + level="error", + ) + knowledge_service.set_document_ingest_statuses( + [document_id], + KNOWLEDGE_INGEST_STATUS_FAILED, + agent_run_id=agent_run_id, + ) + _refresh_ingest_graph(knowledge_ingest) + _sync_ingest_route_json( + run_service, + agent_run_id, + knowledge_ingest, + progress=_build_ingest_progress(knowledge_ingest, total_documents), + ) + continue + + responses.append(response) + response_failed_documents = _extract_failed_documents(response, document_id) + document_summary = _extract_document_summary(response, document_id) + if response_failed_documents: + failed_documents.extend(response_failed_documents) + error_text = ( + response_failed_documents[0].get("error") or "LightRAG 未返回可查询状态" + ) + _patch_ingest_document( + knowledge_ingest, + document_id, + { + **document_summary, + "status": "failed", + "phase": "failed", + "finished_at": datetime.now(UTC).isoformat(), + "error": error_text, + "track_id": str(response.get("track_id") or "").strip(), + }, + event=f"LightRAG 索引失败:{error_text}", + level="error", + ) + knowledge_service.set_document_ingest_statuses( + [document_id], + KNOWLEDGE_INGEST_STATUS_FAILED, + agent_run_id=agent_run_id, + ) + else: + succeeded_document_ids.append(document_id) + chunk_count = int(document_summary.get("chunk_count") or 0) + entity_count = int(document_summary.get("entity_count") or 0) + relation_count = int(document_summary.get("relation_count") or 0) + _patch_ingest_document( + knowledge_ingest, + document_id, + { + **document_summary, + "status": "succeeded", + "phase": "indexed", + "finished_at": datetime.now(UTC).isoformat(), + "track_id": str(response.get("track_id") or "").strip(), + }, + event=( + "LightRAG 索引完成:" + f"{chunk_count} 个 chunk,{entity_count} 个实体," + f"{relation_count} 条关系。" + ), + ) + knowledge_service.set_document_ingest_statuses( + [document_id], + KNOWLEDGE_INGEST_STATUS_INGESTED, + agent_run_id=agent_run_id, + ) + _refresh_ingest_graph(knowledge_ingest) + _sync_ingest_route_json( + run_service, + agent_run_id, + knowledge_ingest, + progress=_build_ingest_progress(knowledge_ingest, total_documents), + ) + failed_document_ids = [ str(item.get("document_id") or "").strip() for item in failed_documents if str(item.get("document_id") or "").strip() ] - if succeeded_document_ids: - knowledge_service.set_document_ingest_statuses( - succeeded_document_ids, - KNOWLEDGE_INGEST_STATUS_INGESTED, - agent_run_id=agent_run_id, - ) - if failed_document_ids: - knowledge_service.set_document_ingest_statuses( - failed_document_ids, - KNOWLEDGE_INGEST_STATUS_FAILED, - agent_run_id=agent_run_id, - ) - duration_ms = int((perf_counter() - started) * 1000) tool_status = "succeeded" if not failed_document_ids else "failed" + latest_track_id = _resolve_latest_track_id(responses) + knowledge_ingest["current_document_id"] = "" + knowledge_ingest["status"] = tool_status + knowledge_ingest["phase"] = "completed" + knowledge_ingest["finished_at"] = datetime.now(UTC).isoformat() + knowledge_ingest["graph"] = _build_ingest_graph(knowledge_ingest) heartbeat_stop.set() if heartbeat_thread is not None: heartbeat_thread.join(timeout=1) run_service.update_tool_call( tool_call_id, - response_json=response, + response_json={ + "track_id": latest_track_id, + "requested_document_ids": document_ids, + "succeeded_document_ids": succeeded_document_ids, + "failed_documents": failed_documents, + "documents": knowledge_ingest.get("documents", []), + "responses": responses, + }, status=tool_status, duration_ms=duration_ms, error_message=None if tool_status == "succeeded" else "部分文档索引失败。", @@ -183,14 +314,17 @@ class KnowledgeIndexTaskManager: summary = ( f"LightRAG 已完成 {completed_documents}/{total_documents} 个知识文档索引。" if failed_count == 0 - else f"LightRAG 已完成 {completed_documents}/{total_documents} 个知识文档索引,失败 {failed_count} 个。" + else ( + f"LightRAG 已完成 {completed_documents}/{total_documents} 个知识文档索引," + f"失败 {failed_count} 个。" + ) ) run_service.merge_route_json( agent_run_id, { "job_type": "knowledge_index_sync", "phase": "completed", - "track_id": str(response.get("track_id") or "").strip(), + "track_id": latest_track_id, "heartbeat_at": datetime.now(UTC).isoformat(), "progress": { "total_documents": total_documents, @@ -199,6 +333,7 @@ class KnowledgeIndexTaskManager: "skipped_documents": 0, "percent": 100, }, + "knowledge_ingest": knowledge_ingest, }, status=( AgentRunStatus.SUCCEEDED.value @@ -234,24 +369,50 @@ class KnowledgeIndexTaskManager: error_message=str(exc), ) KnowledgeService(db=db).set_document_ingest_statuses( - document_ids, + _resolve_failed_ingest_document_ids(knowledge_ingest, document_ids), KNOWLEDGE_INGEST_STATUS_FAILED, agent_run_id=agent_run_id, ) + if knowledge_ingest is not None: + for document_id in document_ids: + document = _find_ingest_document(knowledge_ingest, document_id) + if document is None or document.get("status") in {"succeeded", "failed"}: + continue + _patch_ingest_document( + knowledge_ingest, + document_id, + { + "status": "failed", + "phase": "failed", + "finished_at": datetime.now(UTC).isoformat(), + "error": str(exc), + }, + event=f"归集任务中断:{exc}", + level="error", + ) + knowledge_ingest["status"] = "failed" + knowledge_ingest["phase"] = "failed" + knowledge_ingest["current_document_id"] = "" + knowledge_ingest["finished_at"] = datetime.now(UTC).isoformat() + knowledge_ingest["graph"] = _build_ingest_graph(knowledge_ingest) + + route_payload: dict[str, Any] = { + "job_type": "knowledge_index_sync", + "phase": "failed", + "heartbeat_at": datetime.now(UTC).isoformat(), + "progress": { + "total_documents": len(document_ids), + "completed_documents": 0, + "failed_documents": len(document_ids), + "skipped_documents": 0, + "percent": 100, + }, + } + if knowledge_ingest is not None: + route_payload["knowledge_ingest"] = knowledge_ingest AgentRunService(db).merge_route_json( agent_run_id, - { - "job_type": "knowledge_index_sync", - "phase": "failed", - "heartbeat_at": datetime.now(UTC).isoformat(), - "progress": { - "total_documents": len(document_ids), - "completed_documents": 0, - "failed_documents": len(document_ids), - "skipped_documents": 0, - "percent": 100, - }, - }, + route_payload, status=AgentRunStatus.FAILED.value, result_summary=str(exc), error_message=str(exc), @@ -267,4 +428,312 @@ class KnowledgeIndexTaskManager: db.close() +def _build_initial_knowledge_ingest_state( + knowledge_service: KnowledgeService, + *, + document_ids: list[str], +) -> dict[str, Any]: + now = datetime.now(UTC).isoformat() + documents = [ + _build_initial_knowledge_ingest_document(knowledge_service, document_id, now=now) + for document_id in document_ids + ] + return { + "schema_version": 1, + "status": "running", + "phase": "queued", + "started_at": now, + "finished_at": None, + "current_document_id": documents[0]["document_id"] if documents else "", + "documents": documents, + "graph": _build_ingest_graph({"documents": documents}), + } + + +def _build_initial_knowledge_ingest_document( + knowledge_service: KnowledgeService, + document_id: str, + *, + now: str, +) -> dict[str, Any]: + try: + entry = knowledge_service.get_document_entry(document_id) + except Exception: + entry = {} + return { + "document_id": document_id, + "name": str(entry.get("original_name") or document_id).strip(), + "folder": str(entry.get("folder") or "").strip(), + "extension": str(entry.get("extension") or "").strip(), + "mime_type": str(entry.get("mime_type") or "").strip(), + "status": "queued", + "phase": "queued", + "started_at": None, + "finished_at": None, + "text_chars": 0, + "indexed_text_chars": 0, + "section_count": 0, + "sections": [], + "chunk_count": 0, + "chunk_ids": [], + "chunks": [], + "entity_count": 0, + "relation_count": 0, + "entities": [], + "relations": [], + "events": [ + { + "at": now, + "level": "info", + "message": "已进入知识归集队列,等待 LightRAG 处理。", + } + ], + } + + +def _patch_ingest_document( + knowledge_ingest: dict[str, Any], + document_id: str, + updates: dict[str, Any], + *, + event: str = "", + level: str = "info", +) -> None: + document = _find_ingest_document(knowledge_ingest, document_id) + if document is None: + return + document.update(updates) + if event: + _append_ingest_event(document, event, level=level) + + +def _append_ingest_event(document: dict[str, Any], message: str, *, level: str) -> None: + events = document.get("events") + if not isinstance(events, list): + events = [] + events.append( + { + "at": datetime.now(UTC).isoformat(), + "level": level, + "message": message, + } + ) + document["events"] = events[-30:] + + +def _find_ingest_document( + knowledge_ingest: dict[str, Any], + document_id: str, +) -> dict[str, Any] | None: + for document in list(knowledge_ingest.get("documents") or []): + if not isinstance(document, dict): + continue + if str(document.get("document_id") or "").strip() == document_id: + return document + return None + + +def _sync_ingest_route_json( + run_service: AgentRunService, + agent_run_id: str, + knowledge_ingest: dict[str, Any], + *, + progress: dict[str, int], +) -> None: + run_service.merge_route_json( + agent_run_id, + { + "job_type": "knowledge_index_sync", + "phase": "indexing", + "heartbeat_at": datetime.now(UTC).isoformat(), + "progress": progress, + "knowledge_ingest": knowledge_ingest, + }, + result_summary=_build_ingest_running_summary(knowledge_ingest, progress), + ) + + +def _build_ingest_running_summary( + knowledge_ingest: dict[str, Any], + progress: dict[str, int], +) -> str: + total_documents = int(progress.get("total_documents") or 0) + completed_documents = int(progress.get("completed_documents") or 0) + failed_documents = int(progress.get("failed_documents") or 0) + current_document_id = str(knowledge_ingest.get("current_document_id") or "").strip() + current_document = ( + _find_ingest_document(knowledge_ingest, current_document_id) + if current_document_id + else None + ) + if current_document is not None: + name = str(current_document.get("name") or current_document_id).strip() + current_index = _resolve_ingest_document_index(knowledge_ingest, current_document_id) + return ( + f"知识归纳正在处理 {current_index}/{total_documents}:{name}。" + f"已完成 {completed_documents} 个,失败 {failed_documents} 个。" + ) + return ( + f"知识归纳正在运行,已完成 {completed_documents}/{total_documents} 个文档," + f"失败 {failed_documents} 个。" + ) + + +def _resolve_ingest_document_index( + knowledge_ingest: dict[str, Any], + document_id: str, +) -> int: + documents = [ + item for item in list(knowledge_ingest.get("documents") or []) if isinstance(item, dict) + ] + for index, document in enumerate(documents, start=1): + if str(document.get("document_id") or "").strip() == document_id: + return index + return 0 + + +def _build_ingest_progress( + knowledge_ingest: dict[str, Any], + total_documents: int, +) -> dict[str, int]: + documents = [ + item for item in list(knowledge_ingest.get("documents") or []) if isinstance(item, dict) + ] + completed_documents = sum(1 for item in documents if item.get("status") == "succeeded") + failed_documents = sum(1 for item in documents if item.get("status") == "failed") + skipped_documents = sum(1 for item in documents if item.get("status") == "skipped") + done_documents = completed_documents + failed_documents + skipped_documents + if total_documents <= 0: + percent = 100 + else: + percent = min(95, max(10, 10 + int(done_documents * 85 / total_documents))) + return { + "total_documents": total_documents, + "completed_documents": completed_documents, + "failed_documents": failed_documents, + "skipped_documents": skipped_documents, + "percent": percent, + } + + +def _extract_document_summary(response: dict[str, Any], document_id: str) -> dict[str, Any]: + for item in list(response.get("document_summaries") or []): + if not isinstance(item, dict): + continue + if str(item.get("document_id") or "").strip() == document_id: + return dict(item) + return {} + + +def _extract_failed_documents( + response: dict[str, Any], + document_id: str, +) -> list[dict[str, str]]: + failed_documents: list[dict[str, str]] = [] + for item in list(response.get("failed_documents") or []): + if not isinstance(item, dict): + continue + item_document_id = str(item.get("document_id") or "").strip() + if item_document_id and item_document_id != document_id: + continue + failed_documents.append( + { + "document_id": item_document_id or document_id, + "status": str(item.get("status") or "failed").strip(), + "error": str(item.get("error") or "LightRAG 索引失败").strip(), + } + ) + return failed_documents + + +def _resolve_failed_ingest_document_ids( + knowledge_ingest: dict[str, Any] | None, + document_ids: list[str], +) -> list[str]: + if knowledge_ingest is None: + return document_ids + failed_document_ids: list[str] = [] + seen_document_ids: set[str] = set() + for document in list(knowledge_ingest.get("documents") or []): + if not isinstance(document, dict): + continue + document_id = str(document.get("document_id") or "").strip() + if not document_id: + continue + seen_document_ids.add(document_id) + if document.get("status") != "succeeded": + failed_document_ids.append(document_id) + failed_document_ids.extend( + document_id for document_id in document_ids if document_id not in seen_document_ids + ) + return failed_document_ids + + +def _refresh_ingest_graph(knowledge_ingest: dict[str, Any]) -> None: + knowledge_ingest["graph"] = _build_ingest_graph(knowledge_ingest) + + +def _build_ingest_graph(knowledge_ingest: dict[str, Any]) -> dict[str, Any]: + documents = [ + item for item in list(knowledge_ingest.get("documents") or []) if isinstance(item, dict) + ] + entities = _dedupe_text_items( + entity for document in documents for entity in list(document.get("entities") or []) + ) + relations = _dedupe_relations( + relation for document in documents for relation in list(document.get("relations") or []) + ) + return { + "chunk_count": sum(_to_int(document.get("chunk_count")) for document in documents), + "entity_count": sum(_to_int(document.get("entity_count")) for document in documents), + "relation_count": sum(_to_int(document.get("relation_count")) for document in documents), + "entities": entities[:60], + "relations": relations[:60], + } + + +def _dedupe_text_items(items: Any) -> list[str]: + deduped: list[str] = [] + seen: set[str] = set() + for item in items: + text = str(item or "").strip() + if not text or text in seen: + continue + seen.add(text) + deduped.append(text) + return deduped + + +def _dedupe_relations(items: Any) -> list[dict[str, str]]: + deduped: list[dict[str, str]] = [] + seen: set[tuple[str, str, str]] = set() + for item in items: + if not isinstance(item, dict): + continue + source = str(item.get("source") or "").strip() + target = str(item.get("target") or "").strip() + relation_type = str(item.get("type") or "关联").strip() + key = (source, target, relation_type) + if not source or not target or key in seen: + continue + seen.add(key) + deduped.append({"source": source, "target": target, "type": relation_type}) + return deduped + + +def _resolve_latest_track_id(responses: list[dict[str, Any]]) -> str: + for response in reversed(responses): + track_id = str(response.get("track_id") or "").strip() + if track_id: + return track_id + return "" + + +def _to_int(value: Any) -> int: + try: + return int(value or 0) + except (TypeError, ValueError): + return 0 + + knowledge_index_task_manager = KnowledgeIndexTaskManager() diff --git a/server/src/app/services/knowledge_ingest_log.py b/server/src/app/services/knowledge_ingest_log.py new file mode 100644 index 0000000..aa12c1e --- /dev/null +++ b/server/src/app/services/knowledge_ingest_log.py @@ -0,0 +1,224 @@ +from __future__ import annotations + +import json +import re +from pathlib import Path +from typing import Any + +MAX_INGEST_LOG_CHUNKS = 24 +MAX_INGEST_LOG_ENTITIES = 24 +MAX_INGEST_LOG_RELATIONS = 24 +MAX_INGEST_LOG_SECTIONS = 12 +MAX_INGEST_LOG_TEXT_PREVIEW = 180 + +INGEST_SECTION_HEADING_PATTERN = re.compile( + r"^(?:#{1,4}\s+.+|第[一二三四五六七八九十百零0-9]+[章节条]\s*.*)$" +) + + +def build_ingest_document_summary( + *, + document_id: str, + entry: dict[str, Any], + raw_text: str, + indexed_text: str, +) -> dict[str, Any]: + raw_text_value = str(raw_text or "") + indexed_text_value = str(indexed_text or "") + sections = _extract_ingest_sections(indexed_text_value) + return { + "document_id": document_id, + "name": str(entry.get("original_name") or "").strip(), + "folder": str(entry.get("folder") or "").strip(), + "extension": str(entry.get("extension") or "").strip(), + "mime_type": str(entry.get("mime_type") or "").strip(), + "text_chars": len(raw_text_value), + "indexed_text_chars": len(indexed_text_value), + "section_count": len(sections), + "sections": sections, + "chunk_count": 0, + "chunk_ids": [], + "chunks": [], + "entity_count": 0, + "relation_count": 0, + "entities": [], + "relations": [], + } + + +def build_ingest_status_summary( + *, + status_payload: dict[str, Any], + graph_summary: dict[str, Any], +) -> dict[str, Any]: + chunk_ids = _normalize_chunk_ids(status_payload) + chunk_count = _resolve_chunk_count(status_payload, chunk_ids) + return { + "lightrag_status": str(status_payload.get("status") or "").strip(), + "query_ready": bool(status_payload.get("query_ready")), + "chunk_count": chunk_count, + "chunk_ids": chunk_ids[:MAX_INGEST_LOG_CHUNKS], + **graph_summary, + } + + +def build_document_graph_summary( + storage_root: Path, + *, + workspace: str, + document_id: str, +) -> dict[str, Any]: + workspace_dir = ( + Path(storage_root) / "knowledge" / ".lightrag" / str(workspace).strip() + ).resolve() + entities_payload = _load_json_file(workspace_dir / "kv_store_full_entities.json") + relations_payload = _load_json_file(workspace_dir / "kv_store_full_relations.json") + chunks_payload = _load_json_file(workspace_dir / "kv_store_text_chunks.json") + + entities = _normalize_document_entities(entities_payload, document_id) + relations = _normalize_document_relations(relations_payload, document_id) + chunks = _normalize_document_chunks(chunks_payload, document_id) + return { + "entity_count": len(entities), + "relation_count": len(relations), + "entities": entities[:MAX_INGEST_LOG_ENTITIES], + "relations": relations[:MAX_INGEST_LOG_RELATIONS], + "chunks": chunks[:MAX_INGEST_LOG_CHUNKS], + } + + +def _extract_ingest_sections(text: str) -> list[dict[str, str]]: + sections: list[dict[str, str]] = [] + lines = [line.strip() for line in str(text or "").splitlines()] + for index, line in enumerate(lines): + if len(sections) >= MAX_INGEST_LOG_SECTIONS: + break + if not line or len(line) > 90 or not INGEST_SECTION_HEADING_PATTERN.match(line): + continue + sections.append( + { + "title": line.lstrip("#").strip(), + "excerpt": _find_following_excerpt(lines[index + 1 :]), + } + ) + return sections + + +def _find_following_excerpt(lines: list[str]) -> str: + collected: list[str] = [] + for line in lines: + if not line: + continue + if INGEST_SECTION_HEADING_PATTERN.match(line): + break + collected.append(line) + if len(" ".join(collected)) >= MAX_INGEST_LOG_TEXT_PREVIEW: + break + return _truncate_text(" ".join(collected), max_length=MAX_INGEST_LOG_TEXT_PREVIEW) + + +def _normalize_chunk_ids(status_payload: dict[str, Any]) -> list[str]: + chunks_list = status_payload.get("chunks_list") + if not isinstance(chunks_list, list): + return [] + return [str(item).strip() for item in chunks_list if str(item or "").strip()] + + +def _resolve_chunk_count(status_payload: dict[str, Any], chunk_ids: list[str]) -> int: + try: + return int(status_payload.get("chunks_count") or len(chunk_ids)) + except (TypeError, ValueError): + return len(chunk_ids) + + +def _load_json_file(path: Path) -> dict[str, Any]: + try: + payload = json.loads(path.read_text(encoding="utf-8")) + except (FileNotFoundError, json.JSONDecodeError, OSError): + return {} + return payload if isinstance(payload, dict) else {} + + +def _normalize_document_entities(payload: dict[str, Any], document_id: str) -> list[str]: + document_payload = payload.get(document_id) if isinstance(payload, dict) else {} + entity_names = ( + document_payload.get("entity_names") if isinstance(document_payload, dict) else [] + ) + if not isinstance(entity_names, list): + return [] + return _dedupe_text_items(entity_names) + + +def _normalize_document_relations( + payload: dict[str, Any], document_id: str +) -> list[dict[str, str]]: + document_payload = payload.get(document_id) if isinstance(payload, dict) else {} + relation_pairs = ( + document_payload.get("relation_pairs") if isinstance(document_payload, dict) else [] + ) + if not isinstance(relation_pairs, list): + return [] + + relations: list[dict[str, str]] = [] + seen: set[tuple[str, str]] = set() + for pair in relation_pairs: + if not isinstance(pair, (list, tuple)) or len(pair) < 2: + continue + source = str(pair[0] or "").strip() + target = str(pair[1] or "").strip() + if not source or not target or (source, target) in seen: + continue + seen.add((source, target)) + relations.append({"source": source, "target": target, "type": "关联"}) + return relations + + +def _normalize_document_chunks(payload: dict[str, Any], document_id: str) -> list[dict[str, Any]]: + chunks: list[dict[str, Any]] = [] + for chunk_id, raw_chunk in payload.items(): + if not isinstance(raw_chunk, dict): + continue + if str(raw_chunk.get("full_doc_id") or "").strip() != document_id: + continue + content = str(raw_chunk.get("content") or "").strip() + chunks.append( + { + "id": str(raw_chunk.get("_id") or chunk_id).strip(), + "order": _to_int(raw_chunk.get("chunk_order_index")), + "tokens": _to_int(raw_chunk.get("tokens")), + "summary": _build_chunk_summary(content), + } + ) + return sorted(chunks, key=lambda item: (item["order"], item["id"])) + + +def _build_chunk_summary(content: str) -> str: + lines = [line.strip() for line in str(content or "").splitlines() if line.strip()] + text = next((line for line in lines if len(line) >= 12), lines[0] if lines else "") + return _truncate_text(text, max_length=MAX_INGEST_LOG_TEXT_PREVIEW) + + +def _dedupe_text_items(items: list[Any]) -> list[str]: + deduped: list[str] = [] + seen: set[str] = set() + for item in items: + text = str(item or "").strip() + if not text or text in seen: + continue + seen.add(text) + deduped.append(text) + return deduped + + +def _to_int(value: Any) -> int: + try: + return int(value or 0) + except (TypeError, ValueError): + return 0 + + +def _truncate_text(text: str, *, max_length: int) -> str: + normalized = " ".join(str(text or "").split()).strip() + if len(normalized) <= max_length: + return normalized + return f"{normalized[: max_length - 3].rstrip()}..." diff --git a/server/src/app/services/knowledge_rag.py b/server/src/app/services/knowledge_rag.py index 51607d8..521913b 100644 --- a/server/src/app/services/knowledge_rag.py +++ b/server/src/app/services/knowledge_rag.py @@ -12,24 +12,15 @@ from sqlalchemy.orm import Session from app.core.config import get_settings from app.core.logging import get_logger from app.db.session import get_session_factory +from app.services.knowledge_ingest_log import ( + build_document_graph_summary, + build_ingest_document_summary, + build_ingest_status_summary, +) from app.services.knowledge_rag_runtime import ( - DEFAULT_EMBEDDING_TIMEOUT_SECONDS, - DEFAULT_LIGHTRAG_QUERY_MODE, - DEFAULT_LLM_TIMEOUT_SECONDS, KnowledgeRagError, RuntimeModelConfig, _LightRagRuntime, - _build_ali_rerank_request, - _build_azure_deployment_base, - _build_headers, - _ensure_path, - _extract_chat_text, - _extract_embedding_vectors, - _extract_error_message, - _extract_rerank_results, - _normalize_endpoint, - _parse_json_body, - _send_json_request, ) from app.services.settings import SettingsService @@ -76,11 +67,9 @@ STRUCTURED_APPENDIX_LEADING_MARKERS = ( "# 结构化表格补充", ) STRUCTURED_APPENDIX_LEADING_WINDOW = 220 - - _runtime_lock = threading.RLock() -_runtime_instance: _LightRagRuntime | None = None -_runtime_signature: tuple[Any, ...] | None = None +_runtime_instances: dict[int, _LightRagRuntime] = {} +_runtime_signatures: dict[int, tuple[Any, ...]] = {} class KnowledgeRagService: @@ -147,7 +136,11 @@ class KnowledgeRagService: "query": normalized_query, "record_count": len(hits), "hits": hits, - "references": [str(item.get("code") or "").strip() for item in hits if str(item.get("code") or "").strip()], + "references": [ + str(item.get("code") or "").strip() + for item in hits + if str(item.get("code") or "").strip() + ], "raw_references": references, "metadata": raw.get("metadata") if isinstance(raw, dict) else {}, "message": f"已从知识库中检索到 {len(hits)} 条相关内容。", @@ -172,6 +165,7 @@ class KnowledgeRagService: ) texts: list[str] = [] file_paths: list[str] = [] + document_summaries: list[dict[str, Any]] = [] runtime = self._get_runtime() existing_statuses = runtime.get_document_statuses(normalized_ids) @@ -182,12 +176,29 @@ class KnowledgeRagService: try: runtime.delete_document(document_id) except Exception as exc: - logger.warning("Delete existing LightRAG document failed doc_id=%s: %s", document_id, exc) + logger.warning( + "Delete existing LightRAG document failed doc_id=%s: %s", document_id, exc + ) text = knowledge_service.extract_document_text(document_id) + raw_text = text if normalization_service is not None: text = normalization_service.build_enriched_text(text) texts.append(text) - file_paths.append(str((knowledge_service.library_root / entry["folder"] / entry["stored_name"]).resolve())) + file_paths.append( + str( + ( + knowledge_service.library_root / entry["folder"] / entry["stored_name"] + ).resolve() + ) + ) + document_summaries.append( + build_ingest_document_summary( + document_id=document_id, + entry=entry, + raw_text=raw_text, + indexed_text=text, + ) + ) track_id = runtime.insert_documents( texts=texts, @@ -198,10 +209,32 @@ class KnowledgeRagService: statuses = runtime.get_document_statuses(normalized_ids) succeeded_document_ids: list[str] = [] failed_documents: list[dict[str, str]] = [] + summary_by_id = { + str(item.get("document_id") or "").strip(): item + for item in document_summaries + if str(item.get("document_id") or "").strip() + } for document_id in normalized_ids: status_obj = statuses.get(document_id) status_text = self._status_value(status_obj) + status_payload = self._serialize_status(status_obj) + workspace = ( + os.environ.get("LIGHTRAG_WORKSPACE", DEFAULT_LIGHTRAG_WORKSPACE).strip() + or DEFAULT_LIGHTRAG_WORKSPACE + ) + graph_summary = build_document_graph_summary( + self.storage_root, + workspace=workspace, + document_id=document_id, + ) + if document_id in summary_by_id: + summary_by_id[document_id].update( + build_ingest_status_summary( + status_payload=status_payload, + graph_summary=graph_summary, + ) + ) if self.is_query_ready_status(status_obj): succeeded_document_ids.append(document_id) continue @@ -218,13 +251,18 @@ class KnowledgeRagService: "requested_document_ids": normalized_ids, "succeeded_document_ids": succeeded_document_ids, "failed_documents": failed_documents, + "document_summaries": [ + summary_by_id.get(document_id, {}) for document_id in normalized_ids + ], "status_snapshot": { document_id: self._serialize_status(status_obj) for document_id, status_obj in statuses.items() }, } - def get_document_status_map(self, document_ids: list[str] | None = None) -> dict[str, dict[str, Any]]: + def get_document_status_map( + self, document_ids: list[str] | None = None + ) -> dict[str, dict[str, Any]]: target_ids = [str(item).strip() for item in document_ids or [] if str(item).strip()] if not target_ids: return {} @@ -248,28 +286,32 @@ class KnowledgeRagService: logger.warning("Delete LightRAG document ignored doc_id=%s: %s", normalized_id, exc) def _get_runtime(self) -> _LightRagRuntime: - global _runtime_instance, _runtime_signature - signature, runtime_kwargs = self._build_runtime_signature() + thread_id = threading.get_ident() with _runtime_lock: - if _runtime_instance is not None and _runtime_signature == signature: - return _runtime_instance + runtime = _runtime_instances.get(thread_id) + if runtime is not None and _runtime_signatures.get(thread_id) == signature: + return runtime - if _runtime_instance is not None: + if runtime is not None: try: - _runtime_instance.finalize() + runtime.finalize() except Exception as exc: # pragma: no cover - best effort cleanup logger.warning("Finalize previous LightRAG runtime failed: %s", exc) - _runtime_instance = _LightRagRuntime(**runtime_kwargs) - _runtime_signature = signature - return _runtime_instance + runtime = _LightRagRuntime(**runtime_kwargs) + _runtime_instances[thread_id] = runtime + _runtime_signatures[thread_id] = signature + return runtime def _build_runtime_signature(self) -> tuple[tuple[Any, ...], dict[str, Any]]: configs = self._load_runtime_configs() settings = get_settings() working_dir = (self.storage_root / "knowledge" / ".lightrag").resolve() - workspace = os.environ.get("LIGHTRAG_WORKSPACE", DEFAULT_LIGHTRAG_WORKSPACE).strip() or DEFAULT_LIGHTRAG_WORKSPACE + workspace = ( + os.environ.get("LIGHTRAG_WORKSPACE", DEFAULT_LIGHTRAG_WORKSPACE).strip() + or DEFAULT_LIGHTRAG_WORKSPACE + ) qdrant_url = os.environ.get("QDRANT_URL", "").strip() or _resolve_default_qdrant_url() qdrant_api_key = os.environ.get("QDRANT_API_KEY", "").strip() @@ -318,7 +360,9 @@ class KnowledgeRagService: try: settings_service = SettingsService(session) main = self._normalize_runtime_model(settings_service.get_runtime_model_config("main")) - embedding = self._normalize_runtime_model(settings_service.get_runtime_model_config("embedding")) + embedding = self._normalize_runtime_model( + settings_service.get_runtime_model_config("embedding") + ) try: backup_raw = settings_service.get_runtime_model_config("backup") backup = self._normalize_runtime_model(backup_raw) @@ -405,7 +449,9 @@ class KnowledgeRagService: document_id, document_name = _parse_document_identity(file_path) normalized_chunk_id = chunk_id or f"path-{rank}" - normalized_content = _truncate_text(content, max_length=MAX_KNOWLEDGE_HIT_CONTENT_LENGTH) + normalized_content = _truncate_text( + content, max_length=MAX_KNOWLEDGE_HIT_CONTENT_LENGTH + ) excerpt = _build_query_focused_excerpt( normalized_content, query_terms=query_terms, @@ -510,17 +556,14 @@ class KnowledgeRagService: def shutdown_knowledge_rag_runtime() -> None: - global _runtime_instance, _runtime_signature - with _runtime_lock: - if _runtime_instance is None: - return - try: - _runtime_instance.finalize() - except Exception as exc: # pragma: no cover - best effort cleanup - logger.warning("Finalize LightRAG runtime failed during shutdown: %s", exc) - _runtime_instance = None - _runtime_signature = None + for runtime in list(_runtime_instances.values()): + try: + runtime.finalize() + except Exception as exc: # pragma: no cover - best effort cleanup + logger.warning("Finalize LightRAG runtime failed during shutdown: %s", exc) + _runtime_instances.clear() + _runtime_signatures.clear() def _parse_document_identity(file_path: str) -> tuple[str, str]: @@ -551,9 +594,7 @@ def _build_query_focused_excerpt( lowered = normalized.lower() match_positions = [ - lowered.find(term) - for term in query_terms - if term and lowered.find(term) >= 0 + lowered.find(term) for term in query_terms if term and lowered.find(term) >= 0 ] if not match_positions: return _build_excerpt(normalized, max_length=max_length) @@ -649,7 +690,9 @@ def _score_knowledge_hit( elif leading_appendix_marker == "# 重点章节摘录": score += 4 if matched_terms else -12 elif leading_appendix_marker == "# 问答线索补充": - score += 8 if matched_terms and not prefers_tabular_evidence else 2 if matched_terms else -20 + score += ( + 8 if matched_terms and not prefers_tabular_evidence else 2 if matched_terms else -20 + ) elif leading_appendix_marker == "# 结构化表格补充": if prefers_tabular_evidence and matched_terms: score += 16 @@ -666,7 +709,11 @@ def _score_knowledge_hit( score += 4 if matched_terms and any(marker in content for marker in ("附表", "第", "条")): score += 4 - if not prefers_tabular_evidence and matched_terms and any(marker in content for marker in ("第", "条", ":", "-", "•")): + if ( + not prefers_tabular_evidence + and matched_terms + and any(marker in content for marker in ("第", "条", ":", "-", "•")) + ): score += 4 if title and any(term in title for term in query_terms): score += 6 diff --git a/server/src/app/services/ontology.py b/server/src/app/services/ontology.py index 65522ac..a695ed6 100644 --- a/server/src/app/services/ontology.py +++ b/server/src/app/services/ontology.py @@ -170,6 +170,7 @@ class SemanticOntologyService( entities = self._merge_entities( entities, model_parse.entity_hints if model_parse is not None else [], + compact_query, ) intent = self._resolve_intent( compact_query, @@ -193,6 +194,11 @@ class SemanticOntologyService( context_json=context_json, ) ) + missing_slots = self._filter_expense_missing_slots( + compact_query=compact_query, + entities=entities, + missing_slots=missing_slots, + ) relax_knowledge_follow_up = self._should_relax_knowledge_follow_up_clarification( compact_query=compact_query, scenario=scenario, @@ -306,6 +312,45 @@ class SemanticOntologyService( follow_up_markers = ("那", "那么", "这个", "这种", "呢", "的话", "p", "P") return any(marker in compact_query for marker in follow_up_markers) + @staticmethod + def _filter_expense_missing_slots( + *, + compact_query: str, + entities: list[object], + missing_slots: list[str], + ) -> list[str]: + expense_types = { + str(getattr(item, "normalized_value", "") or getattr(item, "value", "") or "").strip() + for item in entities + if getattr(item, "type", "") == "expense_type" + } + has_transport = "transport" in expense_types + has_entertainment = "entertainment" in expense_types + explicit_entertainment = any( + keyword in compact_query + for keyword in ( + "业务招待", + "招待费", + "招待", + "宴请", + "请客", + "请客户吃饭", + "客户吃饭", + "客户用餐", + "客户餐", + "商务接待", + "商务宴请", + "接待餐", + ) + ) + if has_transport and not has_entertainment and not explicit_entertainment: + return [ + item + for item in missing_slots + if item not in {"customer_name", "participants"} + ] + return missing_slots + def _record_semantic_parse( self, *, diff --git a/server/src/app/services/ontology_detection.py b/server/src/app/services/ontology_detection.py index 3073ad9..4443035 100644 --- a/server/src/app/services/ontology_detection.py +++ b/server/src/app/services/ontology_detection.py @@ -37,6 +37,39 @@ from app.services.ontology_rules import ( logger = get_logger("app.services.ontology") +TRANSPORT_EXPENSE_OVERRIDE_KEYWORDS = ( + "打车", + "网约车", + "出租车票", + "出租车", + "的士票", + "的士", + "滴滴", + "市内交通", + "乘车", + "乘车费", + "用车", + "叫车", + "车费", + "车资", + "机场", +) +EXPLICIT_ENTERTAINMENT_KEYWORDS = ( + "业务招待", + "招待费", + "招待", + "宴请", + "请客", + "请客户吃饭", + "客户吃饭", + "客户用餐", + "客户餐", + "商务接待", + "商务宴请", + "接待餐", +) + + class OntologyDetectionMixin: def _detect_scenario(self, compact_query: str) -> tuple[str, float]: scores = {key: 0.0 for key in SCENARIO_KEYWORDS} @@ -337,6 +370,9 @@ class OntologyDetectionMixin: "出现“客户”不等于应收,出现“供应商”不等于应付,必须结合动作词和业务目标判断。" "只有明确查询、统计、列出、多少、明细、对比时才优先使用 query 或 compare。" "附件名称和 OCR 摘要只作为辅助证据,不能编造未出现的事实。" + "如果用户明确提到打车、的士票、出租车票、网约车、乘车费、车费等交通票据," + "即使句子里出现“客户”,也必须优先识别为 transport,不要推断为 entertainment。" + "不要输出用户原文未出现、且与规则候选冲突的费用类型。" "信息不足时 clarification_required=true,并给出一句简短中文追问。" "missing_slots 使用简短 snake_case,例如 expense_type, amount, " "customer_name, participants, attachments。" @@ -351,12 +387,12 @@ class OntologyDetectionMixin: ' "intent": "draft",\n' ' "confidence": 0.88,\n' ' "clarification_required": true,\n' - ' "clarification_question": "请补充客户单位、参与人员和票据附件。",\n' - ' "missing_slots": ["customer_name", "participants", "attachments"],\n' + ' "clarification_question": "请补充发生时间、金额和票据附件。",\n' + ' "missing_slots": ["time_range", "amount", "attachments"],\n' ' "ambiguity": [],\n' ' "entity_hints": [\n' - ' {"type": "expense_type", "value": "招待", ' - '"normalized_value": "entertainment", "role": "filter", ' + ' {"type": "expense_type", "value": "交通费", ' + '"normalized_value": "transport", "role": "filter", ' '"confidence": 0.86}\n' " ]\n" "}" @@ -432,6 +468,7 @@ class OntologyDetectionMixin: def _merge_entities( base_entities: list[OntologyEntity], entity_hints: list[LlmOntologyEntityHint], + compact_query: str = "", ) -> list[OntologyEntity]: merged: dict[tuple[str, str], OntologyEntity] = { (item.type, item.normalized_value): item for item in base_entities @@ -454,7 +491,36 @@ class OntologyDetectionMixin: if existing is None or existing.confidence < candidate.confidence: merged[key] = candidate - return list(merged.values()) + items = list(merged.values()) + if OntologyDetectionMixin._should_transport_override_entertainment( + compact_query, + items, + ): + items = [ + item + for item in items + if not ( + item.type == "expense_type" + and item.normalized_value == "entertainment" + ) + ] + return items + + @staticmethod + def _should_transport_override_entertainment( + compact_query: str, + entities: list[OntologyEntity], + ) -> bool: + expense_types = { + str(item.normalized_value or item.value or "").strip() + for item in entities + if item.type == "expense_type" + } + if not {"transport", "entertainment"}.issubset(expense_types): + return False + if not any(keyword in compact_query for keyword in TRANSPORT_EXPENSE_OVERRIDE_KEYWORDS): + return False + return not any(keyword in compact_query for keyword in EXPLICIT_ENTERTAINMENT_KEYWORDS) @staticmethod def _normalize_short_text_list(values: list[str]) -> list[str]: diff --git a/server/src/app/services/ontology_extraction.py b/server/src/app/services/ontology_extraction.py index c117c31..2f5bad5 100644 --- a/server/src/app/services/ontology_extraction.py +++ b/server/src/app/services/ontology_extraction.py @@ -59,11 +59,16 @@ class OntologyExtractionMixin: missing_slots.append("attachments") return missing_slots - if any( + has_entertainment_type = any( item.normalized_value == "entertainment" for item in entities if item.type == "expense_type" - ): + ) + has_explicit_entertainment_text = "客户" in compact_query and any( + keyword in compact_query + for keyword in ("招待", "接待", "吃饭", "用餐", "宴请", "请客", "客户餐") + ) + if has_entertainment_type or has_explicit_entertainment_text: if "customer" not in entity_types: missing_slots.append("customer_name") missing_slots.append("participants") @@ -171,14 +176,14 @@ class OntologyExtractionMixin: upsert(self._make_entity("expense_type", label, normalized, role="filter")) has_customer_entertainment_signal = "客户" in query and any( - keyword in query for keyword in ("吃饭", "用餐", "餐饮", "宴请", "请客", "招待") + keyword in query for keyword in ("吃饭", "用餐", "餐饮", "宴请", "请客", "招待", "接待") ) if has_customer_entertainment_signal: upsert( self._make_entity( "expense_type", - "客户招待", - "entertainment", + "业务招待费", + "meal", role="filter", confidence=0.96, ) @@ -189,46 +194,52 @@ class OntologyExtractionMixin: for keyword in ( "打车", "网约车", - "出租车", "出租车票", + "出租车", "车费", "乘车", "用车", "叫车", "车资", - "的士", "的士票", + "的士", + "滴滴", + "市内交通", + "地铁", + "公交", "停车费", "过路费", + "通行费", + "高速费", ) ): upsert(self._make_entity("expense_type", "交通", "transport", role="filter", confidence=0.9)) - if any(keyword in query for keyword in ("出差", "机票", "火车", "高铁", "行程单")): + if any(keyword in query for keyword in ("出差", "机票", "飞机票", "航班", "火车票", "火车", "高铁票", "高铁", "动车", "行程单")): upsert(self._make_entity("expense_type", "差旅", "travel", role="filter", confidence=0.88)) - if any(keyword in query for keyword in ("酒店", "住宿", "宾馆")): + if any(keyword in query for keyword in ("酒店", "酒店发票", "住宿", "住宿费", "宾馆", "民宿", "房费", "客房")): upsert(self._make_entity("expense_type", "住宿", "hotel", role="filter", confidence=0.86)) if ( not has_customer_entertainment_signal and any(keyword in query for keyword in ("餐费", "用餐", "午餐", "晚餐", "早餐", "餐饮")) ): - upsert(self._make_entity("expense_type", "餐费", "meal", role="filter", confidence=0.84)) + upsert(self._make_entity("expense_type", "业务招待费", "meal", role="filter", confidence=0.84)) if any( keyword in query - for keyword in ("办公用品", "文具", "耗材", "办公耗材", "打印纸", "办公设备", "键盘", "鼠标", "白板") + for keyword in ("办公用品", "文具", "耗材", "办公耗材", "打印纸", "办公设备", "键盘", "鼠标", "白板", "硒鼓", "墨盒") ): - upsert(self._make_entity("expense_type", "办公费", "office", role="filter", confidence=0.87)) + upsert(self._make_entity("expense_type", "办公用品费", "office", role="filter", confidence=0.87)) - if any(keyword in query for keyword in ("培训", "讲师费", "课时费", "课程费")): + if any(keyword in query for keyword in ("培训", "讲师费", "课时费", "课程费", "教材", "认证费", "考试费")): upsert(self._make_entity("expense_type", "培训费", "training", role="filter", confidence=0.84)) - if any(keyword in query for keyword in ("通讯费", "话费", "流量费", "宽带费")): + if any(keyword in query for keyword in ("通讯费", "话费", "电话费", "手机费", "流量费", "宽带费", "网络费")): upsert(self._make_entity("expense_type", "通讯费", "communication", role="filter", confidence=0.84)) - if any(keyword in query for keyword in ("福利费", "团建", "慰问", "节日福利", "体检费")): + if any(keyword in query for keyword in ("福利费", "团建", "慰问", "节日福利", "体检费", "员工关怀")): upsert(self._make_entity("expense_type", "福利费", "welfare", role="filter", confidence=0.84)) for amount in self._extract_amount_entities(query): diff --git a/server/src/app/services/ontology_rules.py b/server/src/app/services/ontology_rules.py index e0067e0..72d7865 100644 --- a/server/src/app/services/ontology_rules.py +++ b/server/src/app/services/ontology_rules.py @@ -6,6 +6,7 @@ from dataclasses import dataclass from pydantic import BaseModel, ConfigDict, Field from app.schemas.ontology import OntologyIntent, OntologyScenario +from app.services.expense_type_keywords import build_expense_type_keyword_map DATE_RANGE_PATTERN = re.compile( r"(?P\d{4}-\d{1,2}-\d{1,2})\s*(?:到|至|~|-)\s*(?P\d{4}-\d{1,2}-\d{1,2})" @@ -128,44 +129,7 @@ OPERATE_KEYWORDS = ( "删除", ) -EXPENSE_TYPE_KEYWORDS = { - "差旅": "travel", - "出差": "travel", - "住宿": "hotel", - "酒店": "hotel", - "交通": "transport", - "打车": "transport", - "网约车": "transport", - "出租车": "transport", - "出租车票": "transport", - "乘车": "transport", - "乘车费": "transport", - "用车": "transport", - "叫车": "transport", - "车资": "transport", - "的士": "transport", - "的士票": "transport", - "停车费": "transport", - "餐费": "meal", - "用餐": "meal", - "会务": "meeting", - "招待费": "entertainment", - "招待": "entertainment", - "宴请": "entertainment", - "办公费": "office", - "办公用品": "office", - "文具": "office", - "耗材": "office", - "办公耗材": "office", - "打印纸": "office", - "办公设备": "office", - "培训费": "training", - "培训": "training", - "通讯费": "communication", - "话费": "communication", - "福利费": "welfare", - "团建": "welfare", -} +EXPENSE_TYPE_KEYWORDS = build_expense_type_keyword_map() EXPENSE_NARRATIVE_KEYWORDS = ( "报销", diff --git a/server/src/app/services/orchestrator_expense_query.py b/server/src/app/services/orchestrator_expense_query.py index 3e59126..6f5b61a 100644 --- a/server/src/app/services/orchestrator_expense_query.py +++ b/server/src/app/services/orchestrator_expense_query.py @@ -74,16 +74,16 @@ EXPENSE_RISK_LEVEL_LABELS = { "medium": "中风险", "warning": "中风险", "low": "低风险", - "info": "低风险", + "info": "提示", } EXPENSE_TYPE_LABELS = { "travel": "差旅费", "hotel": "住宿费", "transport": "交通费", - "meal": "餐费", + "meal": "业务招待费", "meeting": "会务费", "entertainment": "业务招待费", - "office": "办公费", + "office": "办公用品费", "training": "培训费", "communication": "通讯费", "welfare": "福利费", diff --git a/server/src/app/services/user_agent_constants.py b/server/src/app/services/user_agent_constants.py index bf8819a..178ea51 100644 --- a/server/src/app/services/user_agent_constants.py +++ b/server/src/app/services/user_agent_constants.py @@ -35,10 +35,10 @@ EXPENSE_TYPE_LABELS = { "travel": "差旅费", "hotel": "住宿费", "transport": "交通费", - "meal": "餐费", + "meal": "业务招待费", "meeting": "会务费", "entertainment": "业务招待费", - "office": "办公费", + "office": "办公用品费", "training": "培训费", "communication": "通讯费", "welfare": "福利费", @@ -48,10 +48,10 @@ EXPENSE_TYPE_LABELS = { GROUP_SCENE_LABELS = { "travel": "差旅费", "entertainment": "业务招待费", - "meal": "伙食费", + "meal": "业务招待费", "transport": "交通费", "hotel": "住宿费", - "office": "办公费", + "office": "办公用品费", "training": "培训费", "communication": "通讯费", "welfare": "福利费", @@ -62,8 +62,12 @@ EXPENSE_SCENE_SELECTION_OPTIONS = ( ("travel", "差旅费", "出差、长途交通、住宿、差旅补贴等场景。"), ("transport", "交通费", "市内打车、停车、过路费等日常交通场景。"), ("hotel", "住宿费", "单独住宿、酒店发票等场景。"), - ("entertainment", "业务招待费", "客户接待、宴请、招待等场景。"), - ("office", "办公费", "办公用品、耗材、办公设备等采购场景。"), + ("meal", "业务招待费", "客户接待、工作餐、加班餐、餐饮票据等场景。"), + ("meeting", "会务费", "会议、论坛、会场、参会等场景。"), + ("office", "办公用品费", "办公用品、耗材、办公设备等采购场景。"), + ("training", "培训费", "培训课程、讲师费、教材、认证等场景。"), + ("communication", "通讯费", "话费、流量、宽带、网络等场景。"), + ("welfare", "福利费", "团建、体检、慰问、节日福利等场景。"), ("other", "其他费用", "暂不属于以上分类的报销场景。"), ) @@ -130,10 +134,10 @@ INFERRED_REASON_LABELS = { "travel": "出差行程", "hotel": "住宿报销", "transport": "交通出行", - "meal": "餐饮用餐", + "meal": "业务招待", "meeting": "会务活动", "entertainment": "客户接待", - "office": "办公采购", + "office": "办公用品采购", "training": "培训学习", "communication": "通讯使用", "welfare": "员工福利", diff --git a/server/src/app/services/user_agent_documents.py b/server/src/app/services/user_agent_documents.py index d56e5c8..ce556ac 100644 --- a/server/src/app/services/user_agent_documents.py +++ b/server/src/app/services/user_agent_documents.py @@ -9,16 +9,32 @@ from app.schemas.user_agent import UserAgentRequest, UserAgentReviewDocumentCard DEFAULT_GROUP_SCENE_LABELS = { "travel": "差旅费", "entertainment": "业务招待费", - "meal": "伙食费", + "meal": "业务招待费", "transport": "交通费", "hotel": "住宿费", - "office": "办公费", + "office": "办公用品费", "training": "培训费", "communication": "通讯费", "welfare": "福利费", "other": "其他费用", } +DOCUMENT_SCENE_LABELS = { + "flight_itinerary": "机票/航班行程单", + "train_ticket": "火车/高铁票", + "ship_ticket": "轮船票", + "travel_ticket": "交通出行票据", + "hotel_invoice": "酒店住宿票据", + "taxi_receipt": "出租车/网约车票据", + "transport_receipt": "乘车票据", + "parking_toll_receipt": "停车/通行费票据", + "meal_receipt": "餐饮发票", + "office_invoice": "文具/办公用品发票", + "meeting_invoice": "会议/会务票据", + "training_invoice": "培训票据", + "other": "其他票据", +} + DOCUMENT_DATE_TEXT_PATTERN = re.compile( r"(\d{4}[年/-]\d{1,2}[月/-]\d{1,2}日?(?:\s*[T ]?\s*(?:[01]?\d|2[0-3])[::][0-5]\d)?)" ) @@ -48,55 +64,55 @@ class UserAgentDocumentService: provided_type = str(item.get("document_type") or "").strip().lower() normalized_expense_type = str(expense_type_code or "").strip().lower() if provided_type: - if provided_type in {"flight_itinerary", "train_ticket"}: + if provided_type in {"flight_itinerary", "train_ticket", "ship_ticket"}: return { "document_type": provided_type, "expense_type": "travel", "group_code": "travel", - "scene_label": "差旅票据", + "scene_label": DOCUMENT_SCENE_LABELS.get(provided_type, "交通出行票据"), } if provided_type == "hotel_invoice": return { "document_type": provided_type, "expense_type": "hotel", "group_code": "travel", - "scene_label": "住宿票据", + "scene_label": DOCUMENT_SCENE_LABELS["hotel_invoice"], } - if provided_type in {"taxi_receipt", "parking_toll_receipt"}: + if provided_type in {"taxi_receipt", "transport_receipt", "parking_toll_receipt"}: return { "document_type": provided_type, "expense_type": "transport", "group_code": "travel", - "scene_label": "交通票据", + "scene_label": DOCUMENT_SCENE_LABELS.get(provided_type, "乘车票据"), } if provided_type == "meal_receipt": - group_code = "entertainment" if normalized_expense_type == "entertainment" or has_customer else "meal" + group_code = "meal" return { "document_type": provided_type, "expense_type": group_code, "group_code": group_code, - "scene_label": "餐饮票据", + "scene_label": DOCUMENT_SCENE_LABELS["meal_receipt"], } if provided_type == "office_invoice": return { "document_type": provided_type, "expense_type": "office", "group_code": "office", - "scene_label": "办公用品票据", + "scene_label": DOCUMENT_SCENE_LABELS["office_invoice"], } if provided_type == "meeting_invoice": return { "document_type": provided_type, "expense_type": "meeting", "group_code": "meeting", - "scene_label": "会务票据", + "scene_label": DOCUMENT_SCENE_LABELS["meeting_invoice"], } if provided_type == "training_invoice": return { "document_type": provided_type, "expense_type": "training", "group_code": "training", - "scene_label": "培训票据", + "scene_label": DOCUMENT_SCENE_LABELS["training_invoice"], } text = " ".join( @@ -108,41 +124,69 @@ class UserAgentDocumentService: ).lower() compact = text.replace(" ", "") - if any(keyword in compact for keyword in ("机票", "航班", "火车", "高铁", "行程单")): + if any(keyword in compact for keyword in ("火车", "高铁", "动车", "铁路", "车次")): return { - "document_type": "travel_ticket", + "document_type": "train_ticket", "expense_type": "travel", "group_code": "travel", - "scene_label": "差旅票据", + "scene_label": DOCUMENT_SCENE_LABELS["train_ticket"], + } + if any(keyword in compact for keyword in ("过路费", "停车", "通行费", "收费站")): + return { + "document_type": "parking_toll_receipt", + "expense_type": "transport", + "group_code": "travel", + "scene_label": DOCUMENT_SCENE_LABELS["parking_toll_receipt"], + } + if any(keyword in compact for keyword in ("打车", "出租车", "滴滴", "网约车", "叫车", "车费", "车资", "的士")): + return { + "document_type": "taxi_receipt", + "expense_type": "transport", + "group_code": "travel", + "scene_label": DOCUMENT_SCENE_LABELS["taxi_receipt"], + } + if any(keyword in compact for keyword in ("乘车", "用车")): + return { + "document_type": "transport_receipt", + "expense_type": "transport", + "group_code": "travel", + "scene_label": DOCUMENT_SCENE_LABELS["transport_receipt"], + } + if any(keyword in compact for keyword in ("机票", "航班", "登机", "航空", "客票")): + return { + "document_type": "flight_itinerary", + "expense_type": "travel", + "group_code": "travel", + "scene_label": DOCUMENT_SCENE_LABELS["flight_itinerary"], + } + if any(keyword in compact for keyword in ("轮船", "船票", "客轮", "渡轮", "航运")): + return { + "document_type": "ship_ticket", + "expense_type": "travel", + "group_code": "travel", + "scene_label": DOCUMENT_SCENE_LABELS["ship_ticket"], } if any(keyword in compact for keyword in ("酒店", "住宿", "宾馆")): return { "document_type": "hotel_invoice", "expense_type": "hotel", "group_code": "travel", - "scene_label": "住宿票据", - } - if any(keyword in compact for keyword in ("打车", "出租车", "滴滴", "网约车", "乘车", "用车", "叫车", "车费", "车资", "的士", "过路费", "停车")): - return { - "document_type": "transport_receipt", - "expense_type": "transport", - "group_code": "travel", - "scene_label": "交通票据", + "scene_label": DOCUMENT_SCENE_LABELS["hotel_invoice"], } if any(keyword in compact for keyword in ("餐", "饭店", "酒楼", "酒家", "餐饮", "meal")): - group_code = "entertainment" if normalized_expense_type == "entertainment" or has_customer else "meal" + group_code = "meal" return { "document_type": "meal_receipt", "expense_type": group_code, "group_code": group_code, - "scene_label": "餐饮票据", + "scene_label": DOCUMENT_SCENE_LABELS["meal_receipt"], } if any(keyword in compact for keyword in ("办公用品", "文具", "耗材", "办公耗材", "打印纸", "键盘", "鼠标", "白板", "墨盒", "硒鼓")): return { - "document_type": "other", + "document_type": "office_invoice", "expense_type": "office", "group_code": "office", - "scene_label": "办公用品票据", + "scene_label": DOCUMENT_SCENE_LABELS["office_invoice"], } return { "document_type": "other", diff --git a/server/src/app/services/user_agent_review_core.py b/server/src/app/services/user_agent_review_core.py index f7019f9..d43f353 100644 --- a/server/src/app/services/user_agent_review_core.py +++ b/server/src/app/services/user_agent_review_core.py @@ -314,10 +314,7 @@ class UserAgentReviewCoreMixin: filename=str(item.get("filename") or f"document-{index}"), document_type=classified["document_type"], suggested_expense_type=classified["expense_type"], - scene_label=GROUP_SCENE_LABELS.get( - classified["group_code"], - classified["scene_label"], - ), + scene_label=self._resolve_review_document_scene_label(item, classified), summary=str(item.get("summary") or item.get("text") or "").strip(), avg_score=float(item.get("avg_score") or 0.0), preview_kind=str(item.get("preview_kind") or "").strip(), @@ -338,6 +335,25 @@ class UserAgentReviewCoreMixin: return cards + @staticmethod + def _resolve_review_document_scene_label(item: dict[str, object], classified: dict[str, str]) -> str: + provided_label = str(item.get("document_type_label") or "").strip() + if provided_label and provided_label != "其他单据": + return provided_label + + classified_scene_label = str(classified.get("scene_label") or "").strip() + if classified_scene_label: + return classified_scene_label + + document_type = str(classified.get("document_type") or item.get("document_type") or "").strip() + document_type_label = resolve_document_type_label(document_type) + if document_type_label and document_type_label not in {"其他单据", document_type}: + return document_type_label + + scene_label = str(item.get("scene_label") or "").strip() + return scene_label or "其他票据" + + def _build_review_claim_groups( self, payload: UserAgentRequest, diff --git a/server/src/app/services/user_agent_review_profile.py b/server/src/app/services/user_agent_review_profile.py index 05bec32..0e9ea62 100644 --- a/server/src/app/services/user_agent_review_profile.py +++ b/server/src/app/services/user_agent_review_profile.py @@ -59,6 +59,20 @@ class UserAgentReviewProfileMixin: manager_name = self._resolve_manager_name(employee) reason = slot_map.get("reason").value if slot_map.get("reason") else "" attachments = "、".join(self._resolve_attachment_names(payload)) + expense_type_code = str(slot_map.get("expense_type").normalized_value if slot_map.get("expense_type") else "").strip() + customer_name = str(slot_map.get("customer_name").value if slot_map.get("customer_name") else "").strip() + merchant_name = str(slot_map.get("merchant_name").value if slot_map.get("merchant_name") else "").strip() + participants = str(slot_map.get("participants").value if slot_map.get("participants") else "").strip() + customer_slot = slot_map.get("customer_name") + participants_slot = slot_map.get("participants") + customer_required = bool( + customer_slot + and (customer_slot.required or customer_slot.status == "missing") + ) + participants_required = bool( + participants_slot + and (participants_slot.required or participants_slot.status == "missing") + ) fields = [ UserAgentReviewEditField( @@ -98,13 +112,20 @@ class UserAgentReviewProfileMixin: required=False, group="basic", ), - UserAgentReviewEditField( - key="customer_name", - label="客户名称", - value=slot_map.get("customer_name").value if slot_map.get("customer_name") else "", - placeholder="请输入客户名称", - group="business", - ), + ] + + if expense_type_code == "entertainment" or customer_required or customer_name: + fields.append( + UserAgentReviewEditField( + key="customer_name", + label="客户名称", + value=customer_name, + placeholder="请输入客户名称", + group="business", + ) + ) + + fields.append( UserAgentReviewEditField( key="business_location", label="业务地点", @@ -112,15 +133,22 @@ class UserAgentReviewProfileMixin: placeholder="例如:北京 / 客户现场", required=False, group="business", - ), - UserAgentReviewEditField( - key="merchant_name", - label="酒店/商户", - value=slot_map.get("merchant_name").value if slot_map.get("merchant_name") else "", - placeholder="请输入酒店或商户名称", - required=False, - group="business", - ), + ) + ) + + if expense_type_code == "hotel" or merchant_name: + fields.append( + UserAgentReviewEditField( + key="merchant_name", + label="酒店/商户", + value=merchant_name, + placeholder="请输入酒店或商户名称", + required=False, + group="business", + ) + ) + + fields.extend([ UserAgentReviewEditField( key="amount", label="金额", @@ -128,13 +156,20 @@ class UserAgentReviewProfileMixin: placeholder="例如:200.00元", group="business", ), - UserAgentReviewEditField( - key="participants", - label="参与人员", - value=slot_map.get("participants").value if slot_map.get("participants") else "", - placeholder="例如:客户 2 人,我方 1 人", - group="business", - ), + ]) + + if expense_type_code == "entertainment" or participants_required or participants: + fields.append( + UserAgentReviewEditField( + key="participants", + label="参与人员", + value=participants, + placeholder="例如:客户 2 人,我方 1 人", + group="business", + ) + ) + + fields.extend([ UserAgentReviewEditField( key="reason", label="事由", @@ -152,7 +187,7 @@ class UserAgentReviewProfileMixin: field_type="textarea", group="attachments", ), - ] + ]) return fields diff --git a/server/src/app/services/user_agent_review_slots.py b/server/src/app/services/user_agent_review_slots.py index 04a7f6f..8ab7a40 100644 --- a/server/src/app/services/user_agent_review_slots.py +++ b/server/src/app/services/user_agent_review_slots.py @@ -37,6 +37,7 @@ from app.services.expense_claims import ExpenseClaimService from app.services.expense_rule_runtime import ExpenseRuleRuntimeService, RuntimeTravelPolicy, resolve_document_type_label from app.services.risk_ontology_bridge import resolve_rule_codes_for_risk_check from app.services.travel_reimbursement_calculator import TravelReimbursementCalculatorService +from app.services.expense_type_keywords import resolve_expense_type_label_from_text from app.services.user_agent_constants import * @@ -568,27 +569,9 @@ class UserAgentReviewSlotMixin: @staticmethod def _normalize_expense_type_input(value: str) -> tuple[str, str]: - compact = str(value or "").replace(" ", "") - if "招待" in compact or ("客户" in compact and any(keyword in compact for keyword in ("吃饭", "用餐", "宴请", "请客"))): - return "entertainment", "业务招待费" - if any(keyword in compact for keyword in ("差旅", "出差", "机票", "行程")): - return "travel", "差旅费" - if any(keyword in compact for keyword in ("住宿", "酒店", "宾馆")): - return "hotel", "住宿费" - if any(keyword in compact for keyword in ("交通", "打车", "网约车", "出租车", "乘车", "用车", "叫车", "车费", "车资", "的士", "停车")): - return "transport", "交通费" - if any(keyword in compact for keyword in ("餐费", "用餐", "午餐", "晚餐", "早餐", "伙食")): - return "meal", "餐费" - if "会务" in compact: - return "meeting", "会务费" - if any(keyword in compact for keyword in ("办公费", "办公用品", "文具", "耗材", "办公耗材", "打印纸", "办公设备", "键盘", "鼠标", "白板")): - return "office", "办公费" - if any(keyword in compact for keyword in ("培训费", "培训", "讲师费", "课时费", "课程费")): - return "training", "培训费" - if any(keyword in compact for keyword in ("通讯费", "话费", "流量费", "宽带费")): - return "communication", "通讯费" - if any(keyword in compact for keyword in ("福利费", "团建", "慰问", "节日福利", "体检费")): - return "welfare", "福利费" + resolved = resolve_expense_type_label_from_text(value) + if resolved is not None: + return resolved return "other", str(value or "").strip() or "其他费用" diff --git a/server/src/app/services/user_agent_review_travel_policy.py b/server/src/app/services/user_agent_review_travel_policy.py index 7b7f353..1293409 100644 --- a/server/src/app/services/user_agent_review_travel_policy.py +++ b/server/src/app/services/user_agent_review_travel_policy.py @@ -137,14 +137,13 @@ class UserAgentReviewTravelPolicyMixin: continue night_count = self._extract_review_hotel_night_count(card) nightly_amount = (amount / Decimal(max(night_count, 1))).quantize(Decimal("0.01")) + if nightly_amount <= cap: + continue amount_measurement_lines.append( f"{card.filename}:识别为{document_type_label},金额 {amount:.2f} 元," f"按 {night_count} 晚折算 {nightly_amount:.2f} 元/晚;" - f"适用标准为 {band_label}{city_tier_label} {cap:.2f} 元/晚," - f"{'超出标准' if nightly_amount > cap else '测算通过'}。" + f"适用标准为 {band_label}{city_tier_label} {cap:.2f} 元/晚,超出标准。" ) - if nightly_amount <= cap: - continue basis = ( f"依据《{standard_rule_name}》({standard_rule_version}),{band_label} 在{city_tier_label}" @@ -200,12 +199,11 @@ class UserAgentReviewTravelPolicyMixin: ) continue - amount_measurement_lines.append( - f"{card.filename}:识别为{document_type_label},金额 {amount:.2f} 元;" - f"适用《{standard_rule_name}》{region_label}伙食补助标准 {standard_amount:.2f} 元/天," - f"{'超出标准' if amount > standard_amount else '测算通过'}。" - ) if amount > standard_amount: + amount_measurement_lines.append( + f"{card.filename}:识别为{document_type_label},金额 {amount:.2f} 元;" + f"适用《{standard_rule_name}》{region_label}伙食补助标准 {standard_amount:.2f} 元/天,超出标准。" + ) append_once( f"travel-meal-allowance-over-limit-{card.index}", UserAgentReviewRiskBrief( @@ -251,13 +249,6 @@ class UserAgentReviewTravelPolicyMixin: ) continue - if standard_amount is not None: - amount_measurement_lines.append( - f"{card.filename}:识别为{document_type_label},金额 {amount:.2f} 元;" - f"适用《{scene_policy.rule_name}》{metric_label}标准 {standard_amount:.2f} 元," - f"{'超出标准' if amount > standard_amount else '测算通过'}。" - ) - amount_risk = self._evaluate_review_scene_amount( amount=amount, limit_config=scene_limit, @@ -265,6 +256,11 @@ class UserAgentReviewTravelPolicyMixin: ) if amount_risk is not None: severity, threshold = amount_risk + if standard_amount is not None: + amount_measurement_lines.append( + f"{card.filename}:识别为{document_type_label},金额 {amount:.2f} 元;" + f"适用《{scene_policy.rule_name}》{metric_label}标准 {standard_amount:.2f} 元,超出标准。" + ) append_once( f"{scene_code}-amount-over-limit-{card.index}", UserAgentReviewRiskBrief( @@ -348,11 +344,11 @@ class UserAgentReviewTravelPolicyMixin: briefs.insert( 0, UserAgentReviewRiskBrief( - title="附件金额测算结果", - level="info", - content="系统已根据首轮上传附件识别金额,并匹配当前可执行的报销标准进行测算。", + title="附件金额测算异常", + level="warning", + content="系统根据首轮上传附件识别金额后,发现有需要进一步核查或说明的测算结果。", detail=";".join(dict.fromkeys(amount_measurement_lines)), - suggestion="如测算结果超标,请补充超标说明、调整金额或更正票据类型后再继续。", + suggestion="请补充超标说明、调整金额或更正票据类型后再继续。", ), ) diff --git a/server/storage/knowledge/.index.json b/server/storage/knowledge/.index.json index d95d3d7..7db7e44 100644 --- a/server/storage/knowledge/.index.json +++ b/server/storage/knowledge/.index.json @@ -15,7 +15,7 @@ "uploaded_by": "admin", "version_number": 1, "ingest_status": 1, - "ingest_status_updated_at": "2026-05-22T07:04:12.388160+00:00", + "ingest_status_updated_at": "2026-05-22T15:12:34.420412+00:00", "ingest_completed_at": "2026-05-17T10:01:33.272539+00:00", "ingest_document_name": "远光《公司支出管理办法(2024)》.pdf", "ingest_document_updated_at": "2026-05-17T09:28:28.999515+00:00", @@ -36,12 +36,12 @@ "uploaded_by": "系统导入", "version_number": 1, "ingest_status": 1, - "ingest_status_updated_at": "2026-05-22T07:03:57.851719+00:00", - "ingest_completed_at": "", - "ingest_document_name": "", - "ingest_document_updated_at": "", + "ingest_status_updated_at": "2026-05-22T15:12:34.423374+00:00", + "ingest_completed_at": "2026-05-22T09:22:26.072669+00:00", + "ingest_document_name": "远光软件会计科目使用说明.xlsx", + "ingest_document_updated_at": "2026-05-22T07:00:22.328877+00:00", "ingest_document_sha256": "", - "ingest_agent_run_id": "" + "ingest_agent_run_id": "run_8c1ab050c9734d96" }, { "id": "b0277cd76034437997fbf5219662725a", @@ -57,12 +57,12 @@ "uploaded_by": "系统导入", "version_number": 1, "ingest_status": 1, - "ingest_status_updated_at": "2026-05-22T07:03:57.861469+00:00", - "ingest_completed_at": "", - "ingest_document_name": "", - "ingest_document_updated_at": "", + "ingest_status_updated_at": "2026-05-22T15:12:34.426517+00:00", + "ingest_completed_at": "2026-05-22T09:22:52.729264+00:00", + "ingest_document_name": "远光软件财务基础知识手册.docx", + "ingest_document_updated_at": "2026-05-22T07:00:22.011016+00:00", "ingest_document_sha256": "", - "ingest_agent_run_id": "" + "ingest_agent_run_id": "run_8c1ab050c9734d96" }, { "id": "23f56f159a3e4bc3b2338056544120dd", @@ -78,12 +78,12 @@ "uploaded_by": "系统导入", "version_number": 1, "ingest_status": 1, - "ingest_status_updated_at": "2026-05-22T07:03:57.870777+00:00", - "ingest_completed_at": "", - "ingest_document_name": "", - "ingest_document_updated_at": "", + "ingest_status_updated_at": "2026-05-22T15:12:34.429968+00:00", + "ingest_completed_at": "2026-05-22T09:22:58.498888+00:00", + "ingest_document_name": "远光软件财务术语解释手册.docx", + "ingest_document_updated_at": "2026-05-22T07:00:22.352133+00:00", "ingest_document_sha256": "", - "ingest_agent_run_id": "" + "ingest_agent_run_id": "run_8c1ab050c9734d96" }, { "id": "09fbcae74d3b41e498a47e05b45262cb", @@ -99,12 +99,12 @@ "uploaded_by": "系统导入", "version_number": 1, "ingest_status": 1, - "ingest_status_updated_at": "2026-05-22T07:03:57.879239+00:00", - "ingest_completed_at": "", - "ingest_document_name": "", - "ingest_document_updated_at": "", + "ingest_status_updated_at": "2026-05-22T15:12:34.433141+00:00", + "ingest_completed_at": "2026-05-22T09:24:19.530985+00:00", + "ingest_document_name": "远光软件高新技术企业税收优惠政策汇总.pdf", + "ingest_document_updated_at": "2026-05-22T07:00:22.304623+00:00", "ingest_document_sha256": "", - "ingest_agent_run_id": "" + "ingest_agent_run_id": "run_8c1ab050c9734d96" }, { "id": "5fb3c63fbfe244a280cf3316a20150cd", diff --git a/server/storage/knowledge/.lightrag/x_financial_knowledge/graph_chunk_entity_relation.graphml b/server/storage/knowledge/.lightrag/x_financial_knowledge/graph_chunk_entity_relation.graphml index 4c381f3..5057d2f 100644 --- a/server/storage/knowledge/.lightrag/x_financial_knowledge/graph_chunk_entity_relation.graphml +++ b/server/storage/knowledge/.lightrag/x_financial_knowledge/graph_chunk_entity_relation.graphml @@ -18,10 +18,16 @@ 远光软件股份有限公司 organization - 远光软件股份有限公司is a company that issued the Company Expenditure Management Measures (2024) to regulate expenditure and reimbursement standards.<SEP>Yuan Guang Software Co., Ltd. is a company that has established internal expense reimbursement and management regulations.<SEP>YuanGuang Software Co., Ltd. is the company that issued the expense reimbursement management regulations to optimize business development, standardize expenditure and reimbursement processes, and prevent operational risks. - chunk-aa5435156b829944c173fa1d2d7a93d4<SEP>chunk-18d968b78afe916b419c1b5973421ebe<SEP>chunk-dd87aa5bc62cc9587ecb4c26d35a5263 - /app/server/storage/knowledge/报销制度/2c1cb358f08d44ceb0e4d287133206ec__远光《公司支出管理办法(2024)》.pdf - 1779011991 + 远光软件股份有限公司 is a high‑tech software company headquartered in China that operates within the information technology sector. As a recognized high‑tech enterprise, the company benefits from various tax preferential policies granted by the government, which support its ongoing innovation and development efforts. + +To ensure sound financial governance, 远光软件股份有限公司 has issued a series of internal regulations governing expenditure and reimbursement. The Company Expenditure Management Measures (2024) set out clear expenditure and reimbursement standards, while the accompanying internal expense reimbursement and management regulations formalize processes for submitting, approving, and recording expenses. These policies are designed to optimize business development, standardize expenditure and reimbursement workflows, and mitigate operational risks across the organization. + +In addition to procedural guidelines, 远光软件股份有限公司 maintains detailed financial documentation to support its internal operations. The company has published the Financial Basic Knowledge Handbook and the Financial Term Explanation Manual, which provide staff with essential knowledge of financial concepts and terminology. Correspondingly, accounting subject usage guidelines and instructions are documented for internal financial operations, ensuring consistent application of accounting classifications and facilitating accurate financial reporting. + +Through these comprehensive regulatory, procedural, and educational initiatives, 远光软件股份有限公司 demonstrates a commitment to robust financial management, operational efficiency, and compliance with both internal standards and applicable tax policies. + chunk-aa5435156b829944c173fa1d2d7a93d4<SEP>chunk-18d968b78afe916b419c1b5973421ebe<SEP>chunk-dd87aa5bc62cc9587ecb4c26d35a5263<SEP>chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-e726f44fb0287c5192cf61b350f18abb<SEP>chunk-78edb0c8ccc8238159196ecaeeb08d43<SEP>chunk-2ee7e2a66cb544bdfe1b09e133863ad1<SEP>chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/报销制度/2c1cb358f08d44ceb0e4d287133206ec__远光《公司支出管理办法(2024)》.pdf<SEP>/app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx<SEP>/app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441857 @@ -3336,6 +3342,906 @@ 1779379005 + + Excel工作簿:远光软件会计科目使用说明.xlsx + content + This is an Excel workbook document titled "远光软件会计科目使用说明.xlsx" that contains accounting subject usage instructions. + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 会计科目说明 + content + 会计科目说明is a worksheet within the Excel workbook that provides usage guidelines for accounting subjects, including account codes, names, categories, and instructions. + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 科目编码 + concept + 科目编码represents the coding system used to identify and classify accounting subjects in the document.<SEP>科目编码is the accounting subject code used to identify and classify different accounting entries. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 科目名称 + concept + 科目名称refers to the names of accounting subjects as defined in the document.<SEP>科目名称refers to the name of an accounting subject. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 科目类别 + concept + 科目类别refers to the classification categories for accounting subjects, including asset, liability, and income/expense types.<SEP>科目类别refers to the category classification of accounting subjects. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 使用说明 + concept + 使用说明provides the specific usage instructions for each accounting subject.<SEP>使用说明refers to the usage description for accounting subjects. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 库存现金 + concept + 库存现金(code 1001) is an asset-type accounting subject used for recording company cash on hand, with daily reconciliation required.<SEP>库存现金represents the cash held by a company. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 银行存款 + concept + 银行存款(code 1002) is an asset-type accounting subject used for recording various bank deposits, requiring detailed records by bank branch.<SEP>银行存款represents funds deposited in banks. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 应收账款 + concept + 应收账款(code 1122) is an asset-type accounting subject used for recording receivables from sales of goods and provision of services, requiring customer-level details.<SEP>应收账款represents amounts receivable from customers for selling goods or providing services. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 其他应收款 + data + 其他应收款(code 1221) is an asset-type accounting subject used for recording temporary receivables, including petty cash and deposits. + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 原材料 + data + 原材料(code 1403) is an asset-type accounting subject used for recording various materials in stock. + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 固定资产 + concept + 固定资产(code 1601) is an asset-type accounting subject used for recording the original value of fixed assets, requiring category-level details.<SEP>固定资产represents tangible assets with a useful life exceeding one year. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 累计折旧 + data + 累计折旧(code 1602) is an asset-type accounting subject used for recording accumulated depreciation of fixed assets, showing credit balance. + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 应付账款 + concept + 应付账款(code 2202) is a liability-type accounting subject used for recording payables for purchased goods and received services, requiring supplier-level details.<SEP>应付账款represents amounts payable for purchasing goods or receiving services. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 应交税费 + concept + 应交税费(code 2221) is a liability-type accounting subject used for recording various taxes payable, requiring tax type-level details.<SEP>应交税费represents various taxes that should be paid. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 应付职工薪酬 + concept + 应付职工薪酬(code 2211) is a liability-type accounting subject used for recording employee compensation, including social insurance and housing fund.<SEP>应付职工薪酬represents wages and benefits owed to employees. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 主营业务收入 + concept + 主营业务收入(code 6001) is an income statement-type accounting subject used for recording revenue from main business operations, requiring business type-level details.<SEP>主营业务收入represents revenue generated from main business operations. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 主营业务成本 + data + 主营业务成本(code 6401) is an income statement-type accounting subject used for recording costs of main business operations. + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 管理费用 + concept + 管理费用(code 6601) is an income statement-type accounting subject used for recording expenses related to management of business operations, requiring expense type-level details.<SEP>管理费用represents expenses incurred for managing production and operations. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 销售费用 + concept + 销售费用(code 6602) is an income statement-type accounting subject used for recording expenses related to product sales, requiring expense type-level details.<SEP>销售费用represents expenses incurred for selling products. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx<SEP>/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 财务费用 + data + 财务费用(code 6603) is an income statement-type accounting subject used for recording financial activity expenses, including interest and fees. + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 所得税费用 + data + 所得税费用(code 6801) is an income statement-type accounting subject used for recording corporate income tax expenses, including deferred income tax. + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 备注 + concept + 备注refers to remarks or additional notes for accounting subjects.<SEP>备注provides additional notes and remarks for accounting subjects, offering supplementary information about usage or specific requirements. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441741 + + + + 资产类 + concept + 资产类is the asset category for accounting subjects, used for recording resources and properties owned by the company.<SEP>资产类is a category classification for accounting subjects representing assets, including cash, receivables, inventory, and fixed assets. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 负债类 + concept + 负债类is the liability category for accounting subjects, used for recording obligations and debts.<SEP>负债类is a category classification for accounting subjects representing liabilities, including payables, taxes, and employee compensation. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 损益类 + concept + 损益类is the income and expense category for accounting subjects, used for recording revenues and expenses.<SEP>损益类is a category classification for accounting subjects used in income statements, including revenue, costs, and expenses. + chunk-31ff57cf79d009c378478f065eda9d4d<SEP>chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 远光软件会计科目使用说明.xlsx + content + 远光软件会计科目使用说明.xlsx is the Excel workbook containing the accounting subject usage instructions for远光软件股份有限公司. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 2221应交税费 + concept + 2221应交税费is an accounting subject classified under liabilities, used for recording various taxes payable. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 2211应付职工薪酬 + concept + 2211应付职工薪酬is an accounting subject classified under liabilities, used for recording employee compensation including social insurance and housing fund. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 6001主营业务收入 + concept + 6001主营业务收入is an accounting subject classified under income and expense category, used for recording revenue from main business operations. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 6401主营业务成本 + concept + 6401主营业务成本is an accounting subject classified under income and expense category, used for recording costs of main business operations. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 6601管理费用 + concept + 6601管理费用is an accounting subject classified under income and expense category, used for recording expenses related to management of business operations. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 6602销售费用 + concept + 6602销售费用is an accounting subject classified under income and expense category, used for recording expenses incurred in product sales. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 6603财务费用 + concept + 6603财务费用is an accounting subject classified under income and expense category, used for recording financing and other financial activity expenses including interest and handling fees. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 6801所得税费用 + concept + 6801所得税费用is an accounting subject classified under income and expense category, used for recording corporate income tax expenses including deferred income tax. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 1001库存现金 + concept + 1001库存现金is an accounting subject classified under assets, used for recording the company's cash on hand with daily and monthly settlement practices. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 1002银行存款 + concept + 1002银行存款is an accounting subject classified under assets, used for recording various deposits held in banks, segmented by bank account. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 1122应收账款 + concept + 1122应收账款is an accounting subject classified under assets, used for recording receivables from sales of goods or provision of services, segmented by customer. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 1221其他应收款 + concept + 1221其他应收款is an accounting subject classified under assets, used for recording various other receivables. + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 财务基础知识手册 + content + 财务基础知识手册is a handbook covering foundational accounting and tax knowledge for the organization. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441755 + + + + 会计要素 + concept + 会计要素consists of six fundamental elements: assets, liabilities, owner's equity, revenue, expenses, and profit. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441755 + + + + 资产 + concept + 资产represents the resources owned by a company that have economic value and are expected to benefit future operations. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441755 + + + + 负债 + concept + 负债represents the obligations or debts that a company owes to external parties. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441755 + + + + 所有者权益 + concept + 所有者权益represents the residual interest in the assets of a company after deducting liabilities. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441755 + + + + 收入 + concept + 收入represents the inflow of economic benefits arising from the main business activities. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441755 + + + + 费用 + concept + 费用represents the outflow of economic benefits incurred in the process of generating revenue. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441755 + + + + 利润 + concept + 利润represents the positive financial result when revenue exceeds expenses during a specific period. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 会计恒等式 + concept + 会计恒等式is the fundamental accounting equation expressed as assets equal liabilities plus owner's equity. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 增值税 + concept + 增值税is a tax levied on the value added at each stage of production and distribution.<SEP>增值税is a consumption-based tax applied to software products and technology services, subject to various preferential policies. + chunk-78edb0c8ccc8238159196ecaeeb08d43<SEP>chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx<SEP>/app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 企业所得税 + concept + 企业所得税is a tax levied on the profits of enterprises, normally at 25% with a preferential 15% rate for high-tech enterprises.<SEP>企业所得税is a type of tax that enterprises must pay on their income, subject to preferential policies under the high-tech enterprise framework. + chunk-78edb0c8ccc8238159196ecaeeb08d43<SEP>chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx<SEP>/app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 个人所得税 + concept + 个人所得税is a tax on individual income levied using progressive rates ranging from 3% to 45%. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 印花税 + concept + 印花税is a tax levied on taxable documents generated during economic activities. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 三大财务报表 + concept + 三大财务报表consists of three main financial statements: balance sheet, income statement, and cash flow statement. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 资产负债表 + concept + 资产负债表reflects a company's financial position at a specific point in time. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 利润表 + concept + 利润表reflects a company's operating results over a defined period. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 现金流量表 + concept + 现金流量表reflects the inflow and outflow of cash and cash equivalents during a specific period. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 会计基础知识 + concept + 会计基础知识is the first section of the handbook covering fundamental accounting concepts. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441756 + + + + 税务基础知识 + concept + 税务基础知识is the second section of the handbook covering major tax types. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441757 + + + + 财务报表解读 + concept + 财务报表解读is the third section of the handbook covering interpretation of financial statements. + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441757 + + + + 财务术语解释手册 + content + The Financial Term Explanation Manual is a document explaining financial and accounting terminology published by远光软件股份有限公司. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 权责发生制 + concept + Accrual basis of accounting where revenue and expenses are recognized based on when rights and obligations are established, regardless of cash receipt or payment. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 收付实现制 + concept + Cash basis of accounting where revenue and expenses are recognized based on cash receipt or payment during the current period. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 固定资产折旧 + concept + Fixed asset depreciation refers to the gradual value transfer of fixed assets due to wear and tear, calculated using the straight-line method. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 摊销 + concept + Amortization is the process of spreading intangible assets or long-term prepaid expenses over a specified period into current period expenses. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 增值税进项税额 + concept + VAT input tax refers to the value-added tax amount paid by enterprises when purchasing goods, receiving taxable services, which can be deducted from output tax. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 增值税销项税额 + concept + VAT output tax refers to the value-added tax amount collected by enterprises when selling goods or providing taxable services. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 预算 + concept + Budget is a quantified plan for enterprise activities over a specific future period, including revenue budget, expenditure budget, and capital budget. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 现金流 + concept + Cash flow refers to the amount of cash and cash equivalents flowing in and out of an enterprise during a specific period. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 毛利率 + concept + Gross profit margin is the percentage of gross profit to operating revenue, reflecting the initial profitability of enterprise products or services. Calculation formula: Gross Margin = (Operating Revenue - Operating Cost) / Operating Revenue × 100% + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 净资产收益率 + concept + Return on Equity (ROE) is the percentage of net income to shareholders' equity, reflecting the profitability of shareholders' invested capital. Calculation formula: ROE = Net Income / Shareholders' Equity × 100% + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 成本中心 + concept + Cost center is an organizational unit within an enterprise that only incurs costs without generating revenue, used for cost accounting and control. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 利润中心 + concept + Profit center is an organizational unit within an enterprise that both incurs costs and generates revenue, used for evaluating profitability. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 年限平均法 + method + The straight-line method is the depreciation calculation method used by the company for fixed asset depreciation. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 毛利润 + concept + Gross profit is the profit remaining after deducting operating costs from operating revenue, used in calculating gross margin. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441774 + + + + 营业收入 + concept + Operating revenue refers to the total revenue generated from enterprise operations, used in calculating profitability ratios. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441775 + + + + 营业成本 + concept + Operating cost refers to the costs incurred in generating operating revenue, used in calculating gross margin. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441775 + + + + 净利润 + concept + Net income is the profit remaining after all expenses and taxes are deducted from total revenue, used in calculating ROE. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441775 + + + + 股东权益 + concept + Shareholders' equity represents the residual interest in enterprise assets after deducting liabilities, used in calculating ROE. + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441775 + + + + Training Expenses + concept + Training expenses refer to costs incurred for employee development and skill training, which can be fully deducted before corporate income tax. + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441843 + + + + Corporate Income Tax + concept + Corporate income tax is a tax levied on the profits of enterprises, and certain expenses can be deducted before its calculation. + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441843 + + + + Venture Capital Deduction + concept + Venture capital deduction is a preferential tax policy that allows deduction of a portion of investment amounts from taxable income. + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441843 + + + + Small And Medium High-Tech Enterprises + organization + Small and medium high-tech enterprises refer to unlisted small and medium-sized enterprises engaged in high technology industries that qualify for special tax incentives. + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441843 + + + + Taxable Income + concept + Taxable income represents the income amount subject to taxation, from which certain deductions can be made. + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441843 + + + + Preferential Tax Policies + concept + Preferential tax policies are tax benefits and incentives provided by the government to support specific industries or activities. + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441843 + + + + Investment Amount + concept + Investment amount refers to the capital invested into enterprises, which forms the basis for calculating tax deductions. + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441843 + + + + 70% Deduction Rate + data + 70% deduction rate is the percentage of investment amount that can be deducted from taxable income for investments in unlisted small and medium high-tech enterprises. + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441843 + + + + Other Preferential Policies + concept + Other preferential policies is a section category in tax regulations that includes provisions such as venture capital deduction. + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 主管税务机关 + organization + 主管税务机关is the competent tax authority responsible for reviewing and approving tax preferential policy applications from enterprises. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 高新技术企业证书 + content + 高新技术企业证书is a certification document that qualifies enterprises for high-tech enterprise tax preferential treatment when within its validity period. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 高新技术企业减按15%税率征收企业所得税 + concept + 高新技术企业减按15%税率征收企业所得税is a preferential tax policy allowing high-tech enterprises to pay corporate income tax at a reduced rate of 15% instead of the standard rate. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 技术转让所得优惠 + concept + 技术转让所得优惠is a preferential policy where qualified technology transfer income below 5 million yuan is exempt from corporate income tax, and income exceeding 5 million yuan is taxed at half rate. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 软件产品增值税即征即退 + concept + 软件产品增值税即征即退is a preferential policy where enterprises selling self-developed software products are refunded the portion of VAT that exceeds 3% of actual tax burden after paying at 13% rate. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 技术服务免征增值税 + concept + 技术服务免征增值税is a preferential policy exempting qualified technology transfer, development, and related technical consulting and service activities from VAT. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 研发费用加计扣除 + concept + 研发费用加计扣除is a policy allowing general enterprises to deduct R&D expenses at 100% extra, with intangible assets formed being amortized at 200%. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 固定资产加速折旧 + concept + 固定资产加速折旧is a preferential policy allowing enterprises to shorten depreciation periods or accelerate depreciation of fixed assets. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 软件企业职工培训费用 + concept + 软件企业职工培训费用is a policy allowing software enterprises to fully deduct employee training expenses from corporate income tax before tax. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 创业投资抵扣 + concept + 创业投资抵扣is a policy allowing venture capital investments in unlisted small and medium-sized high-tech enterprises to deduct 70% of the investment amount from taxable income. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 中小高新技术企业 + concept + 中小高新技术企业refers to small and medium-sized high-tech enterprises that qualify for venture capital deduction policies. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441830 + + + + 13%税率 + data + 13%税率is the standard VAT rate applied to software products under the immediate collection and refund policy. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441831 + + + + 3%实际税负 + data + 3%实际税负is the threshold tax burden level used to determine VAT refunds under the software product immediate collection and refund policy. + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441831 + + 1.0 远光软件股份有限公司is the issuer of the expense reimbursement regulations, with Chapter 1 General Provisions establishing the foundational framework. @@ -3381,6 +4287,123 @@ 1779012086 + + 1.0 + 远光软件股份有限公司owns and publishes the Excel workbook containing accounting subject guidelines. + corporate publication,document ownership + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 1.0 + 远光软件股份有限公司owns and uses the Excel workbook远光软件会计科目使用说明.xlsx for accounting subject management. + company resource,document ownership + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 1.0 + 2221应交税费is used by远光软件股份有限公司to record tax liabilities. + liability recording,subject usage + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441743 + + + + 1.0 + 2211应付职工薪酬is used by远光软件股份有限公司to record employee compensation liabilities. + liability recording,subject usage + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441743 + + + + 1.0 + 6001主营业务收入is used by远光软件股份有限公司to record main business revenue. + income recording,subject usage + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441743 + + + + 1.0 + 6401主营业务成本is used by远光软件股份有限公司to record main business costs. + expense recording,subject usage + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441744 + + + + 1.0 + 1001库存现金is used by远光软件股份有限公司to record cash on hand. + asset recording,subject usage + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441744 + + + + 1.0 + 1002银行存款is used by远光软件股份有限公司to record bank deposits. + asset recording,subject usage + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441744 + + + + 1.0 + 1122应收账款is used by远光软件股份有限公司to record accounts receivable. + asset recording,subject usage + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441745 + + + + 1.0 + 1221其他应收款is used by远光软件股份有限公司to record other receivables. + asset recording,subject usage + chunk-e726f44fb0287c5192cf61b350f18abb + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441745 + + + + 1.0 + 远光软件股份有限公司authored the财务基础知识手册as an internal training document. + authorship,corporate documentation + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441770 + + + + 1.0 + 远光软件股份有限公司published the Financial Term Explanation Manual as an official company document. + corporate authorship,publication + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441775 + + + + 1.0 + 远光软件股份有限公司adopts the straight-line method for calculating fixed asset depreciation. + accounting method,depreciation calculation + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441776 + + 1.0 Article 3 Management Principles provides the foundational principles for the entire expense reimbursement system. @@ -3921,5 +4944,266 @@ 1779379017 + + 1.0 + The Excel workbook contains the worksheet "会计科目说明" as its first sheet. + workbook structure,worksheet inclusion + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441744 + + + + 1.0 + The worksheet includes "科目编码" as a column header for organizing accounting subject codes. + data classification,worksheet column + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 1.0 + The worksheet includes "科目名称" as a column header for naming accounting subjects. + data classification,worksheet column + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 1.0 + The worksheet includes "科目类别" as a column header for categorizing accounting subjects. + data classification,worksheet column + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441743 + + + + 1.0 + The worksheet includes "使用说明" as a column header for describing accounting subject usage. + data classification,worksheet column + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441743 + + + + 1.0 + The worksheet includes "备注" as a column header for additional notes on accounting subjects. + data classification,worksheet column + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441743 + + + + 1.0 + 库存现金is classified under the asset category (资产类) in the accounting system. + account category classification,asset type + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 1.0 + 银行存款is classified under the asset category (资产类 + account category classification,asset type + chunk-31ff57cf79d009c378478f065eda9d4d + /app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx + 1779441742 + + + + 1.0 + 会计基础知识is the first section of财务基础知识手册. + content organization,document structure + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441771 + + + + 1.0 + 税务基础知识is the second section of财务基础知识手册. + content organization,document structure + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441771 + + + + 1.0 + 财务报表解读is the third section of财务基础知识手册. + content organization,document structure + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441771 + + + + 1.0 + 资产is one of the six accounting elements representing economic resources owned by the company. + accounting framework,conceptual component + chunk-78edb0c8ccc8238159196ecaeeb08d43 + /app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx + 1779441770 + + + + 1.0 + The manual explains权责发生制as the accrual basis of accounting principle. + content explanation,terminology definition + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441776 + + + + 1.0 + The manual explains收付实现制as the cash basis of accounting principle. + content explanation,terminology definition + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441776 + + + + 1.0 + The manual explains固定资产折旧as fixed asset depreciation using straight-line method. + content explanation,terminology definition + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441776 + + + + 1.0 + The manual explains摊销as the process of amortizing intangible assets over time. + content explanation,terminology definition + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441790 + + + + 1.0 + The manual explains增值税进项税额as VAT input tax deductible from + content explanation,terminology definition + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441791 + + + + 1.0 + Fixed asset depreciation is calculated using the straight-line method as specified by the company. + accounting standard,depreciation method + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441790 + + + + 1.0 + Gross profit margin is calculated by dividing gross profit by operating revenue. + profitability measure,ratio component + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441776 + + + + 1.0 + Gross margin calculation uses operating revenue as the denominator. + ratio component,revenue basis + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441776 + + + + 1.0 + Gross margin calculation subtracts operating cost from operating revenue in the numerator. + cost deduction,ratio component + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441776 + + + + 1.0 + ROE is calculated by dividing net income by shareholders' equity. + profitability measure,ratio component + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441776 + + + + 1.0 + ROE calculation uses shareholders' equity as the denominator. + equity basis,ratio component + chunk-2ee7e2a66cb544bdfe1b09e133863ad1 + /app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx + 1779441776 + + + + 1.0 + Training expenses can be fully deducted before calculating corporate income tax liability. + deductible expense,tax deduction + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441857 + + + + 1.0 + Venture capital deduction allows a percentage of the investment to be deducted from taxable income. + deductible amount,tax reduction + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441857 + + + + 1.0 + Venture capital deduction applies specifically to investments made in unlisted small and medium high-tech enterprises. + investment target,qualifying entity + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441858 + + + + 1.0 + Venture capital deduction is part of the broader preferential tax policies offered by the government. + policy category,tax incentive + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441858 + + + + 1.0 + The venture capital deduction allows 70% of the investment amount to be deducted from taxable income. + deduction ratio,tax calculation basis + chunk-93d2389cdb74257e90201dccbc3f6539 + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441858 + + + + 1.0 + The software product VAT immediate collection and refund policy uses 3% actual tax burden as the threshold - amounts exceeding 3% are refunded to enterprises. + VAT refund calculation,policy threshold + chunk-2c8384b328272063de4dac306a52d21e + /app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf + 1779441857 + + diff --git a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_doc_status.json b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_doc_status.json index 9131734..997778d 100644 --- a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_doc_status.json +++ b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_doc_status.json @@ -63,5 +63,75 @@ "original_doc_id": "a8f8465df08e455ebe133351721d49f8", "original_track_id": "insert_20260519_155957_88c49850" } + }, + "c7601043d9944ef2bcf4d3f67ed253f7": { + "status": "processed", + "chunks_count": 2, + "chunks_list": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "content_summary": "# Excel 工作簿:远光软件会计科目使用说明.xlsx\n\n## 工作表 1:会计科目说明\n\n| 远光软件股份有限公司常用会计科目使用说明 | 列2 | 列3 | 列4 | 列5 |\n| --- | --- | --- | --- | --- |\n| 科目编码 | 科目名称 | 科目类别 | 使用说明 | 备注 |\n| 1001 | 库存现金 | 资产类 | 核算公司库存现金 | 日清月结 |\n| 1002 | 银行存款 | 资产类 | 核算存入银行的各项存款 | 按开户行明细 |\n| 112...", + "content_length": 2808, + "created_at": "2026-05-22T09:21:01.230400+00:00", + "updated_at": "2026-05-22T09:22:25.565409+00:00", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx", + "track_id": "insert_20260522_092101_e754a15e", + "metadata": { + "processing_start_time": 1779441661, + "processing_end_time": 1779441745 + } + }, + "b0277cd76034437997fbf5219662725a": { + "status": "processed", + "chunks_count": 1, + "chunks_list": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "content_summary": "远光软件股份有限公司\n财务基础知识手册\n第一部分 会计基础知识\n一、会计要素\n会计要素包括:资产、负债、所有者权益、收入、费用和利润。\n会计恒等式:资产 = 负债 + 所有者权益\n二、常用会计科目\n科目类别\n科目名称\n说明\n资产类\n库存现金\n公司持有的现金\n资产类\n银行存款\n存放在银行的资金\n资产类\n应收账款\n因销售商品或提供劳务应收的款项\n资产类\n固定资产\n使用年限超过一年的有形资产\n负债类\n应付账款\n因购买商品或接受劳务应付的款项\n负债类\n应交税费\n应缴纳的各种税费\n负债类\n应付职工薪酬\n...", + "content_length": 1082, + "created_at": "2026-05-22T09:22:31.538281+00:00", + "updated_at": "2026-05-22T09:22:52.110824+00:00", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx", + "track_id": "insert_20260522_092231_e1b9d415", + "metadata": { + "processing_start_time": 1779441751, + "processing_end_time": 1779441772 + } + }, + "23f56f159a3e4bc3b2338056544120dd": { + "status": "processed", + "chunks_count": 1, + "chunks_list": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "content_summary": "远光软件股份有限公司\n财务术语解释手册\n权责发生制\n以权利和责任的发生来决定收入和费用归属期的会计基础。即凡是当期已经实现的收入和已经发生或应当负担的费用,不论款项是否收付,都应当作为当期的收入和费用。\n收付实现制\n以现金收到或付出为标准来记录收入的实现和费用的发生。即凡是当期收到和支付的现金,都作为当期的收入和费用。\n固定资产折旧\n固定资产在使用过程中因磨损而逐渐转移的价值。公司采用年限平均法计提折旧。\n摊销\n将无形资产或长期待摊费用按照规定期限分期计入当期损益的过程。\n增值税进项税额\n企业购...", + "content_length": 1040, + "created_at": "2026-05-22T09:22:44.268551+00:00", + "updated_at": "2026-05-22T09:23:11.334499+00:00", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx", + "track_id": "insert_20260522_092244_2888d301", + "metadata": { + "processing_start_time": 1779441764, + "processing_end_time": 1779441791 + } + }, + "09fbcae74d3b41e498a47e05b45262cb": { + "status": "processed", + "chunks_count": 2, + "chunks_list": [ + "chunk-2c8384b328272063de4dac306a52d21e", + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "content_summary": "远光软件股份有限公司高新技术企业税收优惠政策汇总\n\n 远光软件股份有限公司\n\n 2024年度\n\n一、企业所得税优惠\n\n1. 高新技术企业减按15%税率征收企业所得税\n\n- 条件:取得高新技术企业证书且在有效期内\n\n- 申请:向主管税务机关备案\n\n2. 技术转让所得优惠\n\n- 符合条件的技术转让所得500万元以下免征...", + "content_length": 1772, + "created_at": "2026-05-22T09:23:17.399741+00:00", + "updated_at": "2026-05-22T09:24:18.933073+00:00", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf", + "track_id": "insert_20260522_092317_ca603a9e", + "metadata": { + "processing_start_time": 1779441797, + "processing_end_time": 1779441858 + } } } \ No newline at end of file diff --git a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_entity_chunks.json b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_entity_chunks.json index 980bff3..14dc0c6 100644 --- a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_entity_chunks.json +++ b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_entity_chunks.json @@ -3,11 +3,15 @@ "chunk_ids": [ "chunk-aa5435156b829944c173fa1d2d7a93d4", "chunk-18d968b78afe916b419c1b5973421ebe", - "chunk-dd87aa5bc62cc9587ecb4c26d35a5263" + "chunk-dd87aa5bc62cc9587ecb4c26d35a5263", + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-e726f44fb0287c5192cf61b350f18abb", + "chunk-78edb0c8ccc8238159196ecaeeb08d43", + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1", + "chunk-2c8384b328272063de4dac306a52d21e" ], - "count": 3, - "create_time": 1779011991, - "update_time": 1779011991, + "count": 8, + "update_time": 1779441830, "_id": "远光软件股份有限公司" }, "第一章总则": { @@ -3359,5 +3363,913 @@ "create_time": 1779379005, "update_time": 1779379005, "_id": "Warning Icon" + }, + "Excel工作簿:远光软件会计科目使用说明.xlsx": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "Excel工作簿:远光软件会计科目使用说明.xlsx" + }, + "会计科目说明": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "会计科目说明" + }, + "科目编码": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 2, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "科目编码" + }, + "科目名称": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 2, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "科目名称" + }, + "科目类别": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 2, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "科目类别" + }, + "使用说明": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 2, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "使用说明" + }, + "库存现金": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 2, + "update_time": 1779441756, + "_id": "库存现金" + }, + "银行存款": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 2, + "update_time": 1779441756, + "_id": "银行存款" + }, + "应收账款": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 2, + "update_time": 1779441756, + "_id": "应收账款" + }, + "其他应收款": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "其他应收款" + }, + "原材料": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "原材料" + }, + "固定资产": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 2, + "update_time": 1779441756, + "_id": "固定资产" + }, + "累计折旧": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "累计折旧" + }, + "应付账款": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 2, + "update_time": 1779441756, + "_id": "应付账款" + }, + "应交税费": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 2, + "update_time": 1779441756, + "_id": "应交税费" + }, + "应付职工薪酬": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 2, + "update_time": 1779441756, + "_id": "应付职工薪酬" + }, + "主营业务收入": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 2, + "update_time": 1779441756, + "_id": "主营业务收入" + }, + "主营业务成本": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "主营业务成本" + }, + "管理费用": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 2, + "update_time": 1779441756, + "_id": "管理费用" + }, + "销售费用": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 2, + "update_time": 1779441756, + "_id": "销售费用" + }, + "财务费用": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "财务费用" + }, + "所得税费用": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "所得税费用" + }, + "备注": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 2, + "create_time": 1779441741, + "update_time": 1779441741, + "_id": "备注" + }, + "资产类": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 2, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "资产类" + }, + "负债类": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 2, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "负债类" + }, + "损益类": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d", + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 2, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "损益类" + }, + "远光软件会计科目使用说明.xlsx": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "远光软件会计科目使用说明.xlsx" + }, + "2221应交税费": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "2221应交税费" + }, + "2211应付职工薪酬": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "2211应付职工薪酬" + }, + "6001主营业务收入": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "6001主营业务收入" + }, + "6401主营业务成本": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "6401主营业务成本" + }, + "6601管理费用": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "6601管理费用" + }, + "6602销售费用": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "6602销售费用" + }, + "6603财务费用": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "6603财务费用" + }, + "6801所得税费用": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "6801所得税费用" + }, + "1001库存现金": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "1001库存现金" + }, + "1002银行存款": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "1002银行存款" + }, + "1122应收账款": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "1122应收账款" + }, + "1221其他应收款": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "1221其他应收款" + }, + "财务基础知识手册": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441755, + "update_time": 1779441755, + "_id": "财务基础知识手册" + }, + "会计要素": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441755, + "update_time": 1779441755, + "_id": "会计要素" + }, + "资产": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441755, + "update_time": 1779441755, + "_id": "资产" + }, + "负债": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441755, + "update_time": 1779441755, + "_id": "负债" + }, + "所有者权益": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441755, + "update_time": 1779441755, + "_id": "所有者权益" + }, + "收入": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441755, + "update_time": 1779441755, + "_id": "收入" + }, + "费用": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441755, + "update_time": 1779441755, + "_id": "费用" + }, + "利润": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441756, + "update_time": 1779441756, + "_id": "利润" + }, + "会计恒等式": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441756, + "update_time": 1779441756, + "_id": "会计恒等式" + }, + "增值税": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43", + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 2, + "update_time": 1779441830, + "_id": "增值税" + }, + "企业所得税": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43", + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 2, + "update_time": 1779441830, + "_id": "企业所得税" + }, + "个人所得税": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441756, + "update_time": 1779441756, + "_id": "个人所得税" + }, + "印花税": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441756, + "update_time": 1779441756, + "_id": "印花税" + }, + "三大财务报表": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441756, + "update_time": 1779441756, + "_id": "三大财务报表" + }, + "资产负债表": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441756, + "update_time": 1779441756, + "_id": "资产负债表" + }, + "利润表": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441756, + "update_time": 1779441756, + "_id": "利润表" + }, + "现金流量表": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441756, + "update_time": 1779441756, + "_id": "现金流量表" + }, + "会计基础知识": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441756, + "update_time": 1779441756, + "_id": "会计基础知识" + }, + "税务基础知识": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441757, + "update_time": 1779441757, + "_id": "税务基础知识" + }, + "财务报表解读": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441757, + "update_time": 1779441757, + "_id": "财务报表解读" + }, + "财务术语解释手册": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "财务术语解释手册" + }, + "权责发生制": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "权责发生制" + }, + "收付实现制": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "收付实现制" + }, + "固定资产折旧": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "固定资产折旧" + }, + "摊销": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "摊销" + }, + "增值税进项税额": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "增值税进项税额" + }, + "增值税销项税额": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "增值税销项税额" + }, + "预算": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "预算" + }, + "现金流": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "现金流" + }, + "毛利率": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "毛利率" + }, + "净资产收益率": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "净资产收益率" + }, + "成本中心": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "成本中心" + }, + "利润中心": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "利润中心" + }, + "年限平均法": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "年限平均法" + }, + "毛利润": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441774, + "update_time": 1779441774, + "_id": "毛利润" + }, + "营业收入": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441775, + "update_time": 1779441775, + "_id": "营业收入" + }, + "营业成本": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441775, + "update_time": 1779441775, + "_id": "营业成本" + }, + "净利润": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441775, + "update_time": 1779441775, + "_id": "净利润" + }, + "股东权益": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441775, + "update_time": 1779441775, + "_id": "股东权益" + }, + "Training Expenses": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441843, + "update_time": 1779441843, + "_id": "Training Expenses" + }, + "Corporate Income Tax": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441843, + "update_time": 1779441843, + "_id": "Corporate Income Tax" + }, + "Venture Capital Deduction": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441843, + "update_time": 1779441843, + "_id": "Venture Capital Deduction" + }, + "Small And Medium High-Tech Enterprises": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441843, + "update_time": 1779441843, + "_id": "Small And Medium High-Tech Enterprises" + }, + "Taxable Income": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441843, + "update_time": 1779441843, + "_id": "Taxable Income" + }, + "Preferential Tax Policies": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441843, + "update_time": 1779441843, + "_id": "Preferential Tax Policies" + }, + "Investment Amount": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441843, + "update_time": 1779441843, + "_id": "Investment Amount" + }, + "70% Deduction Rate": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441843, + "update_time": 1779441843, + "_id": "70% Deduction Rate" + }, + "Other Preferential Policies": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "Other Preferential Policies" + }, + "主管税务机关": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "主管税务机关" + }, + "高新技术企业证书": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "高新技术企业证书" + }, + "高新技术企业减按15%税率征收企业所得税": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "高新技术企业减按15%税率征收企业所得税" + }, + "技术转让所得优惠": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "技术转让所得优惠" + }, + "软件产品增值税即征即退": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "软件产品增值税即征即退" + }, + "技术服务免征增值税": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "技术服务免征增值税" + }, + "研发费用加计扣除": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "研发费用加计扣除" + }, + "固定资产加速折旧": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "固定资产加速折旧" + }, + "软件企业职工培训费用": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "软件企业职工培训费用" + }, + "创业投资抵扣": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "创业投资抵扣" + }, + "中小高新技术企业": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441830, + "update_time": 1779441830, + "_id": "中小高新技术企业" + }, + "13%税率": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441831, + "update_time": 1779441831, + "_id": "13%税率" + }, + "3%实际税负": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441831, + "update_time": 1779441831, + "_id": "3%实际税负" } } \ No newline at end of file diff --git a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_docs.json b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_docs.json index c1b40ba..75ce389 100644 --- a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_docs.json +++ b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_docs.json @@ -12,5 +12,33 @@ "create_time": 1779206397, "update_time": 1779206397, "_id": "a8f8465df08e455ebe133351721d49f8" + }, + "c7601043d9944ef2bcf4d3f67ed253f7": { + "content": "# Excel 工作簿:远光软件会计科目使用说明.xlsx\n\n## 工作表 1:会计科目说明\n\n| 远光软件股份有限公司常用会计科目使用说明 | 列2 | 列3 | 列4 | 列5 |\n| --- | --- | --- | --- | --- |\n| 科目编码 | 科目名称 | 科目类别 | 使用说明 | 备注 |\n| 1001 | 库存现金 | 资产类 | 核算公司库存现金 | 日清月结 |\n| 1002 | 银行存款 | 资产类 | 核算存入银行的各项存款 | 按开户行明细 |\n| 1122 | 应收账款 | 资产类 | 核算因销售商品/提供劳务应收款项 | 按客户明细 |\n| 1221 | 其他应收款 | 资产类 | 核算应收暂付款项 | 含备用金、押金 |\n| 1403 | 原材料 | 资产类 | 核算库存的各种材料 | |\n| 1601 | 固定资产 | 资产类 | 核算固定资产原值 | 按类别明细 |\n| 1602 | 累计折旧 | 资产类 | 核算固定资产累计折旧 | 贷方余额 |\n| 2202 | 应付账款 | 负债类 | 核算因购买商品/接受劳务应付款项 | 按供应商明细 |\n| 2221 | 应交税费 | 负债类 | 核算应缴纳的各种税费 | 按税种明细 |\n| 2211 | 应付职工薪酬 | 负债类 | 核算应付给职工的薪酬 | 含社保公积金 |\n| 6001 | 主营业务收入 | 损益类 | 核算主要经营业务产生的收入 | 按业务类型明细 |\n| 6401 | 主营业务成本 | 损益类 | 核算主要经营业务发生的成本 | |\n| 6601 | 管理费用 | 损益类 | 核算为管理生产经营发生的费用 | 按费用类型明细 |\n| 6602 | 销售费用 | 损益类 | 核算为销售产品发生的费用 | 按费用类型明细 |\n| 6603 | 财务费用 | 损益类 | 核算筹资等财务活动费用 | 含利息、手续费 |\n| 6801 | 所得税费用 | 损益类 | 核算企业所得税费用 | 含递延所得税 |\n\n### 行级检索线索\n\n- 会计科目说明 第 2 行:远光软件股份有限公司常用会计科目使用说明=科目编码;列2=科目名称;列3=科目类别;列4=使用说明;列5=备注\n\n- 会计科目说明 第 3 行:远光软件股份有限公司常用会计科目使用说明=1001;列2=库存现金;列3=资产类;列4=核算公司库存现金;列5=日清月结\n\n- 会计科目说明 第 4 行:远光软件股份有限公司常用会计科目使用说明=1002;列2=银行存款;列3=资产类;列4=核算存入银行的各项存款;列5=按开户行明细\n\n- 会计科目说明 第 5 行:远光软件股份有限公司常用会计科目使用说明=1122;列2=应收账款;列3=资产类;列4=核算因销售商品/提供劳务应收款项;列5=按客户明细\n\n- 会计科目说明 第 6 行:远光软件股份有限公司常用会计科目使用说明=1221;列2=其他应收款;列3=资产类;列4=核算应收暂付款项;列5=含备用金、押金\n\n- 会计科目说明 第 7 行:远光软件股份有限公司常用会计科目使用说明=1403;列2=原材料;列3=资产类;列4=核算库存的各种材料\n\n- 会计科目说明 第 8 行:远光软件股份有限公司常用会计科目使用说明=1601;列2=固定资产;列3=资产类;列4=核算固定资产原值;列5=按类别明细\n\n- 会计科目说明 第 9 行:远光软件股份有限公司常用会计科目使用说明=1602;列2=累计折旧;列3=资产类;列4=核算固定资产累计折旧;列5=贷方余额\n\n- 会计科目说明 第 10 行:远光软件股份有限公司常用会计科目使用说明=2202;列2=应付账款;列3=负债类;列4=核算因购买商品/接受劳务应付款项;列5=按供应商明细\n\n- 会计科目说明 第 11 行:远光软件股份有限公司常用会计科目使用说明=2221;列2=应交税费;列3=负债类;列4=核算应缴纳的各种税费;列5=按税种明细\n\n- 会计科目说明 第 12 行:远光软件股份有限公司常用会计科目使用说明=2211;列2=应付职工薪酬;列3=负债类;列4=核算应付给职工的薪酬;列5=含社保公积金\n\n- 会计科目说明 第 13 行:远光软件股份有限公司常用会计科目使用说明=6001;列2=主营业务收入;列3=损益类;列4=核算主要经营业务产生的收入;列5=按业务类型明细\n\n- 会计科目说明 第 14 行:远光软件股份有限公司常用会计科目使用说明=6401;列2=主营业务成本;列3=损益类;列4=核算主要经营业务发生的成本\n\n- 会计科目说明 第 15 行:远光软件股份有限公司常用会计科目使用说明=6601;列2=管理费用;列3=损益类;列4=核算为管理生产经营发生的费用;列5=按费用类型明细\n\n- 会计科目说明 第 16 行:远光软件股份有限公司常用会计科目使用说明=6602;列2=销售费用;列3=损益类;列4=核算为销售产品发生的费用;列5=按费用类型明细\n\n- 会计科目说明 第 17 行:远光软件股份有限公司常用会计科目使用说明=6603;列2=财务费用;列3=损益类;列4=核算筹资等财务活动费用;列5=含利息、手续费\n\n- 会计科目说明 第 18 行:远光软件股份有限公司常用会计科目使用说明=6801;列2=所得税费用;列3=损益类;列4=核算企业所得税费用;列5=含递延所得税\n\n# 问答线索补充\n\n以下内容由入库阶段根据章节标题、条款、列表、键值对与相邻正文提炼,供问答检索时优先命中更短、更直接的制度依据。\n\n- 正文:# Excel 工作簿:远光软件会计科目使用说明.xlsx\n- 正文:会计科目说明 第 2 行:远光软件股份有限公司常用会计科目使用说明=科目编码\n- 正文:列2=科目名称\n- 正文:列3=科目类别\n- 正文:列4=使用说明\n- 正文:列5=备注\n- 正文:会计科目说明 第 3 行:远光软件股份有限公司常用会计科目使用说明=1001\n- 正文:列2=库存现金\n- 正文:列3=资产类\n- 正文:列4=核算公司库存现金\n- 正文:列5=日清月结\n- 正文:会计科目说明 第 4 行:远光软件股份有限公司常用会计科目使用说明=1002\n- 正文:列2=银行存款\n- 正文:列4=核算存入银行的各项存款\n- 正文:列5=按开户行明细\n- 正文:会计科目说明 第 5 行:远光软件股份有限公司常用会计科目使用说明=1122\n- 正文:列2=应收账款\n- 正文:列4=核算因销售商品/提供劳务应收款项\n- 正文:列5=按客户明细\n- 正文:会计科目说明 第 6 行:远光软件股份有限公司常用会计科目使用说明=1221\n- 正文:列2=其他应收款", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx", + "create_time": 1779441661, + "update_time": 1779441661, + "_id": "c7601043d9944ef2bcf4d3f67ed253f7" + }, + "b0277cd76034437997fbf5219662725a": { + "content": "远光软件股份有限公司\n财务基础知识手册\n第一部分 会计基础知识\n一、会计要素\n会计要素包括:资产、负债、所有者权益、收入、费用和利润。\n会计恒等式:资产 = 负债 + 所有者权益\n二、常用会计科目\n科目类别\n科目名称\n说明\n资产类\n库存现金\n公司持有的现金\n资产类\n银行存款\n存放在银行的资金\n资产类\n应收账款\n因销售商品或提供劳务应收的款项\n资产类\n固定资产\n使用年限超过一年的有形资产\n负债类\n应付账款\n因购买商品或接受劳务应付的款项\n负债类\n应交税费\n应缴纳的各种税费\n负债类\n应付职工薪酬\n应付给职工的工资、福利等\n损益类\n主营业务收入\n主要经营业务产生的收入\n损益类\n管理费用\n为管理生产经营发生的费用\n损益类\n销售费用\n为销售产品发生的费用\n第二部分 税务基础知识\n三、主要税种介绍\n(一)增值税:公司为一般纳税人,软件服务适用6%税率,软件产品销售适用13%税率。\n(二)企业所得税:税率为25%,高新技术企业享受15%优惠税率。\n(三)个人所得税:按累进税率3%-45%,由公司代扣代缴。\n(四)印花税:对经济活动中的应税凭证征收。\n第三部分 财务报表解读\n四、三大财务报表\n(一)资产负债表:反映企业在某一特定日期的财务状况。\n(二)利润表:反映企业在一定期间的经营成果。\n(三)现金流量表:反映企业在一定期间现金和现金等价物的流入和流出。\n\n# 章节导航\n\n以下内容由入库阶段从制度原文中提取,供检索时优先理解制度层级、条目和标准所在章节。\n\n- 一、会计要素\n- 二、常用会计科目\n- (四)印花税:对经济活动中的应税凭证征收。\n\n# 重点章节摘录\n\n## 一、会计要素\n\n会计要素包括:资产、负债、所有者权益、收入、费用和利润。;会计恒等式:资产 = 负债 + 所有者权益\n\n## 二、常用会计科目\n\n科目类别;科目名称;说明\n\n## (四)印花税:对经济活动中的应税凭证征收。\n\n第三部分 财务报表解读\n\n# 问答线索补充\n\n以下内容由入库阶段根据章节标题、条款、列表、键值对与相邻正文提炼,供问答检索时优先命中更短、更直接的制度依据。\n\n- 一、会计要素:会计要素包括:资产、负债、所有者权益、收入、费用和利润\n- 一、会计要素:会计恒等式:资产 = 负债 + 所有者权益\n- 二、常用会计科目:因销售商品或提供劳务应收的款项\n- 二、常用会计科目:因购买商品或接受劳务应付的款项\n- 二、常用会计科目:应缴纳的各种税费\n- 二、常用会计科目:应付职工薪酬\n- (四)印花税:对经济活动中的应税凭证征收。:第三部分 财务报表解读", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx", + "create_time": 1779441751, + "update_time": 1779441751, + "_id": "b0277cd76034437997fbf5219662725a" + }, + "23f56f159a3e4bc3b2338056544120dd": { + "content": "远光软件股份有限公司\n财务术语解释手册\n权责发生制\n以权利和责任的发生来决定收入和费用归属期的会计基础。即凡是当期已经实现的收入和已经发生或应当负担的费用,不论款项是否收付,都应当作为当期的收入和费用。\n收付实现制\n以现金收到或付出为标准来记录收入的实现和费用的发生。即凡是当期收到和支付的现金,都作为当期的收入和费用。\n固定资产折旧\n固定资产在使用过程中因磨损而逐渐转移的价值。公司采用年限平均法计提折旧。\n摊销\n将无形资产或长期待摊费用按照规定期限分期计入当期损益的过程。\n增值税进项税额\n企业购进货物、接受应税劳务或应税服务支付的增值税额,可以从销项税额中抵扣。\n增值税销项税额\n企业销售货物、提供应税劳务或应税服务收取的增值税额。\n预算\n企业对未来一定时期内经营活动的数量化计划,包括收入预算、支出预算、资本预算等。\n现金流\n企业在一定期间内现金和现金等价物流入和流出的数量。\n毛利率\n毛利润占营业收入的百分比,反映企业产品或服务的初始盈利能力。计算公式:毛利率 = (营业收入 - 营业成本)/ 营业收入 × 100%\n净资产收益率(ROE)\n净利润占股东权益的百分比,反映股东投入资金的获利能力。计算公式:ROE = 净利润 / 股东权益 × 100%\n成本中心\n企业内部只发生成本费用而不产生收入的组织单位,用于成本核算和控制。\n利润中心\n企业内部既发生成本费用又产生收入的组织单位,用于考核盈利能力。\n\n# 问答线索补充\n\n以下内容由入库阶段根据章节标题、条款、列表、键值对与相邻正文提炼,供问答检索时优先命中更短、更直接的制度依据。\n\n- 正文:以权利和责任的发生来决定收入和费用归属期的会计基础\n- 正文:即凡是当期已经实现的收入和已经发生或应当负担的费用,不论款项是否收付,都应当作为当期的收入和费用\n- 正文:以现金收到或付出为标准来记录收入的实现和费用的发生\n- 正文:即凡是当期收到和支付的现金,都作为当期的收入和费用\n- 正文:企业购进货物、接受应税劳务或应税服务支付的增值税额,可以从销项税额中抵扣\n- 正文:企业销售货物、提供应税劳务或应税服务收取的增值税额\n- 正文:毛利润占营业收入的百分比,反映企业产品或服务的初始盈利能力\n- 正文:计算公式:毛利率 = (营业收入 - 营业成本)/ 营业收入 × 100%\n- 正文:净利润占股东权益的百分比,反映股东投入资金的获利能力\n- 正文:计算公式:ROE = 净利润 / 股东权益 × 100%", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx", + "create_time": 1779441764, + "update_time": 1779441764, + "_id": "23f56f159a3e4bc3b2338056544120dd" + }, + "09fbcae74d3b41e498a47e05b45262cb": { + "content": "远光软件股份有限公司高新技术企业税收优惠政策汇总\n\n 远光软件股份有限公司\n\n 2024年度\n\n一、企业所得税优惠\n\n1. 高新技术企业减按15%税率征收企业所得税\n\n- 条件:取得高新技术企业证书且在有效期内\n\n- 申请:向主管税务机关备案\n\n2. 技术转让所得优惠\n\n- 符合条件的技术转让所得500万元以下免征企业所得税\n\n- 超过500万元的部分减半征收\n\n二、增值税优惠\n\n1. 软件产品增值税即征即退\n\n- 销售自行开发生产的软件产品,按13%征收后\n\n- 实际税负超过3%的部分即征即退\n\n2. 技术服务免征增值税\n\n- 符合条件的技术转让、技术开发和相关的技术咨询、技术服务免征增值税\n\n三、研发费用加计扣除\n\n1. 一般企业研发费用按100%加计扣除\n\n- 形成无形资产的按200%摊销\n\n2. 适用范围:\n\n- 人员人工费用\n\n- 直接投入费用\n\n- 折旧费用\n\n- 无形资产摊销\n\n- 新产品设计费等\n\n 远光软件股份有限公司 - 第 >/2 页\n- 其他相关费用\n\n四、其他优惠政策\n\n1. 固定资产加速折旧:允许缩短折旧年限或加速折旧\n\n2. 软件企业职工培训费用:全额在企业所得税前扣除\n\n3. 创业投资抵扣:投资未上市中小高新技术企业的按投资额70%抵扣应纳税所得额\n\n 远光软件股份有限公司 - 第 >/2 页\n\n# 章节导航\n\n以下内容由入库阶段从制度原文中提取,供检索时优先理解制度层级、条目和标准所在章节。\n\n- 一、企业所得税优惠\n- 二、增值税优惠\n- 三、研发费用加计扣除\n- 四、其他优惠政策\n\n# 重点章节摘录\n\n## 一、企业所得税优惠\n\n1. 高新技术企业减按15%税率征收企业所得税;- 条件:取得高新技术企业证书且在有效期内;- 申请:向主管税务机关备案\n\n## 二、增值税优惠\n\n1. 软件产品增值税即征即退;- 销售自行开发生产的软件产品,按13%征收后;- 实际税负超过3%的部分即征即退\n\n## 三、研发费用加计扣除\n\n1. 一般企业研发费用按100%加计扣除;- 形成无形资产的按200%摊销;2. 适用范围:\n\n## 四、其他优惠政策\n\n1. 固定资产加速折旧:允许缩短折旧年限或加速折旧;2. 软件企业职工培训费用:全额在企业所得税前扣除;3. 创业投资抵扣:投资未上市中小高新技术企业的按投资额70%抵扣应纳税所得额\n\n# 问答线索补充\n\n以下内容由入库阶段根据章节标题、条款、列表、键值对与相邻正文提炼,供问答检索时优先命中更短、更直接的制度依据。\n\n- 一、企业所得税优惠:1. 高新技术企业减按15%税率征收企业所得税\n- 一、企业所得税优惠:条件:取得高新技术企业证书且在有效期内\n- 一、企业所得税优惠:申请:向主管税务机关备案\n- 一、企业所得税优惠:2. 技术转让所得优惠\n- 二、增值税优惠:1. 软件产品增值税即征即退\n- 二、增值税优惠:销售自行开发生产的软件产品,按13%征收后\n- 二、增值税优惠:实际税负超过3%的部分即征即退\n- 二、增值税优惠:2. 技术服务免征增值税\n- 三、研发费用加计扣除:1. 一般企业研发费用按100%加计扣除\n- 三、研发费用加计扣除:形成无形资产的按200%摊销\n- 三、研发费用加计扣除:2. 适用范围:\n- 三、研发费用加计扣除:人员人工费用\n- 四、其他优惠政策:1. 固定资产加速折旧:允许缩短折旧年限或加速折旧\n- 四、其他优惠政策:2. 软件企业职工培训费用:全额在企业所得税前扣除\n- 四、其他优惠政策:3. 创业投资抵扣:投资未上市中小高新技术企业的按投资额70%抵扣应纳税所得额", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf", + "create_time": 1779441797, + "update_time": 1779441797, + "_id": "09fbcae74d3b41e498a47e05b45262cb" } } \ No newline at end of file diff --git a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_entities.json b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_entities.json index 22d4d57..13f607e 100644 --- a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_entities.json +++ b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_entities.json @@ -383,5 +383,153 @@ "create_time": 1779379018, "update_time": 1779379018, "_id": "a8f8465df08e455ebe133351721d49f8" + }, + "c7601043d9944ef2bcf4d3f67ed253f7": { + "entity_names": [ + "损益类", + "6603财务费用", + "固定资产", + "银行存款", + "6601管理费用", + "资产类", + "1122应收账款", + "会计科目说明", + "使用说明", + "科目名称", + "财务费用", + "累计折旧", + "库存现金", + "6602销售费用", + "远光软件会计科目使用说明.xlsx", + "主营业务成本", + "1001库存现金", + "应付账款", + "1221其他应收款", + "6001主营业务收入", + "6801所得税费用", + "备注", + "科目类别", + "所得税费用", + "Excel工作簿:远光软件会计科目使用说明.xlsx", + "负债类", + "2221应交税费", + "6401主营业务成本", + "应收账款", + "科目编码", + "应交税费", + "其他应收款", + "主营业务收入", + "原材料", + "管理费用", + "销售费用", + "应付职工薪酬", + "2211应付职工薪酬", + "1002银行存款", + "远光软件股份有限公司" + ], + "count": 40, + "create_time": 1779441745, + "update_time": 1779441745, + "_id": "c7601043d9944ef2bcf4d3f67ed253f7" + }, + "b0277cd76034437997fbf5219662725a": { + "entity_names": [ + "固定资产", + "财务报表解读", + "银行存款", + "收入", + "负债", + "现金流量表", + "企业所得税", + "三大财务报表", + "会计恒等式", + "库存现金", + "所有者权益", + "费用", + "财务基础知识手册", + "应付账款", + "利润表", + "会计基础知识", + "应收账款", + "应交税费", + "主营业务收入", + "资产", + "管理费用", + "税务基础知识", + "应付职工薪酬", + "销售费用", + "印花税", + "资产负债表", + "个人所得税", + "会计要素", + "远光软件股份有限公司", + "利润", + "增值税" + ], + "count": 31, + "create_time": 1779441772, + "update_time": 1779441772, + "_id": "b0277cd76034437997fbf5219662725a" + }, + "23f56f159a3e4bc3b2338056544120dd": { + "entity_names": [ + "净利润", + "财务术语解释手册", + "年限平均法", + "毛利润", + "预算", + "权责发生制", + "成本中心", + "摊销", + "营业收入", + "增值税进项税额", + "收付实现制", + "营业成本", + "增值税销项税额", + "净资产收益率", + "利润中心", + "固定资产折旧", + "股东权益", + "现金流", + "毛利率", + "远光软件股份有限公司" + ], + "count": 20, + "create_time": 1779441791, + "update_time": 1779441791, + "_id": "23f56f159a3e4bc3b2338056544120dd" + }, + "09fbcae74d3b41e498a47e05b45262cb": { + "entity_names": [ + "Other Preferential Policies", + "3%实际税负", + "Preferential Tax Policies", + "研发费用加计扣除", + "Corporate Income Tax", + "中小高新技术企业", + "企业所得税", + "Taxable Income", + "主管税务机关", + "技术转让所得优惠", + "固定资产加速折旧", + "高新技术企业减按15%税率征收企业所得税", + "技术服务免征增值税", + "软件产品增值税即征即退", + "13%税率", + "软件企业职工培训费用", + "创业投资抵扣", + "Small And Medium High-Tech Enterprises", + "Training Expenses", + "高新技术企业证书", + "Venture Capital Deduction", + "70% Deduction Rate", + "Investment Amount", + "远光软件股份有限公司", + "增值税" + ], + "count": 25, + "create_time": 1779441858, + "update_time": 1779441858, + "_id": "09fbcae74d3b41e498a47e05b45262cb" } } \ No newline at end of file diff --git a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_relations.json b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_relations.json index 03abac9..d7ef52c 100644 --- a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_relations.json +++ b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_full_relations.json @@ -274,5 +274,205 @@ "create_time": 1779379018, "update_time": 1779379018, "_id": "a8f8465df08e455ebe133351721d49f8" + }, + "c7601043d9944ef2bcf4d3f67ed253f7": { + "relation_pairs": [ + [ + "2221应交税费", + "远光软件股份有限公司" + ], + [ + "会计科目说明", + "备注" + ], + [ + "会计科目说明", + "科目类别" + ], + [ + "会计科目说明", + "科目名称" + ], + [ + "Excel工作簿:远光软件会计科目使用说明.xlsx", + "会计科目说明" + ], + [ + "库存现金", + "资产类" + ], + [ + "6001主营业务收入", + "远光软件股份有限公司" + ], + [ + "1002银行存款", + "远光软件股份有限公司" + ], + [ + "1221其他应收款", + "远光软件股份有限公司" + ], + [ + "资产类", + "银行存款" + ], + [ + "6401主营业务成本", + "远光软件股份有限公司" + ], + [ + "Excel工作簿:远光软件会计科目使用说明.xlsx", + "远光软件股份有限公司" + ], + [ + "会计科目说明", + "科目编码" + ], + [ + "1001库存现金", + "远光软件股份有限公司" + ], + [ + "会计科目说明", + "使用说明" + ], + [ + "远光软件会计科目使用说明.xlsx", + "远光软件股份有限公司" + ], + [ + "2211应付职工薪酬", + "远光软件股份有限公司" + ], + [ + "1122应收账款", + "远光软件股份有限公司" + ] + ], + "count": 18, + "create_time": 1779441745, + "update_time": 1779441745, + "_id": "c7601043d9944ef2bcf4d3f67ed253f7" + }, + "b0277cd76034437997fbf5219662725a": { + "relation_pairs": [ + [ + "会计要素", + "资产" + ], + [ + "财务基础知识手册", + "远光软件股份有限公司" + ], + [ + "财务基础知识手册", + "财务报表解读" + ], + [ + "税务基础知识", + "财务基础知识手册" + ], + [ + "会计基础知识", + "财务基础知识手册" + ] + ], + "count": 5, + "create_time": 1779441772, + "update_time": 1779441772, + "_id": "b0277cd76034437997fbf5219662725a" + }, + "23f56f159a3e4bc3b2338056544120dd": { + "relation_pairs": [ + [ + "毛利率", + "营业成本" + ], + [ + "摊销", + "财务术语解释手册" + ], + [ + "年限平均法", + "远光软件股份有限公司" + ], + [ + "增值税进项税额", + "财务术语解释手册" + ], + [ + "固定资产折旧", + "财务术语解释手册" + ], + [ + "净利润", + "净资产收益率" + ], + [ + "收付实现制", + "财务术语解释手册" + ], + [ + "权责发生制", + "财务术语解释手册" + ], + [ + "毛利润", + "毛利率" + ], + [ + "毛利率", + "营业收入" + ], + [ + "财务术语解释手册", + "远光软件股份有限公司" + ], + [ + "净资产收益率", + "股东权益" + ], + [ + "固定资产折旧", + "年限平均法" + ] + ], + "count": 13, + "create_time": 1779441791, + "update_time": 1779441791, + "_id": "23f56f159a3e4bc3b2338056544120dd" + }, + "09fbcae74d3b41e498a47e05b45262cb": { + "relation_pairs": [ + [ + "Corporate Income Tax", + "Training Expenses" + ], + [ + "Small And Medium High-Tech Enterprises", + "Venture Capital Deduction" + ], + [ + "Taxable Income", + "Venture Capital Deduction" + ], + [ + "Preferential Tax Policies", + "Venture Capital Deduction" + ], + [ + "70% Deduction Rate", + "Venture Capital Deduction" + ], + [ + "3%实际税负", + "软件产品增值税即征即退" + ] + ], + "count": 6, + "create_time": 1779441858, + "update_time": 1779441858, + "_id": "09fbcae74d3b41e498a47e05b45262cb" } } \ No newline at end of file diff --git a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_relation_chunks.json b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_relation_chunks.json index 1781878..0c907b9 100644 --- a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_relation_chunks.json +++ b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_relation_chunks.json @@ -583,5 +583,383 @@ "create_time": 1779379017, "update_time": 1779379017, "_id": "Receipt-Free ReimbursementSubmit Reimbursement" + }, + "Excel工作簿:远光软件会计科目使用说明.xlsx远光软件股份有限公司": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "Excel工作簿:远光软件会计科目使用说明.xlsx远光软件股份有限公司" + }, + "会计科目说明科目编码": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "会计科目说明科目编码" + }, + "库存现金资产类": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "库存现金资产类" + }, + "会计科目说明科目名称": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "会计科目说明科目名称" + }, + "资产类银行存款": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "资产类银行存款" + }, + "远光软件会计科目使用说明.xlsx远光软件股份有限公司": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441742, + "update_time": 1779441742, + "_id": "远光软件会计科目使用说明.xlsx远光软件股份有限公司" + }, + "2221应交税费远光软件股份有限公司": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441743, + "update_time": 1779441743, + "_id": "2221应交税费远光软件股份有限公司" + }, + "会计科目说明科目类别": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441743, + "update_time": 1779441743, + "_id": "会计科目说明科目类别" + }, + "会计科目说明使用说明": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441743, + "update_time": 1779441743, + "_id": "会计科目说明使用说明" + }, + "2211应付职工薪酬远光软件股份有限公司": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441743, + "update_time": 1779441743, + "_id": "2211应付职工薪酬远光软件股份有限公司" + }, + "会计科目说明备注": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441743, + "update_time": 1779441743, + "_id": "会计科目说明备注" + }, + "6001主营业务收入远光软件股份有限公司": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441743, + "update_time": 1779441743, + "_id": "6001主营业务收入远光软件股份有限公司" + }, + "Excel工作簿:远光软件会计科目使用说明.xlsx会计科目说明": { + "chunk_ids": [ + "chunk-31ff57cf79d009c378478f065eda9d4d" + ], + "count": 1, + "create_time": 1779441744, + "update_time": 1779441744, + "_id": "Excel工作簿:远光软件会计科目使用说明.xlsx会计科目说明" + }, + "6401主营业务成本远光软件股份有限公司": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441744, + "update_time": 1779441744, + "_id": "6401主营业务成本远光软件股份有限公司" + }, + "1001库存现金远光软件股份有限公司": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441744, + "update_time": 1779441744, + "_id": "1001库存现金远光软件股份有限公司" + }, + "1002银行存款远光软件股份有限公司": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441744, + "update_time": 1779441744, + "_id": "1002银行存款远光软件股份有限公司" + }, + "1122应收账款远光软件股份有限公司": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441745, + "update_time": 1779441745, + "_id": "1122应收账款远光软件股份有限公司" + }, + "1221其他应收款远光软件股份有限公司": { + "chunk_ids": [ + "chunk-e726f44fb0287c5192cf61b350f18abb" + ], + "count": 1, + "create_time": 1779441745, + "update_time": 1779441745, + "_id": "1221其他应收款远光软件股份有限公司" + }, + "财务基础知识手册远光软件股份有限公司": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441770, + "update_time": 1779441770, + "_id": "财务基础知识手册远光软件股份有限公司" + }, + "会计要素资产": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441770, + "update_time": 1779441770, + "_id": "会计要素资产" + }, + "会计基础知识财务基础知识手册": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441771, + "update_time": 1779441771, + "_id": "会计基础知识财务基础知识手册" + }, + "税务基础知识财务基础知识手册": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441771, + "update_time": 1779441771, + "_id": "税务基础知识财务基础知识手册" + }, + "财务基础知识手册财务报表解读": { + "chunk_ids": [ + "chunk-78edb0c8ccc8238159196ecaeeb08d43" + ], + "count": 1, + "create_time": 1779441771, + "update_time": 1779441771, + "_id": "财务基础知识手册财务报表解读" + }, + "财务术语解释手册远光软件股份有限公司": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441775, + "update_time": 1779441775, + "_id": "财务术语解释手册远光软件股份有限公司" + }, + "年限平均法远光软件股份有限公司": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441776, + "update_time": 1779441776, + "_id": "年限平均法远光软件股份有限公司" + }, + "权责发生制财务术语解释手册": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441776, + "update_time": 1779441776, + "_id": "权责发生制财务术语解释手册" + }, + "毛利润毛利率": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441776, + "update_time": 1779441776, + "_id": "毛利润毛利率" + }, + "收付实现制财务术语解释手册": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441776, + "update_time": 1779441776, + "_id": "收付实现制财务术语解释手册" + }, + "毛利率营业收入": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441776, + "update_time": 1779441776, + "_id": "毛利率营业收入" + }, + "净利润净资产收益率": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441776, + "update_time": 1779441776, + "_id": "净利润净资产收益率" + }, + "固定资产折旧财务术语解释手册": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441776, + "update_time": 1779441776, + "_id": "固定资产折旧财务术语解释手册" + }, + "毛利率营业成本": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441776, + "update_time": 1779441776, + "_id": "毛利率营业成本" + }, + "净资产收益率股东权益": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441776, + "update_time": 1779441776, + "_id": "净资产收益率股东权益" + }, + "摊销财务术语解释手册": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441790, + "update_time": 1779441790, + "_id": "摊销财务术语解释手册" + }, + "固定资产折旧年限平均法": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441790, + "update_time": 1779441790, + "_id": "固定资产折旧年限平均法" + }, + "增值税进项税额财务术语解释手册": { + "chunk_ids": [ + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + ], + "count": 1, + "create_time": 1779441791, + "update_time": 1779441791, + "_id": "增值税进项税额财务术语解释手册" + }, + "Corporate Income TaxTraining Expenses": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441857, + "update_time": 1779441857, + "_id": "Corporate Income TaxTraining Expenses" + }, + "Taxable IncomeVenture Capital Deduction": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441857, + "update_time": 1779441857, + "_id": "Taxable IncomeVenture Capital Deduction" + }, + "3%实际税负软件产品增值税即征即退": { + "chunk_ids": [ + "chunk-2c8384b328272063de4dac306a52d21e" + ], + "count": 1, + "create_time": 1779441857, + "update_time": 1779441857, + "_id": "3%实际税负软件产品增值税即征即退" + }, + "Small And Medium High-Tech EnterprisesVenture Capital Deduction": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441858, + "update_time": 1779441858, + "_id": "Small And Medium High-Tech EnterprisesVenture Capital Deduction" + }, + "Preferential Tax PoliciesVenture Capital Deduction": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441858, + "update_time": 1779441858, + "_id": "Preferential Tax PoliciesVenture Capital Deduction" + }, + "70% Deduction RateVenture Capital Deduction": { + "chunk_ids": [ + "chunk-93d2389cdb74257e90201dccbc3f6539" + ], + "count": 1, + "create_time": 1779441858, + "update_time": 1779441858, + "_id": "70% Deduction RateVenture Capital Deduction" } } \ No newline at end of file diff --git a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_text_chunks.json b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_text_chunks.json index dbcba52..9246111 100644 --- a/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_text_chunks.json +++ b/server/storage/knowledge/.lightrag/x_financial_knowledge/kv_store_text_chunks.json @@ -174,5 +174,71 @@ "create_time": 1779378923, "update_time": 1779378923, "_id": "chunk-2224d777c0b72d0b2dab622c79096c2c" + }, + "chunk-31ff57cf79d009c378478f065eda9d4d": { + "tokens": 1200, + "content": "# Excel 工作簿:远光软件会计科目使用说明.xlsx\n\n## 工作表 1:会计科目说明\n\n| 远光软件股份有限公司常用会计科目使用说明 | 列2 | 列3 | 列4 | 列5 |\n| --- | --- | --- | --- | --- |\n| 科目编码 | 科目名称 | 科目类别 | 使用说明 | 备注 |\n| 1001 | 库存现金 | 资产类 | 核算公司库存现金 | 日清月结 |\n| 1002 | 银行存款 | 资产类 | 核算存入银行的各项存款 | 按开户行明细 |\n| 1122 | 应收账款 | 资产类 | 核算因销售商品/提供劳务应收款项 | 按客户明细 |\n| 1221 | 其他应收款 | 资产类 | 核算应收暂付款项 | 含备用金、押金 |\n| 1403 | 原材料 | 资产类 | 核算库存的各种材料 | |\n| 1601 | 固定资产 | 资产类 | 核算固定资产原值 | 按类别明细 |\n| 1602 | 累计折旧 | 资产类 | 核算固定资产累计折旧 | 贷方余额 |\n| 2202 | 应付账款 | 负债类 | 核算因购买商品/接受劳务应付款项 | 按供应商明细 |\n| 2221 | 应交税费 | 负债类 | 核算应缴纳的各种税费 | 按税种明细 |\n| 2211 | 应付职工薪酬 | 负债类 | 核算应付给职工的薪酬 | 含社保公积金 |\n| 6001 | 主营业务收入 | 损益类 | 核算主要经营业务产生的收入 | 按业务类型明细 |\n| 6401 | 主营业务成本 | 损益类 | 核算主要经营业务发生的成本 | |\n| 6601 | 管理费用 | 损益类 | 核算为管理生产经营发生的费用 | 按费用类型明细 |\n| 6602 | 销售费用 | 损益类 | 核算为销售产品发生的费用 | 按费用类型明细 |\n| 6603 | 财务费用 | 损益类 | 核算筹资等财务活动费用 | 含利息、手续费 |\n| 6801 | 所得税费用 | 损益类 | 核算企业所得税费用 | 含递延所得税 |\n\n### 行级检索线索\n\n- 会计科目说明 第 2 行:远光软件股份有限公司常用会计科目使用说明=科目编码;列2=科目名称;列3=科目类别;列4=使用说明;列5=备注\n\n- 会计科目说明 第 3 行:远光软件股份有限公司常用会计科目使用说明=1001;列2=库存现金;列3=资产类;列4=核算公司库存现金;列5=日清月结\n\n- 会计科目说明 第 4 行:远光软件股份有限公司常用会计科目使用说明=1002;列2=银行存款;列3=资产类;列4=核算存入银行的各项存款;列5=按开户行明细\n\n- 会计科目说明 第 5 行:远光软件股份有限公司常用会计科目使用说明=1122;列2=应收账款;列3=资产类;列4=核算因销售商品/提供劳务应收款项;列5=按客户明细\n\n- 会计科目说明 第 6 行:远光软件股份有限公司常用会计科目使用说明=1221;列2=其他应收款;列3=资产类;列4=核算应收暂付款项;列5=含备用金、押金\n\n- 会计科目说明 第 7 行:远光软件股份有限公司常用会计科目使用说明=1403;列2=原材料;列3=资产类;列4=核算库存的各种材料\n\n- 会计科目说明 第 8 行:远光软件股份有限公司常用会计科目使用说明=1601;列2=固定资产;列3=资产类;列4=核算固定资产原值;列5=按类别明细\n\n- 会计科目说明 第 9 行:远光软件股份有限公司常用会计科目使用说明=1602;列2=累计折旧;列3=资产类;列4=核算固定资产累计折旧;列5=贷方余额\n\n- 会计科目说明 第 10 行:远光软件股份有限公司常用会计科目使用说明=2202;列2=应付账款;列3=负债类;列4=核算因购买商品/接受劳务应付款项;列5=按供应商明细\n\n- 会计科目说明 第 11 行:远光软件股份有限公司常用会计科目使用说明=2221;列2=应交税费;列3=负债类;列4=核算应缴纳的各种税费;列5=按税种明细", + "chunk_order_index": 0, + "full_doc_id": "c7601043d9944ef2bcf4d3f67ed253f7", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx", + "llm_cache_list": [], + "create_time": 1779441661, + "update_time": 1779441661, + "_id": "chunk-31ff57cf79d009c378478f065eda9d4d" + }, + "chunk-e726f44fb0287c5192cf61b350f18abb": { + "tokens": 952, + "content": "付账款;列3=负债类;列4=核算因购买商品/接受劳务应付款项;列5=按供应商明细\n\n- 会计科目说明 第 11 行:远光软件股份有限公司常用会计科目使用说明=2221;列2=应交税费;列3=负债类;列4=核算应缴纳的各种税费;列5=按税种明细\n\n- 会计科目说明 第 12 行:远光软件股份有限公司常用会计科目使用说明=2211;列2=应付职工薪酬;列3=负债类;列4=核算应付给职工的薪酬;列5=含社保公积金\n\n- 会计科目说明 第 13 行:远光软件股份有限公司常用会计科目使用说明=6001;列2=主营业务收入;列3=损益类;列4=核算主要经营业务产生的收入;列5=按业务类型明细\n\n- 会计科目说明 第 14 行:远光软件股份有限公司常用会计科目使用说明=6401;列2=主营业务成本;列3=损益类;列4=核算主要经营业务发生的成本\n\n- 会计科目说明 第 15 行:远光软件股份有限公司常用会计科目使用说明=6601;列2=管理费用;列3=损益类;列4=核算为管理生产经营发生的费用;列5=按费用类型明细\n\n- 会计科目说明 第 16 行:远光软件股份有限公司常用会计科目使用说明=6602;列2=销售费用;列3=损益类;列4=核算为销售产品发生的费用;列5=按费用类型明细\n\n- 会计科目说明 第 17 行:远光软件股份有限公司常用会计科目使用说明=6603;列2=财务费用;列3=损益类;列4=核算筹资等财务活动费用;列5=含利息、手续费\n\n- 会计科目说明 第 18 行:远光软件股份有限公司常用会计科目使用说明=6801;列2=所得税费用;列3=损益类;列4=核算企业所得税费用;列5=含递延所得税\n\n# 问答线索补充\n\n以下内容由入库阶段根据章节标题、条款、列表、键值对与相邻正文提炼,供问答检索时优先命中更短、更直接的制度依据。\n\n- 正文:# Excel 工作簿:远光软件会计科目使用说明.xlsx\n- 正文:会计科目说明 第 2 行:远光软件股份有限公司常用会计科目使用说明=科目编码\n- 正文:列2=科目名称\n- 正文:列3=科目类别\n- 正文:列4=使用说明\n- 正文:列5=备注\n- 正文:会计科目说明 第 3 行:远光软件股份有限公司常用会计科目使用说明=1001\n- 正文:列2=库存现金\n- 正文:列3=资产类\n- 正文:列4=核算公司库存现金\n- 正文:列5=日清月结\n- 正文:会计科目说明 第 4 行:远光软件股份有限公司常用会计科目使用说明=1002\n- 正文:列2=银行存款\n- 正文:列4=核算存入银行的各项存款\n- 正文:列5=按开户行明细\n- 正文:会计科目说明 第 5 行:远光软件股份有限公司常用会计科目使用说明=1122\n- 正文:列2=应收账款\n- 正文:列4=核算因销售商品/提供劳务应收款项\n- 正文:列5=按客户明细\n- 正文:会计科目说明 第 6 行:远光软件股份有限公司常用会计科目使用说明=1221\n- 正文:列2=其他应收款", + "chunk_order_index": 1, + "full_doc_id": "c7601043d9944ef2bcf4d3f67ed253f7", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件会计科目使用说明.xlsx", + "llm_cache_list": [], + "create_time": 1779441661, + "update_time": 1779441661, + "_id": "chunk-e726f44fb0287c5192cf61b350f18abb" + }, + "chunk-78edb0c8ccc8238159196ecaeeb08d43": { + "tokens": 839, + "content": "远光软件股份有限公司\n财务基础知识手册\n第一部分 会计基础知识\n一、会计要素\n会计要素包括:资产、负债、所有者权益、收入、费用和利润。\n会计恒等式:资产 = 负债 + 所有者权益\n二、常用会计科目\n科目类别\n科目名称\n说明\n资产类\n库存现金\n公司持有的现金\n资产类\n银行存款\n存放在银行的资金\n资产类\n应收账款\n因销售商品或提供劳务应收的款项\n资产类\n固定资产\n使用年限超过一年的有形资产\n负债类\n应付账款\n因购买商品或接受劳务应付的款项\n负债类\n应交税费\n应缴纳的各种税费\n负债类\n应付职工薪酬\n应付给职工的工资、福利等\n损益类\n主营业务收入\n主要经营业务产生的收入\n损益类\n管理费用\n为管理生产经营发生的费用\n损益类\n销售费用\n为销售产品发生的费用\n第二部分 税务基础知识\n三、主要税种介绍\n(一)增值税:公司为一般纳税人,软件服务适用6%税率,软件产品销售适用13%税率。\n(二)企业所得税:税率为25%,高新技术企业享受15%优惠税率。\n(三)个人所得税:按累进税率3%-45%,由公司代扣代缴。\n(四)印花税:对经济活动中的应税凭证征收。\n第三部分 财务报表解读\n四、三大财务报表\n(一)资产负债表:反映企业在某一特定日期的财务状况。\n(二)利润表:反映企业在一定期间的经营成果。\n(三)现金流量表:反映企业在一定期间现金和现金等价物的流入和流出。\n\n# 章节导航\n\n以下内容由入库阶段从制度原文中提取,供检索时优先理解制度层级、条目和标准所在章节。\n\n- 一、会计要素\n- 二、常用会计科目\n- (四)印花税:对经济活动中的应税凭证征收。\n\n# 重点章节摘录\n\n## 一、会计要素\n\n会计要素包括:资产、负债、所有者权益、收入、费用和利润。;会计恒等式:资产 = 负债 + 所有者权益\n\n## 二、常用会计科目\n\n科目类别;科目名称;说明\n\n## (四)印花税:对经济活动中的应税凭证征收。\n\n第三部分 财务报表解读\n\n# 问答线索补充\n\n以下内容由入库阶段根据章节标题、条款、列表、键值对与相邻正文提炼,供问答检索时优先命中更短、更直接的制度依据。\n\n- 一、会计要素:会计要素包括:资产、负债、所有者权益、收入、费用和利润\n- 一、会计要素:会计恒等式:资产 = 负债 + 所有者权益\n- 二、常用会计科目:因销售商品或提供劳务应收的款项\n- 二、常用会计科目:因购买商品或接受劳务应付的款项\n- 二、常用会计科目:应缴纳的各种税费\n- 二、常用会计科目:应付职工薪酬\n- (四)印花税:对经济活动中的应税凭证征收。:第三部分 财务报表解读", + "chunk_order_index": 0, + "full_doc_id": "b0277cd76034437997fbf5219662725a", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件财务基础知识手册.docx", + "llm_cache_list": [], + "create_time": 1779441751, + "update_time": 1779441751, + "_id": "chunk-78edb0c8ccc8238159196ecaeeb08d43" + }, + "chunk-2ee7e2a66cb544bdfe1b09e133863ad1": { + "tokens": 760, + "content": "远光软件股份有限公司\n财务术语解释手册\n权责发生制\n以权利和责任的发生来决定收入和费用归属期的会计基础。即凡是当期已经实现的收入和已经发生或应当负担的费用,不论款项是否收付,都应当作为当期的收入和费用。\n收付实现制\n以现金收到或付出为标准来记录收入的实现和费用的发生。即凡是当期收到和支付的现金,都作为当期的收入和费用。\n固定资产折旧\n固定资产在使用过程中因磨损而逐渐转移的价值。公司采用年限平均法计提折旧。\n摊销\n将无形资产或长期待摊费用按照规定期限分期计入当期损益的过程。\n增值税进项税额\n企业购进货物、接受应税劳务或应税服务支付的增值税额,可以从销项税额中抵扣。\n增值税销项税额\n企业销售货物、提供应税劳务或应税服务收取的增值税额。\n预算\n企业对未来一定时期内经营活动的数量化计划,包括收入预算、支出预算、资本预算等。\n现金流\n企业在一定期间内现金和现金等价物流入和流出的数量。\n毛利率\n毛利润占营业收入的百分比,反映企业产品或服务的初始盈利能力。计算公式:毛利率 = (营业收入 - 营业成本)/ 营业收入 × 100%\n净资产收益率(ROE)\n净利润占股东权益的百分比,反映股东投入资金的获利能力。计算公式:ROE = 净利润 / 股东权益 × 100%\n成本中心\n企业内部只发生成本费用而不产生收入的组织单位,用于成本核算和控制。\n利润中心\n企业内部既发生成本费用又产生收入的组织单位,用于考核盈利能力。\n\n# 问答线索补充\n\n以下内容由入库阶段根据章节标题、条款、列表、键值对与相邻正文提炼,供问答检索时优先命中更短、更直接的制度依据。\n\n- 正文:以权利和责任的发生来决定收入和费用归属期的会计基础\n- 正文:即凡是当期已经实现的收入和已经发生或应当负担的费用,不论款项是否收付,都应当作为当期的收入和费用\n- 正文:以现金收到或付出为标准来记录收入的实现和费用的发生\n- 正文:即凡是当期收到和支付的现金,都作为当期的收入和费用\n- 正文:企业购进货物、接受应税劳务或应税服务支付的增值税额,可以从销项税额中抵扣\n- 正文:企业销售货物、提供应税劳务或应税服务收取的增值税额\n- 正文:毛利润占营业收入的百分比,反映企业产品或服务的初始盈利能力\n- 正文:计算公式:毛利率 = (营业收入 - 营业成本)/ 营业收入 × 100%\n- 正文:净利润占股东权益的百分比,反映股东投入资金的获利能力\n- 正文:计算公式:ROE = 净利润 / 股东权益 × 100%", + "chunk_order_index": 0, + "full_doc_id": "23f56f159a3e4bc3b2338056544120dd", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件财务术语解释手册.docx", + "llm_cache_list": [], + "create_time": 1779441764, + "update_time": 1779441764, + "_id": "chunk-2ee7e2a66cb544bdfe1b09e133863ad1" + }, + "chunk-2c8384b328272063de4dac306a52d21e": { + "tokens": 1150, + "content": "远光软件股份有限公司高新技术企业税收优惠政策汇总\n\n 远光软件股份有限公司\n\n 2024年度\n\n一、企业所得税优惠\n\n1. 高新技术企业减按15%税率征收企业所得税\n\n- 条件:取得高新技术企业证书且在有效期内\n\n- 申请:向主管税务机关备案\n\n2. 技术转让所得优惠\n\n- 符合条件的技术转让所得500万元以下免征企业所得税\n\n- 超过500万元的部分减半征收\n\n二、增值税优惠\n\n1. 软件产品增值税即征即退\n\n- 销售自行开发生产的软件产品,按13%征收后\n\n- 实际税负超过3%的部分即征即退\n\n2. 技术服务免征增值税\n\n- 符合条件的技术转让、技术开发和相关的技术咨询、技术服务免征增值税\n\n三、研发费用加计扣除\n\n1. 一般企业研发费用按100%加计扣除\n\n- 形成无形资产的按200%摊销\n\n2. 适用范围:\n\n- 人员人工费用\n\n- 直接投入费用\n\n- 折旧费用\n\n- 无形资产摊销\n\n- 新产品设计费等\n\n 远光软件股份有限公司 - 第 >/2 页\n- 其他相关费用\n\n四、其他优惠政策\n\n1. 固定资产加速折旧:允许缩短折旧年限或加速折旧\n\n2. 软件企业职工培训费用:全额在企业所得税前扣除\n\n3. 创业投资抵扣:投资未上市中小高新技术企业的按投资额70%抵扣应纳税所得额\n\n 远光软件股份有限公司 - 第 >/2 页\n\n# 章节导航\n\n以下内容由入库阶段从制度原文中提取,供检索时优先理解制度层级、条目和标准所在章节。\n\n- 一、企业所得税优惠\n- 二、增值税优惠\n- 三、研发费用加计扣除\n- 四、其他优惠政策\n\n# 重点章节摘录\n\n## 一、企业所得税优惠\n\n1. 高新技术企业减按15%税率征收企业所得税;- 条件:取得高新技术企业证书且在有效期内;- 申请:向主管税务机关备案\n\n## 二、增值税优惠\n\n1. 软件产品增值税即征即退;- 销售自行开发生产的软件产品,按13%征收后;- 实际税负超过3%的部分即征即退\n\n## 三、研发费用加计扣除\n\n1. 一般企业研发费用按100%加计扣除;- 形成无形资产的按200%摊销;2. 适用范围:\n\n## 四、其他优惠政策\n\n1. 固定资产加速折旧:允许缩短折旧年限或加速折旧;2. 软件企业职工培训费用:全额在企业所得税前扣除;3. 创业投资抵扣:投资未上市中小高新技术企业的按投资额70%抵扣应纳税所得额\n\n# 问答线索补充\n\n以下内容由入库阶段根据章节标题、条款、列表、键值对与相邻正文提炼,供问答检索时优先命中更短、更直接的制度依据。\n\n- 一、企业所得税优惠:1. 高新技术企业减按15%税率征收企业所得税\n- 一、企业所得税优惠:条件:取得高新技术企业证书且在有效期内\n- 一、企业所得税优惠:申请:向主管税务机关备案\n- 一、企业所得税优惠:2. 技术转让所得优惠\n- 二、增值税优惠:1. 软件产品增值税即征即退\n- 二、增值税优惠:销售自行开发生产的软件产品,按13%征收后\n- 二、增值税优惠:实际税负超过3%的部分即征即退\n- 二、增值税优惠:2. 技术服务免征增值税\n- 三、研发费用加计扣除:1. 一般企业研发费用按100%加计扣除\n- 三、研发费用加计扣除:形成无形资产的按200%摊销\n- 三、研发费用加计扣除:2. 适用范围:\n- 三、研发费用加计扣除:人员人工费用\n- 四、其他优惠政策:1. 固定资产加速折旧:允许缩短折旧年限或加速折旧\n- 四、其他优惠政策:2. 软件企业职工培训费用:全额在企业所得税前扣除\n- 四、其他优惠政策:3. 创业投资抵扣:投资未上市中小高新技术企业的按投资额70%抵扣应纳税所得额", + "chunk_order_index": 0, + "full_doc_id": "09fbcae74d3b41e498a47e05b45262cb", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf", + "llm_cache_list": [], + "create_time": 1779441797, + "update_time": 1779441797, + "_id": "chunk-2c8384b328272063de4dac306a52d21e" + }, + "chunk-93d2389cdb74257e90201dccbc3f6539": { + "tokens": 50, + "content": "培训费用:全额在企业所得税前扣除\n- 四、其他优惠政策:3. 创业投资抵扣:投资未上市中小高新技术企业的按投资额70%抵扣应纳税所得额", + "chunk_order_index": 1, + "full_doc_id": "09fbcae74d3b41e498a47e05b45262cb", + "file_path": "/app/server/storage/knowledge/财务知识库/远光软件高新技术企业税收优惠政策汇总.pdf", + "llm_cache_list": [], + "create_time": 1779441797, + "update_time": 1779441797, + "_id": "chunk-93d2389cdb74257e90201dccbc3f6539" } } \ No newline at end of file diff --git a/server/tests/test_expense_claim_service.py b/server/tests/test_expense_claim_service.py index d705825..8da0b30 100644 --- a/server/tests/test_expense_claim_service.py +++ b/server/tests/test_expense_claim_service.py @@ -181,6 +181,49 @@ def test_save_or_submit_persists_claim_only_after_save_draft_action() -> None: assert _count_claims(db) == before_count + 1 +def test_save_draft_persists_user_changed_expense_category() -> None: + user_id = "save-draft-category@example.com" + message = "业务发生时间:2026-03-04,打车去客户现场,交通费32元,请帮我看看怎么报" + + with build_session() as db: + employee = Employee( + employee_no="E5102", + name="分类员工", + email=user_id, + ) + db.add(employee) + db.commit() + ontology = SemanticOntologyService(db).parse( + OntologyParseRequest( + query=message, + user_id=user_id, + ) + ) + + result = ExpenseClaimService(db).save_or_submit_from_ontology( + run_id=ontology.run_id, + user_id=user_id, + message=message, + ontology=ontology, + context_json={ + "name": "分类员工", + "user_input_text": message, + "review_action": "save_draft", + "review_form_values": { + "expense_type": "办公用品费", + "amount": "32元", + "occurred_date": "2026-03-04", + "reason": "右侧核对后改为办公用品费", + }, + }, + ) + + claim = db.get(ExpenseClaim, result["claim_id"]) + assert claim is not None + assert claim.expense_type == "office" + assert claim.items[0].item_type == "office" + + def test_unsaved_conversation_expires_after_retention_but_saved_conversation_stays() -> None: with build_session() as db: service = AgentConversationService(db) diff --git a/server/tests/test_knowledge_rag_service.py b/server/tests/test_knowledge_rag_service.py index 1eb37d5..4184967 100644 --- a/server/tests/test_knowledge_rag_service.py +++ b/server/tests/test_knowledge_rag_service.py @@ -1,6 +1,14 @@ from __future__ import annotations +import json +import threading + from app.services import knowledge_rag as knowledge_rag_module +from app.services.knowledge_ingest_log import ( + build_document_graph_summary, + build_ingest_document_summary, + build_ingest_status_summary, +) from app.services.knowledge_rag import KnowledgeRagService @@ -86,7 +94,10 @@ def test_build_hits_demotes_chapter_navigation_for_specific_rule_queries() -> No { "chunk_id": "body-1", "file_path": "/tmp/doc-1__费用制度.md", - "content": "附表3:支出归口管理部门与归口业务范围\n组织人事部:探亲差旅、条件艰苦及安全风险较高区域补助等支出。", + "content": ( + "附表3:支出归口管理部门与归口业务范围\n" + "组织人事部:探亲差旅、条件艰苦及安全风险较高区域补助等支出。" + ), }, ], entities=[], @@ -100,9 +111,11 @@ def test_resolve_default_qdrant_url_prefers_container_host(monkeypatch) -> None: monkeypatch.setattr( knowledge_rag_module.socket, "getaddrinfo", - lambda hostname, port: [("family", "type", "proto", "canonname", ("172.21.0.2", 0))] - if hostname == "qdrant" - else [], + lambda hostname, port: ( + [("family", "type", "proto", "canonname", ("172.21.0.2", 0))] + if hostname == "qdrant" + else [] + ), ) assert knowledge_rag_module._resolve_default_qdrant_url() == "http://qdrant:6333" @@ -117,6 +130,45 @@ def test_resolve_default_qdrant_url_falls_back_to_loopback(monkeypatch) -> None: assert knowledge_rag_module._resolve_default_qdrant_url() == "http://127.0.0.1:6333" +def test_runtime_cache_is_isolated_by_thread(monkeypatch) -> None: + knowledge_rag_module.shutdown_knowledge_rag_runtime() + created_runtimes = [] + + class FakeRuntime: + def __init__(self, **_kwargs): + self.finalized = False + created_runtimes.append(self) + + def finalize(self): + self.finalized = True + + monkeypatch.setattr(knowledge_rag_module, "_LightRagRuntime", FakeRuntime) + monkeypatch.setattr( + KnowledgeRagService, + "_build_runtime_signature", + lambda self: (("same-config",), {}), + ) + + service = KnowledgeRagService() + main_runtime = service._get_runtime() + assert service._get_runtime() is main_runtime + + worker_runtimes = [] + + def load_worker_runtime() -> None: + worker_runtimes.append(KnowledgeRagService()._get_runtime()) + + thread = threading.Thread(target=load_worker_runtime) + thread.start() + thread.join(timeout=5) + + assert len(created_runtimes) == 2 + assert worker_runtimes[0] is not main_runtime + + knowledge_rag_module.shutdown_knowledge_rag_runtime() + assert all(runtime.finalized for runtime in created_runtimes) + + def test_is_query_ready_status_rejects_failed_status_even_with_chunks() -> None: assert ( KnowledgeRagService.is_query_ready_status( @@ -141,3 +193,89 @@ def test_is_query_ready_status_rejects_processing_status_even_with_chunks() -> N ) is False ) + + +def test_build_document_graph_summary_reads_lightrag_storage(tmp_path) -> None: + workspace = tmp_path / "knowledge" / ".lightrag" / "test_workspace" + workspace.mkdir(parents=True) + (workspace / "kv_store_full_entities.json").write_text( + json.dumps({"doc-1": {"entity_names": ["远光软件", "支出管理", "远光软件"]}}), + encoding="utf-8", + ) + (workspace / "kv_store_full_relations.json").write_text( + json.dumps({"doc-1": {"relation_pairs": [["远光软件", "支出管理"]]}}), + encoding="utf-8", + ) + (workspace / "kv_store_text_chunks.json").write_text( + json.dumps( + { + "chunk-2": { + "_id": "chunk-2", + "full_doc_id": "doc-1", + "chunk_order_index": 1, + "tokens": 45, + "content": "第二条 支出审批需要结合预算、归口部门和授权标准执行。", + }, + "chunk-1": { + "_id": "chunk-1", + "full_doc_id": "doc-1", + "chunk_order_index": 0, + "tokens": 31, + "content": "第一条 本办法适用于公司支出管理。", + }, + } + ), + encoding="utf-8", + ) + + summary = build_document_graph_summary( + tmp_path, + workspace="test_workspace", + document_id="doc-1", + ) + + assert summary["entity_count"] == 2 + assert summary["entities"] == ["远光软件", "支出管理"] + assert summary["relation_count"] == 1 + assert summary["relations"] == [{"source": "远光软件", "target": "支出管理", "type": "关联"}] + assert [item["id"] for item in summary["chunks"]] == ["chunk-1", "chunk-2"] + + +def test_build_ingest_document_summary_extracts_sections() -> None: + summary = build_ingest_document_summary( + document_id="doc-1", + entry={ + "original_name": "公司支出管理办法.pdf", + "folder": "制度文件", + "extension": "pdf", + "mime_type": "application/pdf", + }, + raw_text="第一章 总则\n本办法用于规范公司支出。", + indexed_text="# 第一章 总则\n本办法用于规范公司支出。\n第二条 审批\n审批需按授权执行。", + ) + + assert summary["name"] == "公司支出管理办法.pdf" + assert summary["section_count"] == 2 + assert summary["sections"][0]["title"] == "第一章 总则" + + +def test_build_ingest_status_summary_keeps_chunk_status() -> None: + summary = build_ingest_status_summary( + status_payload={ + "status": "processed", + "query_ready": True, + "chunks_count": 2, + "chunks_list": ["chunk-1", "chunk-2"], + }, + graph_summary={ + "entity_count": 1, + "relation_count": 0, + "entities": ["预算"], + "relations": [], + }, + ) + + assert summary["lightrag_status"] == "processed" + assert summary["query_ready"] is True + assert summary["chunk_count"] == 2 + assert summary["chunk_ids"] == ["chunk-1", "chunk-2"] diff --git a/server/tests/test_ontology_service.py b/server/tests/test_ontology_service.py index 854231a..fbb96f1 100644 --- a/server/tests/test_ontology_service.py +++ b/server/tests/test_ontology_service.py @@ -389,10 +389,10 @@ def test_semantic_ontology_service_prefers_expense_for_customer_entertainment_na assert result.clarification_required is True assert "customer_name" in result.missing_slots assert "participants" in result.missing_slots - assert any( - item.type == "expense_type" and item.normalized_value == "entertainment" - for item in result.entities - ) + assert any( + item.type == "expense_type" and item.normalized_value == "meal" + for item in result.entities + ) def test_semantic_ontology_service_uses_client_local_date_for_relative_time() -> None: @@ -556,6 +556,39 @@ def test_semantic_ontology_service_maps_taxi_ticket_reimbursement_to_transport_d ) +@pytest.mark.parametrize( + "query,expected_type", + [ + ("报销飞机票和行程单", "travel"), + ("报销酒店发票和房费", "hotel"), + ("报销滴滴打车票", "transport"), + ("报销工作餐餐费", "meal"), + ("报销会议场地费", "meeting"), + ("报销客户接待餐", "meal"), + ("报销打印纸和硒鼓", "office"), + ("报销培训课程费", "training"), + ("报销手机话费和流量费", "communication"), + ("报销员工体检费", "welfare"), + ], +) +def test_semantic_ontology_service_covers_common_expense_scene_keywords( + query: str, + expected_type: str, +) -> None: + session_factory = build_session_factory() + with session_factory() as db: + result = SemanticOntologyService(db).parse( + OntologyParseRequest(query=query, user_id="pytest") + ) + + assert result.scenario == "expense" + assert result.intent == "draft" + assert any( + item.type == "expense_type" and item.normalized_value == expected_type + for item in result.entities + ) + + def test_semantic_ontology_service_uses_model_parse_when_available(monkeypatch) -> None: session_factory = build_session_factory() with session_factory() as db: diff --git a/server/tests/test_user_agent_service.py b/server/tests/test_user_agent_service.py index a6f4b08..946cea2 100644 --- a/server/tests/test_user_agent_service.py +++ b/server/tests/test_user_agent_service.py @@ -540,7 +540,11 @@ def test_user_agent_asks_for_type_when_trip_context_is_ambiguous() -> None: "交通费", "住宿费", "业务招待费", - "办公费", + "会务费", + "办公用品费", + "培训费", + "通讯费", + "福利费", "其他费用", ] assert response.suggested_actions[0].payload["original_message"] == message @@ -729,6 +733,9 @@ def test_user_agent_keeps_taxi_ticket_for_customer_dropoff_as_transport_expense( assert "业务招待费" not in response.review_payload.intent_summary assert "客户名称" not in response.review_payload.missing_slots assert "参与人员" not in response.review_payload.missing_slots + edit_field_keys = {item.key for item in response.review_payload.edit_fields} + assert "merchant_name" not in edit_field_keys + assert "participants" not in edit_field_keys def test_user_agent_keeps_travel_range_when_user_adds_receipts_after_text_context() -> None: @@ -1000,6 +1007,9 @@ def test_user_agent_transport_flow_infers_reason_and_does_not_require_location_o assert response.review_payload is not None slot_map = {item.key: item for item in response.review_payload.slot_cards} + document_card = response.review_payload.document_cards[0] + assert document_card.scene_label == "出租车/网约车票据" + assert document_card.suggested_expense_type == "transport" assert slot_map["reason"].value == "交通出行" assert slot_map["reason"].status == "inferred" assert "酒店/商户" not in response.review_payload.missing_slots @@ -1189,8 +1199,15 @@ def test_user_agent_document_service_normalizes_ocr_fields_and_scene() -> None: assert fields["列车出发时间"] == "2026-03-04" assert "商户/酒店" not in fields assert document_service.extract_amount_text_from_value("滴滴出行 支付金额 1 元,实付 13.4 元,订单号 12345678") == "13.40元" + taxi_classified = document_service.classify_document({"filename": "行程单_的士票.jpg", "summary": "的士 车费 48 元"}) + assert taxi_classified["document_type"] == "taxi_receipt" + assert taxi_classified["expense_type"] == "transport" + assert taxi_classified["scene_label"] == "出租车/网约车票据" + ship_classified = document_service.classify_document({"filename": "轮船票.jpg", "summary": "轮船 船票 金额 180 元"}) + assert ship_classified["document_type"] == "ship_ticket" + assert ship_classified["scene_label"] == "轮船票" assert classified["document_type"] == "meal_receipt" - assert classified["expense_type"] == "entertainment" + assert classified["expense_type"] == "meal" assert document_service.infer_expense_type_from_documents( [{"filename": "客户餐饮发票.jpg", "summary": "餐饮发票 客户招待 金额 320 元"}], expense_type_code="entertainment", @@ -1262,11 +1279,13 @@ def test_user_agent_builds_review_payload_for_multi_document_expense_flow() -> N assert response.review_payload is not None assert len(response.review_payload.document_cards) == 2 assert len(response.review_payload.claim_groups) == 2 - assert response.review_payload.missing_slots == ["参与人员"] + assert response.review_payload.missing_slots == ["参与人员", "酒店的报销票据待上传(必须)"] assert [item.action_type for item in response.review_payload.confirmation_actions] == [ "save_draft", ] - assert any(item.scene_label == "业务招待费" for item in response.review_payload.document_cards) + assert any(item.scene_label == "餐饮发票" for item in response.review_payload.document_cards) + assert all(item.scene_label != "业务招待费" for item in response.review_payload.document_cards) + assert any(item.scene_label == "业务招待费" for item in response.review_payload.claim_groups) assert f"时间:{yesterday}" in response.review_payload.intent_summary slot_map = {item.key: item for item in response.review_payload.slot_cards} assert slot_map["time_range"].value == yesterday @@ -1899,7 +1918,58 @@ def test_user_agent_review_payload_prechecks_taxi_amount_against_rule_standard() assert "单笔交通金额" in combined assert "报销场景提交与附件标准" in combined assert amount_brief.level == "high" - assert any(item.title == "附件金额测算结果" for item in response.review_payload.risk_briefs) + measurement = next(item for item in response.review_payload.risk_briefs if item.title == "附件金额测算异常") + assert measurement.level == "warning" + assert "超出标准" in measurement.detail + + +def test_user_agent_review_payload_does_not_mark_compliant_taxi_amount_as_low_risk() -> None: + session_factory = build_session_factory() + with session_factory() as db: + query = "我上传一张的士票59.10元,帮我生成交通费报销草稿" + context = { + "name": "张三", + "attachment_names": ["的士1.jpg"], + "attachment_count": 1, + "ocr_documents": [ + { + "filename": "的士1.jpg", + "document_type": "taxi_receipt", + "summary": "出租车/网约车票据 支付金额 59.10 元", + "text": "的士 车费 59.10 元", + "avg_score": 0.95, + "document_fields": [ + {"key": "amount", "label": "支付金额", "value": "59.10"}, + ], + "warnings": [], + } + ], + } + ontology = SemanticOntologyService(db).parse( + OntologyParseRequest( + query=query, + user_id="pytest-taxi-pass@example.com", + context_json=context, + ) + ) + + response = UserAgentService(db).respond( + UserAgentRequest( + run_id=ontology.run_id, + user_id="pytest-taxi-pass@example.com", + message=query, + ontology=ontology, + context_json=context, + tool_payload={"draft_only": True}, + ) + ) + + assert response.review_payload is not None + risk_titles = [item.title for item in response.review_payload.risk_briefs] + risk_details = "\n".join(item.detail for item in response.review_payload.risk_briefs) + assert "附件金额测算结果" not in risk_titles + assert "附件金额测算异常" not in risk_titles + assert "测算通过" not in risk_details def test_user_agent_review_payload_uses_finance_spreadsheet_hotel_amount_standard() -> None: @@ -2067,8 +2137,9 @@ def test_user_agent_review_payload_uses_finance_spreadsheet_meal_allowance_stand assert "直辖市/特区" in combined assert "公司差旅费报销规则" in combined assert meal_brief.level == "high" - measurement = next(item for item in response.review_payload.risk_briefs if item.title == "附件金额测算结果") + measurement = next(item for item in response.review_payload.risk_briefs if item.title == "附件金额测算异常") assert "伙食补助标准 65.00" in measurement.detail + assert "超出标准" in measurement.detail def test_user_agent_filters_deprecated_review_risk_briefs() -> None: diff --git a/web/src/assets/styles/views/settings-view-form.css b/web/src/assets/styles/views/settings-view-form.css new file mode 100644 index 0000000..7b25c26 --- /dev/null +++ b/web/src/assets/styles/views/settings-view-form.css @@ -0,0 +1,227 @@ +/* 设置页表单/卡片/开关 — 供 SettingsView 与子面板各自 scoped 引入 */ + +.settings-card { + padding: 0; + border: 1px solid #e2e8f0; + border-radius: 12px; + background: #ffffff; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02), 0 4px 16px rgba(0, 0, 0, 0.03); + overflow: hidden; + display: flex; + flex-direction: column; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.settings-card:hover { + border-color: #cbd5e1; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.01), 0 10px 24px rgba(0, 0, 0, 0.04); +} + +.card-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 14px 24px; + background: #f8fafc; + border-bottom: 1px solid #e2e8f0; + margin: 0; +} + +.settings-card > *:not(.card-head) { + padding-left: 24px; + padding-right: 24px; + margin-top: 24px; +} + +.settings-card > *:not(.card-head):last-child { + margin-bottom: 24px; +} + +.card-head-actions { + display: flex; + align-items: center; + gap: 10px; + flex: 0 0 auto; +} + +.card-head h4 { + margin: 0; + padding: 0; + color: #0f172a; + font-size: 15px; + font-weight: 700; + line-height: 1.2; +} + +.card-head p { + margin: 4px 0 0 0; + padding: 0; + color: #64748b; + font-size: 13px; + line-height: 1.4; +} + +.form-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 20px; + align-items: start; +} + +.compact-grid { + margin-bottom: 20px; +} + +.field { + display: grid; + gap: 8px; +} + +.field-wide { + grid-column: span 2; +} + +.field-full { + grid-column: 1 / -1; +} + +.field span { + color: #475569; + font-size: 12.5px; + font-weight: 700; + line-height: 1.2; +} + +.field em { + margin-right: 4px; + color: #ef4444; + font-style: normal; +} + +.field input, +.field select { + width: 100%; + min-height: 44px; + padding: 0 14px; + border: 1px solid #cbd5e1; + border-radius: 12px; + background: #ffffff; + color: #0f172a; + font-size: 13px; + line-height: 1.45; + transition: all 0.2s ease; +} + +.field input::placeholder { + color: #94a3b8; +} + +.field input:focus, +.field select:focus { + outline: none; + border-color: #10b981; + box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.08); + background: #ffffff; +} + +.switch-group { + display: grid; + gap: 12px; +} + +.switch-row { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 16px 20px; + border: 1px solid #e2e8f0; + border-radius: 16px; + background: #ffffff; + text-align: left; + transition: all 0.25s ease; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.01); + cursor: pointer; +} + +.switch-row:hover { + border-color: rgba(16, 185, 129, 0.25); + background: linear-gradient(180deg, #ffffff 0%, rgba(16, 185, 129, 0.01) 100%); + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.03); + transform: translateY(-1px); +} + +.switch-copy { + min-width: 0; + display: grid; + gap: 4px; +} + +.switch-copy strong { + color: #0f172a; + font-size: 14px; + font-weight: 700; +} + +.switch-copy small { + color: #64748b; + font-size: 12px; + line-height: 1.5; +} + +/* Switch Toggle Buttons */ +.switch-btn { + position: relative; + display: inline-flex; + align-items: center; + flex: 0 0 auto; + width: 48px; + height: 26px; + padding: 3px; + border: none; + border-radius: 99px; + background: #cbd5e1; + cursor: pointer; + outline: none; + transition: background-color 0.25s ease; +} + +.switch-btn i { + width: 20px; + height: 20px; + border-radius: 50%; + background: #ffffff; + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.15); + transition: transform 0.25s cubic-bezier(0.25, 1, 0.5, 1); +} + +.switch-btn.active { + background-color: #10b981; +} + +.switch-btn.active i { + transform: translateX(22px); +} + +.switch-btn:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +/* Mini Switch */ +.switch-btn.mini { + width: 38px; + height: 20px; + padding: 2px; +} + +.switch-btn.mini i { + width: 16px; + height: 16px; +} + +.switch-btn.mini.active i { + transform: translateX(18px); +} diff --git a/web/src/assets/styles/views/settings-view-hermes.css b/web/src/assets/styles/views/settings-view-hermes.css new file mode 100644 index 0000000..adafa55 --- /dev/null +++ b/web/src/assets/styles/views/settings-view-hermes.css @@ -0,0 +1,228 @@ +/* Container */ +.hermes-settings-container { + display: flex; + flex-direction: column; + gap: 20px; + width: 100%; +} + +/* Master Control Hero Card Active Hover & Color */ +.hermes-hero-card.active { + border-color: rgba(16, 185, 129, 0.25); + background: linear-gradient(180deg, #ffffff 0%, rgba(16, 185, 129, 0.02) 100%); + box-shadow: 0 1px 3px rgba(16, 185, 129, 0.01), 0 8px 24px rgba(16, 185, 129, 0.05); +} + +.hermes-hero-card .model-icon-box { + position: relative; +} + +.hermes-hero-card .model-icon-box.active { + background: rgba(16, 185, 129, 0.1); + color: #10b981; + border-color: rgba(16, 185, 129, 0.15); +} + +/* Pulse Dot */ +.status-pulse-dot { + position: absolute; + top: -2px; + right: -2px; + width: 10px; + height: 10px; + background-color: #cbd5e1; + border: 2px solid #ffffff; + border-radius: 50%; + transition: background-color 0.3s ease; +} + +.status-pulse-dot.active { + background-color: #10b981; + animation: pulse-ring 1.8s cubic-bezier(0.455, 0.03, 0.515, 0.955) infinite; +} + +@keyframes pulse-ring { + 0% { + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); + } + 70% { + box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); + } +} + +.status-badge { + padding: 6px 12px; + border-radius: 99px; + background: #f1f5f9; + color: #64748b; + font-size: 12px; + font-weight: 600; + letter-spacing: 0.02em; + transition: all 0.3s ease; +} + +.status-badge.active { + background: rgba(16, 185, 129, 0.1); + color: #10b981; +} + +/* Task Section */ +.hermes-tasks-section.disabled { + opacity: 0.6; + pointer-events: none; +} + +/* Tasks Grid */ +.hermes-task-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + gap: 16px; + margin: 0; + padding: 0; + list-style: none; +} + +/* Task Card */ +.hermes-task-card { + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 20px; + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 16px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.01), 0 2px 8px rgba(0, 0, 0, 0.02); + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); +} + +.hermes-task-card:hover { + transform: translateY(-2px); + border-color: #cbd5e1; + box-shadow: 0 4px 6px rgba(15, 23, 42, 0.01), 0 10px 20px rgba(15, 23, 42, 0.04); +} + +.task-card-header { + display: flex; + align-items: flex-start; + gap: 14px; + margin-bottom: 20px; +} + +.task-icon-box { + display: flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + border-radius: 12px; + font-size: 20px; + transition: all 0.3s ease; +} + +.task-meta-info { + flex: 1; + min-width: 0; +} + +.task-meta-info strong { + display: block; + color: #0f172a; + font-size: 14px; + font-weight: 700; + line-height: 1.4; +} + +.task-meta-info small { + display: block; + margin-top: 4px; + color: #64748b; + font-size: 12px; + line-height: 1.45; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Task card colors (SaaS Gray) */ +.task-icon-box.indigo, +.task-icon-box.warning, +.task-icon-box.danger, +.task-icon-box.info, +.task-icon-box.success, +.task-icon-box.primary, +.task-icon-box.secondary, +.task-icon-box.default { + background: #f8fafc; + color: #475569; + border: 1px solid #e2e8f0; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02); +} + +/* Card Footer & Time picker */ +.task-card-footer { + display: flex; + align-items: center; + justify-content: space-between; + padding-top: 14px; + border-top: 1px dashed #f1f5f9; +} + +.frequency-badge { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + border-radius: 99px; + background: #f8fafc; + color: #94a3b8; + font-size: 11px; + font-weight: 600; + transition: all 0.3s ease; +} + +.frequency-badge.active { + background: #f0f9ff; + color: #0284c7; +} + +.time-picker-wrapper input[type="time"] { + padding: 5px 8px; + border: 1px solid #e2e8f0; + border-radius: 8px; + background: #f8fafc; + color: #334155; + font-size: 12px; + font-weight: 600; + outline: none; + cursor: pointer; + transition: all 0.2s ease; +} + +.time-picker-wrapper input[type="time"]:hover { + border-color: #cbd5e1; + background: #f1f5f9; +} + +.time-picker-wrapper input[type="time"]:focus { + border-color: #10b981; + background: #ffffff; + box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.08); +} + +.time-picker-placeholder { + color: #cbd5e1; + font-size: 11px; + font-weight: 600; +} + +@media (max-width: 768px) { + .hermes-hero-card.active { + background: #ffffff; + } + .hermes-task-grid { + grid-template-columns: 1fr; + } +} diff --git a/web/src/assets/styles/views/settings-view.css b/web/src/assets/styles/views/settings-view.css index 56c2469..7194041 100644 --- a/web/src/assets/styles/views/settings-view.css +++ b/web/src/assets/styles/views/settings-view.css @@ -1,367 +1,249 @@ -.settings-page { - height: 100%; - min-height: 0; - animation: fadeUp 220ms var(--ease) both; -} - -.settings-shell { - height: 100%; - min-height: 0; - display: grid; - grid-template-columns: 248px minmax(0, 1fr); - overflow: hidden; - border-radius: 24px; - background: linear-gradient(180deg, #ffffff 0%, #fbfefd 100%); -} - -.settings-nav { - min-width: 0; - min-height: 0; - display: grid; - grid-template-rows: auto minmax(0, 1fr); - gap: 10px; - padding: 22px 16px 18px; - border-right: 1px solid #e7edf3; - background: linear-gradient(180deg, #fcfffd 0%, #f5fbf8 58%, #ffffff 100%); -} - -.settings-nav-head { - display: grid; - gap: 8px; - padding: 4px 10px 18px; - border-bottom: 1px solid #eef3f7; -} - -.nav-kicker { - color: #10b981; - font-size: 11px; - font-weight: 800; - letter-spacing: 0.08em; - text-transform: uppercase; -} - -.settings-nav-head h2 { - color: #0f172a; - font-size: 24px; - font-weight: 860; - line-height: 1.1; -} - -.settings-nav-head p { - color: #64748b; - font-size: 12px; - line-height: 1.6; -} - -.settings-nav-list { - min-height: 0; - display: grid; - align-content: start; - gap: 8px; - overflow: auto; - padding-right: 4px; -} - -.settings-nav-item { - width: 100%; - min-height: 74px; - display: block; - padding: 14px 14px 14px 16px; - border: 1px solid transparent; - border-radius: 18px; - background: transparent; - color: #334155; - text-align: left; - transition: - background 180ms var(--ease), - border-color 180ms var(--ease), - box-shadow 180ms var(--ease), - color 180ms var(--ease), - transform 180ms var(--ease); -} - -.settings-nav-item:hover { - transform: translateY(-1px); - border-color: rgba(16, 185, 129, 0.14); - background: rgba(255, 255, 255, 0.9); -} - -.settings-nav-item.active { - border-color: rgba(16, 185, 129, 0.16); - background: linear-gradient(135deg, rgba(16, 185, 129, 0.14), rgba(16, 185, 129, 0.04)); - box-shadow: inset 3px 0 0 #10b981; - color: #047857; -} - -.nav-item-copy { - min-width: 0; - display: grid; - gap: 4px; -} - -.nav-item-copy strong { - color: inherit; - font-size: 14px; - font-weight: 820; - line-height: 1.25; -} - -.nav-item-copy small { - color: #64748b; - font-size: 12px; - line-height: 1.45; -} - -.settings-body { - min-width: 0; - min-height: 0; - display: grid; - grid-template-rows: auto minmax(0, 1fr); - background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, rgba(248, 250, 252, 0.96) 100%); -} - -.settings-toolbar { - position: sticky; - top: 0; - z-index: 2; - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 18px; - padding: 24px 28px 20px; - border-bottom: 1px solid #eef2f7; - background: rgba(255, 255, 255, 0.92); - backdrop-filter: blur(14px); -} - -.settings-toolbar-copy { - min-width: 0; -} - -.settings-breadcrumb { - display: inline-flex; - align-items: center; - min-height: 28px; - padding: 0 12px; - border-radius: 999px; - background: #eef8f2; - color: #047857; - font-size: 12px; - font-weight: 800; -} - -.settings-toolbar-copy h3 { - margin-top: 14px; - color: #0f172a; - font-size: 28px; - font-weight: 860; - line-height: 1.15; -} - -.settings-toolbar-copy p { - margin-top: 10px; - max-width: 760px; - color: #64748b; - font-size: 14px; - line-height: 1.7; -} - -.settings-toolbar-actions { - display: grid; - justify-items: end; - gap: 12px; -} - -.save-button { - min-height: 42px; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 8px; - padding: 0 18px; - border: 0; - border-radius: 14px; - background: linear-gradient(135deg, #13b87b, #0a9d68); - color: #fff; - font-size: 13px; - font-weight: 820; - box-shadow: 0 12px 26px rgba(5, 150, 105, 0.2); - transition: - transform 180ms var(--ease), - box-shadow 180ms var(--ease), - filter 180ms var(--ease); -} - -.save-button:hover { - transform: translateY(-1px); - box-shadow: 0 16px 30px rgba(5, 150, 105, 0.22); - filter: saturate(1.04); -} - -.settings-content { - min-height: 0; - overflow: auto; - display: grid; - align-content: start; - gap: 18px; - padding: 24px 28px 28px; -} - -.model-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 18px; -} - -.settings-card { - padding: 22px 22px 24px; - border: 1px solid #e8eef3; - border-radius: 22px; - background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(248, 251, 255, 0.94)); -} - -.card-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 14px; - margin-bottom: 18px; -} - -.card-head-actions { - display: flex; - align-items: center; - gap: 10px; - flex: 0 0 auto; -} - -.card-head h4 { - color: #0f172a; - font-size: 18px; - font-weight: 840; - line-height: 1.2; -} - -.card-head p { - margin-top: 6px; - color: #64748b; - font-size: 13px; - line-height: 1.65; -} - -.test-button { - min-height: 38px; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 8px; - padding: 0 14px; - border: 1px solid rgba(16, 185, 129, 0.18); - border-radius: 12px; - background: #f7fffb; - color: #047857; - font-size: 13px; - font-weight: 800; - transition: - border-color 180ms var(--ease), - background 180ms var(--ease), - color 180ms var(--ease), - transform 180ms var(--ease); -} - -.test-button:hover:not(:disabled) { - transform: translateY(-1px); - border-color: rgba(16, 185, 129, 0.34); - background: #ecfdf5; -} - -.test-button:disabled { - cursor: wait; - opacity: 0.78; -} - -.form-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 18px 20px; - align-items: start; -} - -.profile-grid { - grid-template-columns: 96px repeat(2, minmax(0, 1fr)); -} - -.compact-grid { - margin-bottom: 18px; -} - -.field { - display: grid; - gap: 8px; -} - -.field-wide { - grid-column: span 2; -} - -.field-full { - grid-column: 1 / -1; -} - -.field span { - color: #334155; - font-size: 12px; - font-weight: 800; - line-height: 1.2; -} - -.field em { - margin-right: 4px; - color: #ef4444; - font-style: normal; -} - -.field input { - width: 100%; - min-height: 44px; - padding: 0 14px; - border: 1px solid #d7e0ea; - border-radius: 16px; - background: #fff; - color: #0f172a; - font-size: 13px; - line-height: 1.45; - transition: - border-color 180ms var(--ease), - box-shadow 180ms var(--ease), - background 180ms var(--ease); +.settings-page { + height: 100%; + min-height: 0; + animation: fadeUp 220ms var(--ease) both; } -.field select { - width: 100%; - min-height: 44px; - padding: 0 14px; - border: 1px solid #d7e0ea; - border-radius: 16px; - background: #fff; +.settings-shell { + height: 100%; + min-height: 0; + display: grid; + grid-template-columns: 240px minmax(0, 1fr); + overflow: hidden; + border-radius: 24px; + background: #ffffff; + border: 1px solid #e2e8f0; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02), 0 4px 16px rgba(0, 0, 0, 0.03); +} + +.settings-nav { + min-width: 0; + min-height: 0; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + gap: 12px; + padding: 24px 16px 20px; + border-right: 1px solid #e2e8f0; + background: #f8fafc; +} + +.settings-nav-head { + display: grid; + gap: 6px; + padding: 0 8px 16px; + border-bottom: 1px solid #e2e8f0; +} + +.nav-kicker { + color: #10b981; + font-size: 10px; + font-weight: 800; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.settings-nav-head h2 { color: #0f172a; + font-size: 20px; + font-weight: 700; + line-height: 1.2; +} + +.settings-nav-head p { + color: #64748b; + font-size: 12px; + line-height: 1.5; +} + +.settings-nav-list { + min-height: 0; + display: grid; + align-content: start; + gap: 6px; + overflow: auto; + padding-right: 2px; +} + +.settings-nav-item { + width: 100%; + min-height: 60px; + display: block; + padding: 10px 14px; + border: 1px solid transparent; + border-radius: 12px; + background: transparent; + color: #334155; + text-align: left; + transition: all 0.2s ease; + cursor: pointer; +} + +.settings-nav-item:hover { + background: rgba(255, 255, 255, 0.8); + border-color: #cbd5e1; +} + +.settings-nav-item.active { + background: #ecfdf5; + color: #059669; + border-color: rgba(16, 185, 129, 0.12); +} + +.nav-item-copy { + min-width: 0; + display: grid; + gap: 2px; +} + +.nav-item-copy strong { + color: inherit; + font-size: 13.5px; + font-weight: 700; + line-height: 1.3; +} + +.nav-item-copy small { + color: #64748b; + font-size: 11.5px; + line-height: 1.4; +} + +.settings-nav-item.active .nav-item-copy small { + color: #059669; + opacity: 0.8; +} + +.settings-body { + min-width: 0; + min-height: 0; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + background: #f8fafc; +} + +.settings-toolbar { + position: sticky; + top: 0; + z-index: 2; + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 18px; + padding: 16px 24px; + border-bottom: 1px solid #e2e8f0; + background: rgba(255, 255, 255, 0.9); + backdrop-filter: blur(12px); +} + +.settings-toolbar-copy { + min-width: 0; +} + +.settings-breadcrumb { + display: inline-flex; + align-items: center; + min-height: 24px; + padding: 0 10px; + border-radius: 99px; + background: #ecfdf5; + color: #059669; + font-size: 12px; + font-weight: 700; +} + +.settings-toolbar-copy h3 { + margin-top: 4px; + color: #0f172a; + font-size: 22px; + font-weight: 700; + line-height: 1.2; +} + +.settings-toolbar-copy p { + margin-top: 3px; + max-width: 760px; + color: #64748b; + font-size: 12.5px; + line-height: 1.5; +} + +.settings-toolbar-actions { + display: grid; + justify-items: end; + gap: 12px; +} + +.save-button { + min-height: 42px; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 0 20px; + border: 0; + border-radius: 12px; + background: #10b981; + color: #ffffff; font-size: 13px; - line-height: 1.45; - transition: - border-color 180ms var(--ease), - box-shadow 180ms var(--ease), - background 180ms var(--ease); + font-weight: 700; + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.15); + transition: all 0.2s ease; + cursor: pointer; } -.field input::placeholder { - color: #94a3b8; +.save-button:hover { + transform: translateY(-1px); + box-shadow: 0 6px 20px rgba(16, 185, 129, 0.25); + background: #059669; } -.field input:focus, -.field select:focus { - outline: none; - border-color: rgba(16, 185, 129, 0.55); - box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.12); +.settings-content { + min-height: 0; + overflow: auto; + display: grid; + align-content: start; + gap: 20px; + padding: 20px 24px; +} + +.model-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 20px; +} + +.test-button { + min-height: 36px; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + padding: 0 12px; + border: 1px solid rgba(16, 185, 129, 0.2); + border-radius: 10px; + background: #ecfdf5; + color: #059669; + font-size: 12.5px; + font-weight: 700; + transition: all 0.2s ease; + cursor: pointer; +} + +.test-button:hover:not(:disabled) { + transform: translateY(-1px); + border-color: #10b981; + background: #10b981; + color: #ffffff; + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.15); +} + +.test-button:disabled { + cursor: wait; + opacity: 0.6; +} + +.profile-grid { + grid-template-columns: 96px repeat(2, minmax(0, 1fr)); } .session-picker-filter { @@ -376,18 +258,15 @@ justify-content: space-between; gap: 10px; padding: 0 40px 0 14px; - border: 1px solid #d7e0ea; - border-radius: 16px; - background: #fff; - color: #334155; + border: 1px solid #cbd5e1; + border-radius: 12px; + background: #ffffff; + color: #0f172a; font-size: 13px; - font-weight: 760; + font-weight: 600; text-align: left; - transition: - border-color 180ms var(--ease), - box-shadow 180ms var(--ease), - background 180ms var(--ease), - color 180ms var(--ease); + transition: all 0.2s ease; + cursor: pointer; } .session-picker-label { @@ -408,18 +287,18 @@ .session-picker-trigger:hover, .session-picker-filter.open .session-picker-trigger { - border-color: rgba(16, 185, 129, 0.34); - background: #f6fffb; - color: #0f9f78; + border-color: #10b981; + background: #ffffff; + color: #059669; } .session-picker-filter.open .session-picker-trigger { - box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.12); + box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.08); } .session-picker-filter.open .session-picker-trigger .mdi, .session-picker-trigger:hover .mdi { - color: #0f9f78; + color: #059669; } .session-picker-popover { @@ -429,12 +308,12 @@ width: 100%; z-index: 40; display: grid; - gap: 14px; + gap: 12px; padding: 16px; - border: 1px solid #d7e0ea; - border-radius: 16px; - background: #fff; - box-shadow: 0 18px 42px rgba(15, 23, 42, 0.16); + border: 1px solid #cbd5e1; + border-radius: 12px; + background: #ffffff; + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.05), 0 8px 10px -6px rgba(0, 0, 0, 0.05); } .session-picker-popover header { @@ -446,18 +325,21 @@ .session-picker-popover header strong { color: #0f172a; - font-size: 15px; + font-size: 14px; + font-weight: 700; } .session-picker-popover header button { - width: 30px; - height: 30px; + width: 28px; + height: 28px; display: grid; place-items: center; border: 0; - border-radius: 10px; + border-radius: 8px; background: transparent; color: #64748b; + cursor: pointer; + transition: all 0.2s ease; } .session-picker-popover header button:hover { @@ -467,383 +349,342 @@ .session-picker-option-list { display: grid; - gap: 8px; - max-height: 240px; + gap: 6px; + max-height: 200px; overflow-y: auto; } .session-picker-option { - min-height: 38px; + min-height: 36px; display: inline-flex; align-items: center; padding: 0 12px; - border: 1px solid #d7e0ea; - border-radius: 12px; - background: #fff; + border: 1px solid #cbd5e1; + border-radius: 8px; + background: #ffffff; color: #334155; font-size: 13px; - font-weight: 750; + font-weight: 600; text-align: left; - transition: - border-color 180ms var(--ease), - background 180ms var(--ease), - color 180ms var(--ease); + transition: all 0.2s ease; + cursor: pointer; } .session-picker-option:hover, .session-picker-option.active { - border-color: rgba(16, 185, 129, 0.32); - background: rgba(16, 185, 129, 0.08); + border-color: #10b981; + background: #ecfdf5; color: #059669; } - -.secret-bound-state { - min-height: 24px; - display: inline-flex; - align-items: center; - gap: 7px; - color: #047857; - font-size: 12px; - font-weight: 750; - line-height: 1.45; -} - -.secret-bound-state i { - font-size: 15px; -} - -.test-feedback { - display: flex; - align-items: flex-start; - gap: 8px; - margin-top: 16px; - padding: 12px 14px; - border-radius: 14px; - font-size: 12px; - font-weight: 700; - line-height: 1.6; -} - -.test-feedback i { - margin-top: 2px; - font-size: 15px; -} - -.test-feedback.is-success { - background: #ecfdf5; - color: #047857; -} - -.test-feedback.is-error { - background: #fef2f2; - color: #b91c1c; -} - -.test-feedback.is-testing { - background: #eff6ff; - color: #1d4ed8; -} - -.logo-field { - align-self: stretch; -} - -.logo-tile { - width: 96px; - height: 96px; - display: grid; - place-items: center; - border: 1px dashed #cbd5e1; - border-radius: 22px; - background: - linear-gradient(45deg, #f8fafc 25%, transparent 25%, transparent 75%, #f8fafc 75%, #f8fafc), - linear-gradient(45deg, #f8fafc 25%, transparent 25%, transparent 75%, #f8fafc 75%, #f8fafc); - background-position: 0 0, 9px 9px; - background-size: 18px 18px; - color: #10b981; - font-size: 36px; -} - -.preview-card { - display: grid; - grid-template-columns: 78px minmax(0, 1fr) auto; - align-items: center; - gap: 18px; - padding: 22px; - border: 1px solid rgba(16, 185, 129, 0.14); - border-radius: 24px; - background: linear-gradient(135deg, rgba(16, 185, 129, 0.08), rgba(59, 130, 246, 0.05)); -} - -.preview-icon { - width: 78px; - height: 78px; - display: grid; - place-items: center; - border-radius: 22px; - background: linear-gradient(135deg, #10b981, #0f766e); - color: #fff; - font-size: 34px; - box-shadow: 0 14px 28px rgba(16, 185, 129, 0.18); -} - -.preview-copy strong { - display: block; - color: #0f172a; - font-size: 18px; - font-weight: 840; -} - -.preview-copy p { - margin-top: 6px; - color: #334155; - font-size: 14px; - font-weight: 700; -} - -.preview-copy small { - display: block; - margin-top: 8px; - color: #64748b; - font-size: 12px; - line-height: 1.55; -} - -.preview-badge { - min-height: 30px; - display: inline-flex; - align-items: center; - justify-content: center; - padding: 0 12px; - border-radius: 999px; - background: #ecfdf5; - color: #059669; - font-size: 12px; - font-weight: 820; - white-space: nowrap; -} - -.chip-row { - display: flex; - gap: 10px; - flex-wrap: wrap; - margin-bottom: 18px; -} - -.level-chip { - min-width: 78px; - min-height: 36px; - padding: 0 14px; - border: 1px solid #d7e0ea; - border-radius: 999px; - background: #fff; - color: #475569; - font-size: 12px; - font-weight: 820; - transition: - border-color 160ms ease, - background 160ms ease, - box-shadow 160ms ease, - color 160ms ease; -} - -.level-chip.active { - border-color: #10b981; - background: #10b981; - color: #fff; - box-shadow: 0 8px 18px rgba(16, 185, 129, 0.18); -} - -.range-shell { - min-height: 44px; - display: flex; - align-items: center; - gap: 14px; - padding: 0 14px; - border: 1px solid #d7e0ea; - border-radius: 16px; - background: #fff; -} - -.range-shell input[type='range'] { - flex: 1 1 auto; - accent-color: #10b981; -} - -.range-shell strong { - min-width: 28px; - color: #0f172a; - font-size: 13px; - font-weight: 800; - text-align: right; -} - -.switch-group { + +.secret-bound-state { + min-height: 24px; + display: inline-flex; + align-items: center; + gap: 6px; + color: #059669; + font-size: 12px; + font-weight: 600; + line-height: 1.45; +} + +.secret-bound-state i { + font-size: 14px; +} + +.test-feedback { + display: flex; + align-items: flex-start; + gap: 8px; + margin-top: 16px; + padding: 12px 14px; + border-radius: 12px; + font-size: 12px; + font-weight: 600; + line-height: 1.6; +} + +.test-feedback i { + margin-top: 2px; + font-size: 14px; +} + +.test-feedback.is-success { + background: #ecfdf5; + color: #059669; +} + +.test-feedback.is-error { + background: #fef2f2; + color: #b91c1c; +} + +.test-feedback.is-testing { + background: #eff6ff; + color: #1d4ed8; +} + +.logo-field { + align-self: stretch; +} + +.logo-tile { + width: 96px; + height: 96px; display: grid; - gap: 12px; + place-items: center; + border: 1px dashed #cbd5e1; + border-radius: 20px; + background: + linear-gradient(45deg, #f8fafc 25%, transparent 25%, transparent 75%, #f8fafc 75%, #f8fafc), + linear-gradient(45deg, #f8fafc 25%, transparent 25%, transparent 75%, #f8fafc 75%, #f8fafc); + background-position: 0 0, 9px 9px; + background-size: 18px 18px; + color: #10b981; + font-size: 32px; +} + +.preview-card { + display: grid; + grid-template-columns: 64px minmax(0, 1fr) auto; + align-items: center; + gap: 18px; + padding: 24px; + border: 1px solid rgba(16, 185, 129, 0.15); + border-radius: 20px; + background: linear-gradient(135deg, rgba(16, 185, 129, 0.04) 0%, rgba(59, 130, 246, 0.02) 100%); +} + +.preview-icon { + width: 64px; + height: 64px; + display: grid; + place-items: center; + border-radius: 16px; + background: linear-gradient(135deg, #10b981 0%, #059669 100%); + color: #ffffff; + font-size: 28px; + box-shadow: 0 8px 20px rgba(16, 185, 129, 0.15); +} + +.preview-copy strong { + display: block; + color: #0f172a; + font-size: 16px; + font-weight: 700; +} + +.preview-copy p { + margin-top: 4px; + color: #334155; + font-size: 13.5px; + font-weight: 600; +} + +.preview-copy small { + display: block; + margin-top: 6px; + color: #64748b; + font-size: 12px; + line-height: 1.5; +} + +.preview-badge { + min-height: 26px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 10px; + border-radius: 999px; + background: #ecfdf5; + color: #059669; + font-size: 11px; + font-weight: 700; + white-space: nowrap; +} + +.chip-row { + display: flex; + gap: 8px; + flex-wrap: wrap; + margin-bottom: 20px; +} + +.level-chip { + min-width: 80px; + min-height: 34px; + padding: 0 16px; + border: 1px solid #cbd5e1; + border-radius: 999px; + background: #ffffff; + color: #64748b; + font-size: 12px; + font-weight: 700; + transition: all 0.2s ease; + cursor: pointer; +} + +.level-chip:hover { + border-color: #10b981; + color: #10b981; + background: #ecfdf5; +} + +.level-chip.active { + border-color: #10b981; + background: #10b981; + color: #ffffff; + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.2); +} + +.range-shell { + min-height: 44px; + display: flex; + align-items: center; + gap: 14px; + padding: 0 14px; + border: 1px solid #cbd5e1; + border-radius: 12px; + background: #ffffff; +} + +.range-shell input[type='range'] { + flex: 1 1 auto; + accent-color: #10b981; + cursor: pointer; +} + +.range-shell strong { + min-width: 28px; + color: #0f172a; + font-size: 13px; + font-weight: 700; + text-align: right; } .rendering-settings-card .switch-group { - margin-bottom: 24px; + margin-bottom: 20px; } -.switch-row { - width: 100%; +/* 大语言模型配置卡片特定图标与标题排版 */ +.card-title-with-icon { display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - padding: 15px 16px; - border: 1px solid #e5eaf0; - border-radius: 18px; - background: #fbfdff; - text-align: left; - transition: - border-color 180ms var(--ease), - background 180ms var(--ease), - transform 180ms var(--ease); -} - -.switch-row:hover { - transform: translateY(-1px); - border-color: rgba(16, 185, 129, 0.18); - background: #f7fffb; -} - -.switch-copy { - min-width: 0; - display: grid; - gap: 4px; -} - -.switch-copy strong { - color: #0f172a; - font-size: 14px; - font-weight: 800; -} - -.switch-copy small { - color: #64748b; - font-size: 12px; - line-height: 1.55; -} - -.switch { - position: relative; - flex: 0 0 auto; - width: 48px; - height: 28px; - display: inline-flex; - align-items: center; - padding: 3px; - border-radius: 999px; - background: #dbe4ee; - transition: background 180ms var(--ease); -} - -.switch i { - width: 22px; - height: 22px; - border-radius: 999px; - background: #fff; - box-shadow: 0 2px 6px rgba(15, 23, 42, 0.14); - transition: transform 180ms var(--ease); -} - -.switch.active { - background: #10b981; -} - -.switch.active i { - transform: translateX(20px); -} - -@media (max-width: 1260px) { - .settings-shell { - grid-template-columns: 226px minmax(0, 1fr); - } - - .settings-toolbar { - flex-direction: column; - align-items: stretch; - } - - .settings-toolbar-actions { - justify-items: stretch; - } - - .save-button { - justify-content: center; - } -} - -@media (max-width: 960px) { - .settings-shell { - grid-template-columns: 1fr; - } - - .settings-nav { - grid-template-rows: auto auto auto; - border-right: 0; - border-bottom: 1px solid #e7edf3; - } - - .settings-nav-list { - display: flex; - gap: 8px; - overflow-x: auto; - padding-right: 0; - } - - .settings-nav-item { - min-width: 208px; - } - - .settings-toolbar, - .settings-content { - padding-inline: 20px; - } - - .model-grid, - .form-grid, - .profile-grid { - grid-template-columns: 1fr; - } - - .field-wide, - .field-full { - grid-column: span 1; - } - - .logo-field { - width: fit-content; - } - - .preview-card { - grid-template-columns: 1fr; - justify-items: start; - } -} - -@media (max-width: 640px) { - .settings-toolbar { - padding: 18px 16px; - } - - .settings-toolbar-copy h3 { - font-size: 24px; - } - - .settings-content { - padding: 16px; - } - - .settings-card { - padding: 18px 16px; - border-radius: 18px; - } - - .settings-nav { - padding: 18px 12px 14px; - } -} + align-items: center; + gap: 12px; +} + +.model-icon-box { + width: 40px; + height: 40px; + display: grid; + place-items: center; + border-radius: 10px; + font-size: 18px; + flex: 0 0 auto; + background: #f8fafc; + color: #475569; + border: 1px solid #e2e8f0; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02); +} + +.model-icon-box.purple, +.model-icon-box.orange, +.model-icon-box.cyan, +.model-icon-box.teal { + background: #f8fafc; + color: #475569; + border: 1px solid #e2e8f0; +} + +@media (max-width: 1260px) { + .settings-shell { + grid-template-columns: 200px minmax(0, 1fr); + } + + .settings-toolbar { + flex-direction: column; + align-items: stretch; + gap: 12px; + } + + .settings-toolbar-actions { + justify-items: stretch; + } + + .save-button { + justify-content: center; + } +} + +@media (max-width: 960px) { + .settings-shell { + grid-template-columns: 1fr; + } + + .settings-nav { + grid-template-rows: auto auto auto; + border-right: 0; + border-bottom: 1px solid #e2e8f0; + } + + .settings-nav-list { + display: flex; + gap: 8px; + overflow-x: auto; + padding-right: 0; + padding-bottom: 4px; + } + + .settings-nav-item { + min-width: 170px; + } + + .settings-toolbar, + .settings-content { + padding: 20px; + } + + .model-grid, + .form-grid, + .profile-grid { + grid-template-columns: 1fr; + } + + .field-wide, + .field-full { + grid-column: span 1; + } + + .logo-field { + width: fit-content; + } + + .preview-card { + grid-template-columns: 1fr; + justify-items: start; + padding: 20px; + } +} + +@media (max-width: 640px) { + .settings-toolbar { + padding: 16px; + } + + .settings-toolbar-copy h3 { + font-size: 20px; + } + + .settings-content { + padding: 16px; + } + + .settings-card { + padding: 20px 16px; + border-radius: 16px; + } + + .settings-nav { + padding: 16px 12px 12px; + } +} diff --git a/web/src/assets/styles/views/travel-reimbursement-create-view-part2.css b/web/src/assets/styles/views/travel-reimbursement-create-view-part2.css index 5d30f53..929bddd 100644 --- a/web/src/assets/styles/views/travel-reimbursement-create-view-part2.css +++ b/web/src/assets/styles/views/travel-reimbursement-create-view-part2.css @@ -759,7 +759,8 @@ .review-side-metric-card.editable:hover, .review-side-metric-card.editing { border-color: rgba(16, 185, 129, 0.34); - background: rgba(248, 252, 250, 0.92); + background: #ffffff; + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.08); transform: translateY(-1px); } @@ -768,10 +769,25 @@ height: 32px; display: grid; place-items: center; - border-radius: 10px; - background: rgba(240, 253, 244, 0.95); - color: #059669; + border-radius: 8px; + background: #f1f5f9; + border: 1px solid transparent; + color: #64748b; font-size: 15px; + transition: all 0.2s ease; +} + +.review-side-metric-card.editable:hover .review-side-metric-icon, +.review-side-metric-card.editing .review-side-metric-icon { + color: #059669; + border-color: rgba(16, 185, 129, 0.22); + background: #ecfdf5; +} + +.review-side-metric-card.invalid .review-side-metric-icon { + color: #ef4444; + border-color: rgba(239, 68, 68, 0.22); + background: #fef2f2; } .review-side-metric-copy { diff --git a/web/src/assets/styles/views/travel-reimbursement-create-view-part3.css b/web/src/assets/styles/views/travel-reimbursement-create-view-part3.css index 1b3256c..11a2203 100644 --- a/web/src/assets/styles/views/travel-reimbursement-create-view-part3.css +++ b/web/src/assets/styles/views/travel-reimbursement-create-view-part3.css @@ -298,6 +298,16 @@ cursor: not-allowed; } +.review-next-step-rich-copy { + margin-top: 30px; + color: #475569; + line-height: 1.64; +} + +.review-next-step-rich-copy :deep(.markdown-action-paragraph) { + margin-top: 10px; +} + .review-section-card { display: grid; gap: 10px; diff --git a/web/src/assets/styles/views/travel-reimbursement-create-view-part4.css b/web/src/assets/styles/views/travel-reimbursement-create-view-part4.css index de9be64..720fa20 100644 --- a/web/src/assets/styles/views/travel-reimbursement-create-view-part4.css +++ b/web/src/assets/styles/views/travel-reimbursement-create-view-part4.css @@ -1,6 +1,14 @@ +.review-overlay { + display: flex; + align-items: center; + justify-content: center; +} + .review-preview-modal { width: min(980px, calc(100vw - 40px)); max-height: min(92vh, calc(100vh - 32px)); + margin: auto; + flex: none; display: grid; grid-template-rows: auto minmax(0, 1fr); overflow: hidden; @@ -63,48 +71,56 @@ } .welcome-quick-actions-title { - margin: 0 0 22px; + margin: 0 0 16px !important; color: #64748b; - font-size: 12px; - font-weight: 800; + font-size: 13px; + font-weight: 700; + letter-spacing: 0.5px; } .welcome-quick-action-grid { display: flex; flex-wrap: wrap; - gap: 7px; + gap: 12px; + margin-top: 16px; } .welcome-quick-action-btn { - min-height: 30px; + min-height: 32px; display: inline-flex; align-items: center; - gap: 5px; - padding: 0 11px; - border: 1px solid rgba(191, 219, 254, 0.92); - border-radius: 999px; - background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, rgba(239, 246, 255, 0.94) 100%); - color: #1d4ed8; + gap: 6px; + padding: 0 14px; + border: 1px solid #cbd5e1; + border-radius: 12px; + background: #ffffff; + color: #334155; font-size: var(--wb-fs-chip); - font-weight: 700; + font-weight: 600; line-height: 1.2; - box-shadow: 0 4px 10px rgba(59, 130, 246, 0.07); - transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease; + transition: all 0.2s ease; } .welcome-quick-action-btn i { - font-size: 13px; - color: #2563eb; + font-size: 14px; + color: #64748b; + transition: color 0.2s ease; } .welcome-quick-action-btn:hover:not(:disabled) { transform: translateY(-1px); - border-color: rgba(59, 130, 246, 0.34); - box-shadow: 0 7px 14px rgba(59, 130, 246, 0.12); + border-color: #10b981; + background: #ecfdf5; + color: #059669; + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.1); +} + +.welcome-quick-action-btn:hover:not(:disabled) i { + color: #059669; } .welcome-quick-action-btn:disabled { - opacity: 0.48; + opacity: 0.5; cursor: not-allowed; } diff --git a/web/src/assets/styles/views/travel-reimbursement-create-view.css b/web/src/assets/styles/views/travel-reimbursement-create-view.css index de8bd8a..6d452f8 100644 --- a/web/src/assets/styles/views/travel-reimbursement-create-view.css +++ b/web/src/assets/styles/views/travel-reimbursement-create-view.css @@ -522,8 +522,10 @@ .insight-panel-shell { flex: none; + display: flex; width: clamp(300px, 28vw, 420px); min-width: 0; + min-height: 0; max-width: 100%; margin-left: 0; overflow: hidden; @@ -634,6 +636,30 @@ box-shadow: 0 10px 22px rgba(226, 232, 240, 0.48); } +.message-bubble-review-risk-low { + border-color: rgba(37, 99, 235, 0.72); + background: linear-gradient(180deg, rgba(239, 246, 255, 0.72), rgba(255, 255, 255, 0.96)); + box-shadow: + 0 0 0 2px rgba(37, 99, 235, 0.12), + 0 12px 24px rgba(37, 99, 235, 0.1); +} + +.message-bubble-review-risk-medium { + border-color: rgba(217, 119, 6, 0.78); + background: linear-gradient(180deg, rgba(255, 251, 235, 0.76), rgba(255, 255, 255, 0.96)); + box-shadow: + 0 0 0 2px rgba(217, 119, 6, 0.14), + 0 12px 24px rgba(217, 119, 6, 0.11); +} + +.message-bubble-review-risk-high { + border-color: rgba(220, 38, 38, 0.78); + background: linear-gradient(180deg, rgba(254, 242, 242, 0.78), rgba(255, 255, 255, 0.96)); + box-shadow: + 0 0 0 2px rgba(220, 38, 38, 0.14), + 0 12px 24px rgba(220, 38, 38, 0.11); +} + .message-meta { display: flex; align-items: center; @@ -814,6 +840,26 @@ color: #1d4ed8; } +.message-answer-markdown :deep(.markdown-action-link-next), +.message-answer-markdown :deep(.markdown-action-link-edit) { + color: #1d4ed8; +} + +.message-answer-markdown :deep(.markdown-risk-text-low) { + color: #2563eb; + font-weight: 850; +} + +.message-answer-markdown :deep(.markdown-risk-text-medium) { + color: #d97706; + font-weight: 850; +} + +.message-answer-markdown :deep(.markdown-risk-text-high) { + color: #dc2626; + font-weight: 850; +} + .message-answer-markdown :deep(.markdown-table-wrap) { width: 100%; max-width: 100%; diff --git a/web/src/components/layout/SidebarRail.vue b/web/src/components/layout/SidebarRail.vue index 08c3062..13616cb 100644 --- a/web/src/components/layout/SidebarRail.vue +++ b/web/src/components/layout/SidebarRail.vue @@ -2,7 +2,8 @@