From aedb037a68b925ef5f0229de1484d63db7e19da9 Mon Sep 17 00:00:00 2001
From: shinnosuke <tantao@1hoper.com>
Date: 星期一, 24 八月 2026 10:19:54 +0800
Subject: [PATCH] fix: expose admin approval window pdf

---
 talent-card-server/src/main/java/com/talentcard/server/business/portal/AdminApprovalPortalService.java |   35 +++++++++++++++++
 rencaika/pages/admin-approval/admin-approval.uvue                                                      |   33 +++++++++++++++-
 2 files changed, 64 insertions(+), 4 deletions(-)

diff --git a/rencaika/pages/admin-approval/admin-approval.uvue b/rencaika/pages/admin-approval/admin-approval.uvue
index 4366db4..0ec93db 100644
--- a/rencaika/pages/admin-approval/admin-approval.uvue
+++ b/rencaika/pages/admin-approval/admin-approval.uvue
@@ -54,6 +54,32 @@
 	const listLoaded = ref(false)
 	const listError = ref(false)
 
+	const uploadUrl = (value: any): string => {
+		if (value == null) return ''
+		if (typeof value == 'object') {
+			const keys = ['url', 'path', 'fileUrl', 'file_url', 'downloadUrl', 'download_url']
+			for (const key of keys) {
+				if (value[key] != null && String(value[key]).trim() != '') return String(value[key]).trim()
+			}
+			return ''
+		}
+		return String(value).trim()
+	}
+	const normalizeOrganization = (source: any): any => {
+		const item = source != null ? source : {}
+		const keys = ['windowInfoUrl', 'window_info_url', 'pdfFile', 'pdf_file', 'windowInfo', 'window_info', 'windowFile', 'window_file', 'fileUrl', 'file_url']
+		let windowInfoUrl = ''
+		for (const key of keys) {
+			const candidate = uploadUrl(item[key])
+			if (candidate != '') { windowInfoUrl = candidate; break }
+		}
+		const normalized: any = { ...item }
+		normalized.windowInfoUrl = windowInfoUrl
+		normalized.hasWindowInfo = windowInfoUrl != ''
+		if (normalized.phone == null) normalized.phone = ''
+		return normalized
+	}
+
 	onLoad((options: any) => {
 		// #ifdef MP-WEIXIN
 		const capsule = uni.getMenuButtonBoundingClientRect()
@@ -77,7 +103,7 @@
 			heroImage.value = data.heroImage != null && data.heroImage != '' ? resolveApiUrl(data.heroImage as string) : ''
 			benefitAvailable.value = data.available === true
 			unavailableReason.value = data.unavailableReason || '该权益暂未开放'
-			orgs.value = data.organizations instanceof Array ? data.organizations as any[] : []
+			orgs.value = data.organizations instanceof Array ? (data.organizations as any[]).map(normalizeOrganization) : []
 			listLoading.value = false
 			listLoaded.value = true
 		}, () => { listLoading.value = false; listError.value = true })
@@ -86,12 +112,13 @@
 	const replaceHero = () => { heroImage.value = '' }
 	const callOrg = (item: any) => { if (item.phone == null || item.phone == '') { uni.showToast({ title: '机构暂未提供咨询电话', icon: 'none' }); return }; uni.makePhoneCall({ phoneNumber: item.phone, fail: () => uni.showToast({ title: '暂时无法拨打电话', icon: 'none' }) }) }
 	const openWindowInfo = (item: any) => {
-		const source = item != null && item.windowInfoUrl != null ? String(item.windowInfoUrl) : ''
+		const normalized = normalizeOrganization(item)
+		const source = normalized.windowInfoUrl
 		if (source == '') {
 			uni.showToast({ title: '窗口信息文件暂未配置', icon: 'none' })
 			return
 		}
-		const url = resolveApiUrl(source)
+		const url = resolveApiUrl(source.indexOf('/') == 0 || source.indexOf('http://') == 0 || source.indexOf('https://') == 0 ? source : '/' + source)
 		uni.showLoading({ title: '正在打开文件', mask: true })
 		uni.downloadFile({
 			url: url,
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/portal/AdminApprovalPortalService.java b/talent-card-server/src/main/java/com/talentcard/server/business/portal/AdminApprovalPortalService.java
index ac9091a..8270988 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/portal/AdminApprovalPortalService.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/portal/AdminApprovalPortalService.java
@@ -67,7 +67,10 @@
             organization.put("description", text(item.get("content"), text(merchant.get("rightsText"), "暂无权益说明")));
             organization.put("phone", text(item.get("phone"), text(merchant.get("contactMobile"))));
             organization.put("officeHours", "");
-            String windowInfoUrl = text(item.get("windowInfoUrl"), text(item.get("pdfFile"), text(item.get("pdf_file"))));
+            String windowInfoUrl = firstText(item,
+                    "windowInfoUrl", "window_info_url", "pdfFile", "pdf_file",
+                    "windowInfo", "window_info", "windowFile", "window_file",
+                    "fileUrl", "file_url");
             organization.put("windowInfoUrl", windowInfoUrl);
             organization.put("windows", List.of());
             organization.put("hasWindowInfo", !windowInfoUrl.isBlank());
@@ -157,4 +160,34 @@
         String valueText = text(value).trim();
         return valueText.isBlank() ? fallback : valueText;
     }
+
+    /**
+     * Window PDFs were stored under several names while the admin form was
+     * being rolled out. Keep the portal contract stable by resolving the
+     * first non-empty URL from all supported names (including nested upload
+     * response objects).
+     */
+    private String firstText(Map<String, Object> source, String... keys) {
+        for (String key : keys) {
+            Object value = source.get(key);
+            String resolved = uploadUrl(value);
+            if (!resolved.isBlank()) return resolved;
+        }
+        return "";
+    }
+
+    @SuppressWarnings("unchecked")
+    private String uploadUrl(Object value) {
+        if (value instanceof Map<?, ?> map) {
+            for (String key : List.of("url", "path", "fileUrl", "file_url", "downloadUrl", "download_url")) {
+                Object nested = map.get(key);
+                if (nested != null) {
+                    String resolved = text(nested).trim();
+                    if (!resolved.isBlank()) return resolved;
+                }
+            }
+            return "";
+        }
+        return text(value).trim();
+    }
 }

--
Gitblit v1.7.1