diff --git a/backend/app/api/approval.py b/backend/app/api/approval.py index ccaa639..800e9d4 100644 --- a/backend/app/api/approval.py +++ b/backend/app/api/approval.py @@ -51,6 +51,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["办公用品", "超额", "领用"], "url": "https://app.work.weixin.qq.com/wework_admin/approval_v3#/?template_id=3WN6zRucbjycdnR94gBvkSVuXRamX7pKW4PrmNFh&sp_id=&from=template_list", "location": "企微审批", + "category": "办公用品申请", + "icon": "gift-o", + "desc": "超配额申领", }, # --- 设备申请 --- # IT资产领用 改用ITSM工单系统(企微审批模板 C4c8qt31... 已失效) @@ -61,6 +64,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["资产领用", "领用登记", "设备领用"], "url": "https://itsm.servyou.com.cn/itsm-miniapp-mobile/", "location": "运维平台", + "category": "设备申请", + "icon": "orders-o", + "desc": "申请新设备", }, # IT资产借用 改用ITSM工单系统(企微审批模板 3TmACnFs... 已失效) "asset_borrow": { @@ -70,6 +76,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["资产借用", "借用", "借用设备"], "url": "https://itsm.servyou.com.cn/itsm-miniapp-mobile/", "location": "运维平台", + "category": "设备申请", + "icon": "orders-o", + "desc": "临时借用设备", }, # IT资产升级 改用ITSM工单系统(企微审批模板 Bs7ucTGs... 已失效) "asset_upgrade": { @@ -79,6 +88,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["资产升级", "设备升级", "升级"], "url": "https://itsupport.servyou.com.cn/h5/itsm-bridge.html?name=IT%E8%AE%BE%E5%A4%87%E5%8D%87%E7%BA%A7%E4%B8%8E%E7%A1%AC%E4%BB%B6%E7%BB%B4%E4%BF%AE", "location": "运维平台", + "category": "设备申请", + "icon": "orders-o", + "desc": "设备升级换新", }, "it_device_repair": { "id": "it_device_repair", @@ -87,6 +99,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["设备升级", "硬件维修", "设备维修"], "url": "https://itsupport.servyou.com.cn/h5/itsm-bridge.html?name=IT%E8%AE%BE%E5%A4%87%E5%8D%87%E7%BA%A7%E4%B8%8E%E7%A1%AC%E4%BB%B6%E7%BB%B4%E4%BF%AE", "location": "运维平台", + "category": "设备申请", + "icon": "warn-o", + "desc": "设备维修", }, # --- 账号权限申请 --- "wecom_external": { @@ -96,6 +111,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["外联", "企微外联", "外部联系人"], "url": "https://app.work.weixin.qq.com/wework_admin/approval_v3#/?template_id=C4WrCbZd214XrDMZJiHDho7ZQHWX7gsabb7x2fF72&sp_id=&from=template_list", "location": "企微审批", + "category": "账号权限申请", + "icon": "lock", + "desc": "外部联系人权限", }, "zero_trust_vpn": { "id": "zero_trust_vpn", @@ -104,6 +122,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["VPN", "vpn", "零信任", "VPN账号"], "url": "https://itsupport.servyou.com.cn/h5/itsm-bridge.html?name=%E5%91%98%E5%B7%A5%E9%9B%B6%E4%BF%A1%E4%BB%BB%EF%BC%88%E5%8E%9FVPN%EF%BC%89%E8%B4%A6%E5%8F%B7%E7%94%B3%E8%AF%B7", "location": "运维平台", + "category": "账号权限申请", + "icon": "lock", + "desc": "零信任VPN", }, # --- 软件服务申请 --- 改用ITSM工单系统(企微审批模板 3TmACf8D... 已失效) "software_service": { @@ -113,6 +134,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["软件", "商业软件", "软件服务"], "url": "https://itsupport.servyou.com.cn/h5/itsm-bridge.html?name=%E5%95%86%E4%B8%9A%E8%BD%AF%E4%BB%B6%E6%9C%8D%E5%8A%A1%E7%94%B3%E8%AF%B7", "location": "运维平台", + "category": "软件服务申请", + "icon": "apps-o", + "desc": "正版软件授权", }, # --- 资产处置申请 --- "asset_repair": { @@ -122,6 +146,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["外修", "资产外修", "设备维修"], "url": "https://app.work.weixin.qq.com/wework_admin/approval_v3#/?template_id=Bs7ucTLPo42dtj8Y1LzBoujijsa6geRWaRxZJjk4X&sp_id=&from=template_list", "location": "企微审批", + "category": "资产处置申请", + "icon": "warn-o", + "desc": "设备送修", }, "asset_scrap": { "id": "asset_scrap", @@ -130,6 +157,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["报废", "资产报废", "设备报废"], "url": "https://app.work.weixin.qq.com/wework_admin/approval_v3#/?template_id=C4WroCDfWuHKyujQjatjm3AjNv67imXk5C6WNooFkb&sp_id=&from=template_list", "location": "企微审批", + "category": "资产处置申请", + "icon": "delete-o", + "desc": "设备报废", }, "asset_return": { "id": "asset_return", @@ -138,6 +168,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["退还", "资产退还", "退还设备"], "url": "https://app.work.weixin.qq.com/wework_admin/approval_v3#/?template_id=C4c8qt33AZ52a7n9BBWDh6PmsDnpM5B6w8geqqqoHz&sp_id=&from=template_list", "location": "企微审批", + "category": "资产处置申请", + "icon": "back-top", + "desc": "退还设备", }, # --- 会议室故障报修 --- "meeting_room_repair": { @@ -147,6 +180,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["会议室", "故障报修", "报修"], "url": "https://app.work.weixin.qq.com/wework_admin/approval_v3#/?template_id=C4ZXJMtjQJiPXo6N5vNMK26uPRT3KTi9VvkH2NScg&sp_id=&from=template_list", "location": "企微审批", + "category": "会议室故障报修", + "icon": "warn-o", + "desc": "会议室设备故障", }, # --- 企业应用管理 --- "app_management": { @@ -156,6 +192,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["企业应用", "应用管理", "应用开通"], "url": "https://app.work.weixin.qq.com/wework_admin/approval_v3#/?template_id=3WLJnRg2Se1fwizQtNvFtcYMgci1mhRJZhMw2FFKb&sp_id=&from=template_list", "location": "企微审批", + "category": "企业应用管理", + "icon": "apps-o", + "desc": "企业应用开通管理", }, # --- 资产变更确认 --- "asset_change": { @@ -165,6 +204,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["资产变更", "变更确认", "变更"], "url": "https://app.work.weixin.qq.com/wework_admin/approval_v3#/?template_id=C4cA2owjRcXRRPQ46otZvUHoWNEKL5t25tHHfeePip&sp_id=&from=template_list", "location": "企微审批", + "category": "资产变更确认", + "icon": "exchange", + "desc": "资产信息变更", }, # --- 终端设备网络准入 --- "network_access": { @@ -174,6 +216,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["网络准入", "终端准入", "准入申请"], "url": "https://itsupport.servyou.com.cn/h5/itsm-bridge.html?name=%E7%BB%88%E7%AB%AF%E8%AE%BE%E5%A4%87%E7%BD%91%E7%BB%9C%E5%87%86%E5%85%A5%E7%94%B3%E8%AF%B7", "location": "运维平台", + "category": "终端设备网络准入", + "icon": "lock", + "desc": "终端网络准入", }, # --- 活动与会议技术支持 --- "event_support": { @@ -183,6 +228,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["活动支持", "会议支持", "技术支持", "活动技术"], "url": "https://itsupport.servyou.com.cn/h5/itsm-bridge.html?name=%E6%B4%BB%E5%8A%A8%E4%B8%8E%E4%BC%9A%E8%AE%AE%E6%8A%80%E6%9C%AF%E6%94%AF%E6%8C%81", "location": "运维平台", + "category": "活动与会议技术支持", + "icon": "calendar-o", + "desc": "活动会议技术保障", }, # --- 员工IT支持与故障报修 --- "it_support_repair": { @@ -192,6 +240,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["故障报修", "IT支持", "技术支持", "报修"], "url": "https://itsupport.servyou.com.cn/h5/itsm-bridge.html?name=%E5%91%98%E5%B7%A5IT%E6%94%AF%E6%8C%81%E4%B8%8E%E6%95%85%E9%9A%9C%E6%8A%A5%E4%BF%AE", "location": "运维平台", + "category": "员工IT支持与故障报修", + "icon": "warn-o", + "desc": "IT支持与故障报修", }, # --- 公共邮箱账号申请 --- "public_email": { @@ -201,6 +252,9 @@ APPROVAL_TEMPLATES: dict[str, dict] = { "keywords": ["公共邮箱", "公共账号", "共享邮箱"], "url": "https://itsupport.servyou.com.cn/h5/itsm-bridge.html?name=%E5%85%AC%E5%85%B1%E9%82%AE%E7%AE%B1%E8%B4%A6%E5%8F%B7%E7%94%B3%E8%AF%B7", "location": "运维平台", + "category": "公共邮箱账号申请", + "icon": "lock", + "desc": "公共邮箱账号", }, } diff --git a/backend/app/services/approval_matcher.py b/backend/app/services/approval_matcher.py index d7d433c..8d2576c 100644 --- a/backend/app/services/approval_matcher.py +++ b/backend/app/services/approval_matcher.py @@ -8,7 +8,7 @@ # 2. approval_type 关键词匹配模板 keywords(如 "VPN账号申请" 含 "VPN") # 3. approval_type 匹配 category(如 "账号权限申请" → 返回该分类下所有模板) # 4. title 匹配模板 name(如 "VPN账号申请" → zero_trust_vpn) -# 5. 降级:用户原始文本遍历所有 keywords +# 5. 兜底:全部失败时返回全量 18 项卡片(v4.0 P1-4,不再返回 None) # # 输出:标准化卡片数据 card_data,前端纯渲染 # ============================================================================= @@ -31,44 +31,58 @@ class ApprovalMatcher: self, approval_type: Optional[str], title: Optional[str] = None ) -> Optional[dict]: """一站式匹配 + 构建标准化卡片数据(Dify 正常路径)。 - + Args: approval_type: Dify 返回的审批类型(可能是 ID、中文分类名或具体名称) title: Dify 返回的具体审批名称(如"VPN账号申请") - + Returns: - 标准化 card_data,或 None(匹配失败) + 标准化 card_data,或 None(无 approval_type 时) + v4.0 P1-4:匹配失败不再返回 None,兜底返回全量卡片 """ if not approval_type: return None - + # 优先级 1:精确 ID 匹配 template = self._match_by_id(approval_type) if template: return self._build_single_card(template) - + # 优先级 2:关键词匹配 template = self._match_by_keyword(approval_type) if template: return self._build_single_card(template) - + # 优先级 3:category 匹配 options = self._match_by_category(approval_type) if options: return self._build_multi_card(approval_type, options) - - # 优先级 4:title 匹配 + + # 优先级 4:title 匹配(先关键词后名称,title 是具体名称如"VPN账号申请") if title: - template = self._match_by_name(title) + template = self._match_by_keyword(title) or self._match_by_name(title) if template: return self._build_single_card(template) - - # 优先级 5:文本 keywords 兜底 - template = self._match_by_keyword(approval_type) - if template: - return self._build_single_card(template) - - return None + + # 优先级 5(v4.0 P1-4):兜底返回全量卡片 + # 原死分支(重复调用 _match_by_keyword)已删除——与优先级 2 数学上等价不可达 + logger.info( + f"[ApprovalMatcher] 匹配失败,兜底全量卡片: " + f"approval_type={approval_type}, title={title}" + ) + return self.build_all_categories_card() + + def build_all_categories_card(self) -> dict: + """构建全量 18 项审批卡片(匹配失败兜底 + 快捷申请入口共用)。""" + return { + "card_type": "multiple", + "title": "全部审批类型", + "description": "请选择需要提交的审批", + "options": [ + self._template_to_option(t) + for t in APPROVAL_TEMPLATES.values() + ], + } def match_by_keywords(self, user_text: str) -> Optional[dict]: """Dify 不可用时,用用户原始文本关键词降级匹配。 diff --git a/backend/app/tasks/h5_ai_task.py b/backend/app/tasks/h5_ai_task.py index cb13f2f..0d74183 100644 --- a/backend/app/tasks/h5_ai_task.py +++ b/backend/app/tasks/h5_ai_task.py @@ -466,7 +466,8 @@ async def _persist_and_push_structured( action["card_data"] = matched_card logger.info(f"[ApprovalMatcher] 匹配成功: {approval_type} -> card_type={matched_card.get('card_type')}") else: - logger.warning(f"[ApprovalMatcher] 匹配失败: approval_type={approval_type}, title={title}") + # v4.0 P1-4 后:matcher 仅在 approval_type 为空时返回 None(其余情况兜底全量卡片) + logger.info(f"[ApprovalMatcher] approval_type 为空,无卡片: title={title}") extra_data["action"] = action