sunshibo 1 неделя назад
Родитель
Сommit
19642f3062

+ 189 - 0
HBuilderProjects/lifeAiFetch.js

@@ -0,0 +1,189 @@
+/**
+ * 分享页「更多推荐」AI 接口请求(HTTPS 页在 iOS 微信内不能直连 HTTP:9100,须走同源 alienStore 网关)。
+ * URL 可用 lifeAiBase 覆盖,例如 lifeAiBase=https://test.ailien.shop/alienStore
+ */
+(function (global) {
+	'use strict';
+
+	var API_LIFE_AI_HTTP = 'http://183.252.196.135:9100';
+
+	function isPageHttps() {
+		return (location.protocol || '').toLowerCase() === 'https:';
+	}
+
+	function isIosWxBrowser() {
+		var ua = navigator.userAgent || '';
+		return /iphone|ipad|ipod/i.test(ua) && /micromessenger/i.test(ua);
+	}
+
+	function readQueryParam(name) {
+		var params = new URLSearchParams(location.search || '');
+		var v = params.get(name);
+		if (v != null) return String(v);
+		var hash = location.hash || '';
+		var qi = hash.indexOf('?');
+		if (qi >= 0) {
+			var hp = new URLSearchParams(hash.slice(qi + 1));
+			var hv = hp.get(name);
+			if (hv != null) return String(hv);
+		}
+		return '';
+	}
+
+	/**
+	 * @param {string} [alienStoreBase] 页面内 API_BASE,如 https://test.ailien.shop/alienStore
+	 */
+	function resolveLifeAiApiBases(alienStoreBase) {
+		var out = [];
+		var seen = {};
+		function push(base) {
+			base = String(base || '').trim().replace(/\/+$/, '');
+			if (!base || seen[base]) return;
+			seen[base] = true;
+			out.push(base);
+		}
+
+		var custom = readQueryParam('lifeAiBase').trim();
+		if (custom) push(custom);
+
+		var host = (location.hostname || '').toLowerCase();
+		if (host === 'test.ailien.shop' || host === 'uat.ailien.shop') {
+			push('https://' + host + '/alienStore');
+		} else if (alienStoreBase) {
+			push(String(alienStoreBase).replace(/\/+$/, ''));
+		}
+
+		/* iOS+HTTPS 会拦截明文 fetch;Android 微信常仍可回落直连 IP */
+		if (!isPageHttps() || !isIosWxBrowser()) {
+			push(API_LIFE_AI_HTTP);
+		}
+
+		return out;
+	}
+
+	function isLifeAiApiOk(res) {
+		if (!res || typeof res !== 'object') return false;
+		if (res.success === false) return false;
+		var c = res.code;
+		return c === 200 || c === '200' || Number(c) === 200;
+	}
+
+	function extractRecommendList(res) {
+		if (!res || typeof res !== 'object') return [];
+		if (!isLifeAiApiOk(res)) return [];
+		var raw = res.data != null ? res.data : res.result;
+		if (Array.isArray(raw)) return raw;
+		if (raw && typeof raw === 'object') {
+			var keys = [
+				'list',
+				'records',
+				'rows',
+				'content',
+				'stores',
+				'storeList',
+				'storeVos',
+				'items'
+			];
+			for (var i = 0; i < keys.length; i++) {
+				if (Array.isArray(raw[keys[i]])) return raw[keys[i]];
+			}
+		}
+		if (Array.isArray(res.list)) return res.list;
+		if (Array.isArray(res.records)) return res.records;
+		return [];
+	}
+
+	/**
+	 * 按顺序尝试多个 API 根地址,401/404/网络失败时换下一个。
+	 */
+	function fetchLifeAiPostJson(alienStoreBase, apiPath, body) {
+		var bases = resolveLifeAiApiBases(alienStoreBase);
+		var idx = 0;
+
+		function next(err) {
+			if (idx >= bases.length) {
+				return Promise.reject(err || new Error('lifeAi recommend failed'));
+			}
+			var url = bases[idx++] + apiPath;
+			return fetch(url, {
+				method: 'POST',
+				mode: 'cors',
+				credentials: 'omit',
+				headers: {
+					Accept: 'application/json',
+					'Content-Type': 'application/json;charset=UTF-8'
+				},
+				body: JSON.stringify(body)
+			})
+				.then(function (res) {
+					if (!res.ok) {
+						if (res.status === 401 || res.status === 403 || res.status === 404) {
+							return next(new Error('HTTP ' + res.status));
+						}
+						throw new Error('HTTP ' + res.status);
+					}
+					return res.json();
+				})
+				.then(function (json) {
+					if (json && (json.code === 401 || json.code === '401')) {
+						return next(new Error('code 401'));
+					}
+					return json;
+				})
+				.catch(function (e) {
+					return next(e);
+				});
+		}
+
+		return next();
+	}
+
+	/**
+	 * @param {function(string): string} getParam 如 getMergedParam / mergedQ
+	 */
+	function getMergedRecommendUserLocation(getParam, defaults) {
+		defaults = defaults || {};
+		var defLat = defaults.lat != null ? defaults.lat : 38.925747;
+		var defLng = defaults.lng != null ? defaults.lng : 121.662531;
+		var defCity = defaults.city || '大连市';
+		var gp =
+			typeof getParam === 'function'
+				? getParam
+				: function (n) {
+						return readQueryParam(n);
+					};
+
+		function pick() {
+			var args = arguments;
+			var i;
+			for (i = 0; i < args.length; i++) {
+				var v = String(gp(args[i]) || '').trim();
+				if (v) return v;
+			}
+			return '';
+		}
+
+		var latRaw = pick('userLat', 'latitude', 'lat', 'weidu');
+		var lngRaw = pick('userLng', 'longitude', 'lon', 'lng', 'jingdu');
+		var cityRaw = pick('userCity', 'city');
+
+		return {
+			userLat:
+				latRaw !== '' && !isNaN(Number(latRaw)) ? Number(latRaw) : defLat,
+			userLng:
+				lngRaw !== '' && !isNaN(Number(lngRaw)) ? Number(lngRaw) : defLng,
+			userCity: cityRaw !== '' ? cityRaw : defCity
+		};
+	}
+
+	global.LifeAiFetch = {
+		API_LIFE_AI_HTTP: API_LIFE_AI_HTTP,
+		isPageHttps: isPageHttps,
+		isIosWxBrowser: isIosWxBrowser,
+		resolveLifeAiApiBases: resolveLifeAiApiBases,
+		isLifeAiApiOk: isLifeAiApiOk,
+		extractRecommendList: extractRecommendList,
+		fetchLifeAiPostJson: fetchLifeAiPostJson,
+		getMergedRecommendUserLocation: getMergedRecommendUserLocation
+	};
+})(typeof window !== 'undefined' ? window : this);

+ 13 - 30
HBuilderProjects/shareCheckIn.html

@@ -635,6 +635,7 @@
 	</div>
 
 	<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
+	<script src="lifeAiFetch.js"></script>
 	<script>
 	(function () {
 		'use strict';
@@ -1451,8 +1452,7 @@
 				});
 		}
 
-		/** 与 shareIndex.html 关店「更多推荐」:POST 全局店铺推荐 */
-		var API_LIFE_AI_BASE = 'http://183.252.196.135:9100';
+		/** 与 shareIndex.html 关店「更多推荐」:POST 全局店铺推荐(见 lifeAiFetch.js) */
 		var STORE_GLOBAL_RECOMMEND_PATH =
 			'/ai/multimodal-services/api/v1/search/global/store-recommend';
 		var FALLBACK_REC_USER_LAT = 38.925749;
@@ -1636,21 +1636,9 @@
 		}
 
 		function normalizeClosedStoreRecommendList(res) {
-			if (!res || typeof res !== 'object') return [];
-			var raw = res.data != null ? res.data : res.result;
-			if (Array.isArray(raw)) return raw;
-			if (raw && typeof raw === 'object') {
-				if (Array.isArray(raw.list)) return raw.list;
-				if (Array.isArray(raw.records)) return raw.records;
-				if (Array.isArray(raw.rows)) return raw.rows;
-				if (Array.isArray(raw.content)) return raw.content;
-				if (Array.isArray(raw.stores)) return raw.stores;
-				if (Array.isArray(raw.storeList)) return raw.storeList;
-				if (Array.isArray(raw.storeVos)) return raw.storeVos;
-				if (Array.isArray(raw.items)) return raw.items;
-			}
-			if (Array.isArray(res.list)) return res.list;
-			if (Array.isArray(res.records)) return res.records;
+			if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.extractRecommendList) {
+				return LifeAiFetch.extractRecommendList(res);
+			}
 			return [];
 		}
 
@@ -1691,19 +1679,14 @@
 					userLat: userLat,
 					userLng: userLng
 				};
-				return fetch(API_LIFE_AI_BASE + STORE_GLOBAL_RECOMMEND_PATH, {
-					method: 'POST',
-					mode: 'cors',
-					credentials: 'omit',
-					headers: {
-						Accept: 'application/json',
-						'Content-Type': 'application/json;charset=UTF-8'
-					},
-					body: JSON.stringify(body)
-				}).then(function (res) {
-					if (!res.ok) throw new Error('HTTP ' + res.status);
-					return res.json();
-				});
+				if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.fetchLifeAiPostJson) {
+					return LifeAiFetch.fetchLifeAiPostJson(
+						API_BASE,
+						STORE_GLOBAL_RECOMMEND_PATH,
+						body
+					);
+				}
+				return Promise.reject(new Error('LifeAiFetch unavailable'));
 			}
 
 			var hasUrlCoords =

+ 11 - 28
HBuilderProjects/shareCheckInUndefined.html

@@ -337,6 +337,7 @@
 	</div>
 
 	<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
+	<script src="lifeAiFetch.js"></script>
 	<script>
 	(function () {
 		'use strict';
@@ -348,7 +349,6 @@
 		 * businessStatus=99(关店,与 shareIndex.html 一致):点「APP内打开」深链为 shopro://pages/index/login?…,不再进打卡页。
 		 */
 		var API_BASE = 'https://test.ailien.shop/alienStore';
-		var API_LIFE_AI_BASE = 'http://183.252.196.135:9100';
 		var STORE_GLOBAL_RECOMMEND_PATH =
 			'/ai/multimodal-services/api/v1/search/global/store-recommend';
 		var DEFAULT_USER_LAT = 38.925747;
@@ -1115,37 +1115,20 @@
 				userLng: userLng
 			};
 
-			return fetch(API_LIFE_AI_BASE + STORE_GLOBAL_RECOMMEND_PATH, {
-				method: 'POST',
-				mode: 'cors',
-				credentials: 'omit',
-				headers: {
-					Accept: 'application/json',
-					'Content-Type': 'application/json;charset=UTF-8'
-				},
-				body: JSON.stringify(body)
-			}).then(function (res) {
-				if (!res.ok) throw new Error('HTTP ' + res.status);
-				return res.json();
-			});
+			if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.fetchLifeAiPostJson) {
+				return LifeAiFetch.fetchLifeAiPostJson(
+					API_BASE,
+					STORE_GLOBAL_RECOMMEND_PATH,
+					body
+				);
+			}
+			return Promise.reject(new Error('LifeAiFetch unavailable'));
 		}
 
 		function normalizeStoreRecommendList(res) {
-			if (!res || typeof res !== 'object') return [];
-			var raw = res.data != null ? res.data : res.result;
-			if (Array.isArray(raw)) return raw;
-			if (raw && typeof raw === 'object') {
-				if (Array.isArray(raw.list)) return raw.list;
-				if (Array.isArray(raw.records)) return raw.records;
-				if (Array.isArray(raw.rows)) return raw.rows;
-				if (Array.isArray(raw.content)) return raw.content;
-				if (Array.isArray(raw.stores)) return raw.stores;
-				if (Array.isArray(raw.storeList)) return raw.storeList;
-				if (Array.isArray(raw.storeVos)) return raw.storeVos;
-				if (Array.isArray(raw.items)) return raw.items;
+			if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.extractRecommendList) {
+				return LifeAiFetch.extractRecommendList(res);
 			}
-			if (Array.isArray(res.list)) return res.list;
-			if (Array.isArray(res.records)) return res.records;
 			return [];
 		}
 

+ 60 - 46
HBuilderProjects/shareDynamic.html

@@ -687,6 +687,7 @@
 	</div>
 
 	<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
+	<script src="lifeAiFetch.js"></script>
 	<script> 
 	(function () {
 		'use strict';
@@ -710,14 +711,14 @@
 
 		/**
 		 * 暂无承载数据时更多推荐:POST …/ai/multimodal-services/api/v1/search/global/store-recommend
-		 * 与 shareIndex.html / shareCheckInUndefined.html 一致
+		 * HTTPS 页(尤其 iOS 微信)须走同源 alienStore;见 lifeAiFetch.js。可用 URL 参数 lifeAiBase 覆盖
 		 */
-		var API_LIFE_AI_BASE = 'http://183.252.196.135:9100';
 		var STORE_GLOBAL_RECOMMEND_PATH =
 			'/ai/multimodal-services/api/v1/search/global/store-recommend';
 		var DEFAULT_REC_USER_LAT = 38.925747;
 		var DEFAULT_REC_USER_LNG = 121.662531;
 		var DEFAULT_REC_USER_CITY = '大连市';
+		var cachedRecommendStoreId = '';
 
 		var COMMENT_PAGE_NUM = 1;
 		var COMMENT_PAGE_SIZE = 20;
@@ -1897,6 +1898,15 @@
 		/**
 		 * getDeleteFlagById 要求 query 参数名为 id,值为店铺 storeId(与后端约定一致)。
 		 */
+		function rememberRecommendStoreIdFromGetOne(res) {
+			if (!res || typeof res !== 'object') return;
+			var d = res.data;
+			if (d && typeof d === 'object' && d.storeId != null) {
+				var sid = String(d.storeId).trim();
+				if (sid) cachedRecommendStoreId = sid;
+			}
+		}
+
 		function resolveStoreIdForDeleteFlagApi() {
 			var sid =
 				getMergedParam('storeId').trim() ||
@@ -1917,6 +1927,9 @@
 					}
 				}
 			}
+			if (!sid && cachedRecommendStoreId) {
+				sid = cachedRecommendStoreId;
+			}
 			return sid;
 		}
 
@@ -1929,6 +1942,7 @@
 			var path = '/store/info/getOne?' + qs.toString();
 			return apiFetch(path)
 				.then(function (res) {
+					rememberRecommendStoreIdFromGetOne(res);
 					return res;
 				})
 				.catch(function (e) {
@@ -2012,62 +2026,51 @@
 		}
 
 		function normalizeClosedStoreRecommendList(res) {
-			if (!res || typeof res !== 'object') return [];
-			var raw = res.data != null ? res.data : res.result;
-			if (Array.isArray(raw)) return raw;
-			if (raw && typeof raw === 'object') {
-				if (Array.isArray(raw.list)) return raw.list;
-				if (Array.isArray(raw.records)) return raw.records;
-				if (Array.isArray(raw.rows)) return raw.rows;
-				if (Array.isArray(raw.content)) return raw.content;
-				if (Array.isArray(raw.stores)) return raw.stores;
-				if (Array.isArray(raw.storeList)) return raw.storeList;
-				if (Array.isArray(raw.storeVos)) return raw.storeVos;
-				if (Array.isArray(raw.items)) return raw.items;
-			}
-			if (Array.isArray(res.list)) return res.list;
-			if (Array.isArray(res.records)) return res.records;
+			if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.extractRecommendList) {
+				return LifeAiFetch.extractRecommendList(res);
+			}
 			return [];
 		}
 
+		function getDynRecommendUserLocation() {
+			if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.getMergedRecommendUserLocation) {
+				return LifeAiFetch.getMergedRecommendUserLocation(getMergedParam, {
+					lat: DEFAULT_REC_USER_LAT,
+					lng: DEFAULT_REC_USER_LNG,
+					city: DEFAULT_REC_USER_CITY
+				});
+			}
+			return {
+				userLat: DEFAULT_REC_USER_LAT,
+				userLng: DEFAULT_REC_USER_LNG,
+				userCity: DEFAULT_REC_USER_CITY
+			};
+		}
+
 		function fetchShareClosedStoreRecommend(storeIdStr) {
-			var latRaw = (q('userLat') || q('latitude') || q('lat') || q('weidu')).trim();
-			var lngRaw = (q('userLng') || q('longitude') || q('lon') || q('jingdu')).trim();
-			var userLat =
-				latRaw !== '' && !isNaN(Number(latRaw)) ? Number(latRaw) : DEFAULT_REC_USER_LAT;
-			var userLng =
-				lngRaw !== '' && !isNaN(Number(lngRaw)) ? Number(lngRaw) : DEFAULT_REC_USER_LNG;
-
-			var page = parseInt(q('page') || '1', 10);
-			var pageSize = parseInt(q('pageSize') || '10', 10);
+			var loc = getDynRecommendUserLocation();
+			var page = parseInt(getMergedParam('page') || q('page') || '1', 10);
+			var pageSize = parseInt(getMergedParam('pageSize') || q('pageSize') || '10', 10);
 			if (isNaN(page) || page < 1) page = 1;
 			if (isNaN(pageSize) || pageSize < 1) pageSize = 10;
 
-			var userCityRaw = (q('userCity') || q('city') || '').trim();
-			var userCity = userCityRaw !== '' ? userCityRaw : DEFAULT_REC_USER_CITY;
-
 			var body = {
 				page: page,
 				pageSize: pageSize,
 				storeId: String(storeIdStr || ''),
-				userCity: userCity,
-				userLat: userLat,
-				userLng: userLng
+				userCity: loc.userCity,
+				userLat: loc.userLat,
+				userLng: loc.userLng
 			};
 
-			return fetch(API_LIFE_AI_BASE + STORE_GLOBAL_RECOMMEND_PATH, {
-				method: 'POST',
-				mode: 'cors',
-				credentials: 'omit',
-				headers: {
-					Accept: 'application/json',
-					'Content-Type': 'application/json;charset=UTF-8'
-				},
-				body: JSON.stringify(body)
-			}).then(function (res) {
-				if (!res.ok) throw new Error('HTTP ' + res.status);
-				return res.json();
-			});
+			if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.fetchLifeAiPostJson) {
+				return LifeAiFetch.fetchLifeAiPostJson(
+					API_BASE,
+					STORE_GLOBAL_RECOMMEND_PATH,
+					body
+				);
+			}
+			return Promise.reject(new Error('LifeAiFetch unavailable'));
 		}
 
 		function renderShareClosedRecommended(list) {
@@ -2143,6 +2146,7 @@
 
 				var coverIm = card.querySelector('img.closed-rec-card__cover');
 				if (coverIm) {
+					applyMediaNoReferrer(coverIm);
 					coverIm.src = imgUrl;
 					coverIm.onerror = function () {
 						this.onerror = null;
@@ -2168,7 +2172,17 @@
 					renderShareClosedRecommended(list);
 				})
 				.catch(function (e) {
-					console.error(e);
+					console.error('[store-recommend]', e);
+					if (
+						typeof LifeAiFetch !== 'undefined' &&
+						LifeAiFetch.isPageHttps() &&
+						LifeAiFetch.isIosWxBrowser()
+					) {
+						console.warn(
+							'[store-recommend] iOS HTTPS 页需通过同源 alienStore 访问 AI 推荐;请确认网关已放行匿名 POST ' +
+								STORE_GLOBAL_RECOMMEND_PATH
+						);
+					}
 					renderShareClosedRecommended([]);
 				});
 		}

+ 30 - 44
HBuilderProjects/shareIndex.html

@@ -1068,6 +1068,7 @@
 		<div class="home-indicator" aria-hidden="true"></div>
 	</div>
 	<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
+	<script src="lifeAiFetch.js"></script>
 	<script>
 	(function () {
 		'use strict';
@@ -1116,21 +1117,24 @@
 		 * 关店(businessStatus=99)更多推荐:POST …/ai/multimodal-services/api/v1/search/global/store-recommend
 		 * 与 shareCheckInUndefined.html 一致;请求体 userCity/userLat/userLng 优先取 URL:userCity、lat、lon(兼认 userLat/userLng、weidu/jingdu 等)。
 		 */
-		var API_LIFE_AI_BASE = 'http://183.252.196.135:9100';
 		var STORE_GLOBAL_RECOMMEND_PATH =
 			'/ai/multimodal-services/api/v1/search/global/store-recommend';
-		/** 关店推荐用户位置:优先 URL 的 lat、lon、userCity;缺省再用大连默认值 */
+
+		function mergedQ(name) {
+			var v = mergeSearchAndHashParamsForRedirect().get(name);
+			return v == null ? '' : String(v);
+		}
+
+		/** 关店推荐用户位置:优先 URL(含 hash)的 lat、lon、userCity;缺省再用大连默认值 */
 		function getClosedRecommendUserLocation() {
-			var latRaw = (q('lat') || q('userLat') || q('latitude') || q('weidu') || '').trim();
-			var lonRaw = (q('lon') || q('userLng') || q('longitude') || q('lng') || q('jingdu') || '').trim();
-			var cityRaw = (q('userCity') || q('city') || '').trim();
-			return {
-				userLat:
-					latRaw !== '' && !isNaN(Number(latRaw)) ? Number(latRaw) : 38.925747,
-				userLng:
-					lonRaw !== '' && !isNaN(Number(lonRaw)) ? Number(lonRaw) : 121.662531,
-				userCity: cityRaw !== '' ? cityRaw : '大连市'
-			};
+			if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.getMergedRecommendUserLocation) {
+				return LifeAiFetch.getMergedRecommendUserLocation(mergedQ, {
+					lat: 38.925747,
+					lng: 121.662531,
+					city: '大连市'
+				});
+			}
+			return { userLat: 38.925747, userLng: 121.662531, userCity: '大连市' };
 		}
 
 		/**
@@ -2577,29 +2581,16 @@
 		}
 
 		function normalizeClosedStoreRecommendList(res) {
-			if (!res || typeof res !== 'object') return [];
-			var raw = res.data != null ? res.data : res.result;
-			if (Array.isArray(raw)) return raw;
-			if (raw && typeof raw === 'object') {
-				if (Array.isArray(raw.list)) return raw.list;
-				if (Array.isArray(raw.records)) return raw.records;
-				if (Array.isArray(raw.rows)) return raw.rows;
-				if (Array.isArray(raw.content)) return raw.content;
-				if (Array.isArray(raw.stores)) return raw.stores;
-				if (Array.isArray(raw.storeList)) return raw.storeList;
-				if (Array.isArray(raw.storeVos)) return raw.storeVos;
-				if (Array.isArray(raw.items)) return raw.items;
-			}
-			if (Array.isArray(res.list)) return res.list;
-			if (Array.isArray(res.records)) return res.records;
+			if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.extractRecommendList) {
+				return LifeAiFetch.extractRecommendList(res);
+			}
 			return [];
 		}
 
 		function fetchShareClosedStoreRecommend(storeIdStr) {
 			var loc = getClosedRecommendUserLocation();
-
-			var page = parseInt(q('page') || '1', 10);
-			var pageSize = parseInt(q('pageSize') || '10', 10);
+			var page = parseInt(mergedQ('page') || q('page') || '1', 10);
+			var pageSize = parseInt(mergedQ('pageSize') || q('pageSize') || '10', 10);
 			if (isNaN(page) || page < 1) page = 1;
 			if (isNaN(pageSize) || pageSize < 1) pageSize = 10;
 
@@ -2612,19 +2603,14 @@
 				userLng: loc.userLng
 			};
 
-			return fetch(API_LIFE_AI_BASE + STORE_GLOBAL_RECOMMEND_PATH, {
-				method: 'POST',
-				mode: 'cors',
-				credentials: 'omit',
-				headers: {
-					Accept: 'application/json',
-					'Content-Type': 'application/json;charset=UTF-8'
-				},
-				body: JSON.stringify(body)
-			}).then(function (res) {
-				if (!res.ok) throw new Error('HTTP ' + res.status);
-				return res.json();
-			});
+			if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.fetchLifeAiPostJson) {
+				return LifeAiFetch.fetchLifeAiPostJson(
+					API_BASE,
+					STORE_GLOBAL_RECOMMEND_PATH,
+					body
+				);
+			}
+			return Promise.reject(new Error('LifeAiFetch unavailable'));
 		}
 
 		function renderShareClosedRecommended(list) {
@@ -2725,7 +2711,7 @@
 					renderShareClosedRecommended(list);
 				})
 				.catch(function (e) {
-					console.error(e);
+					console.error('[store-recommend]', e);
 					renderShareClosedRecommended([]);
 				});
 		}

+ 24 - 26
HBuilderProjects/shareUndefined.html

@@ -588,6 +588,7 @@
 	</div>
 
 	<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
+	<script src="lifeAiFetch.js"></script>
 	<script>
 	(function () {
 		'use strict';
@@ -602,7 +603,6 @@
 		 * 注意:用 file:// 打开本页时,浏览器可能因 CORS 拦截跨域请求。
 		 */
 		var API_BASE = 'https://test.ailien.shop/alienStore';
-		var API_LIFE_AI_BASE = 'http://183.252.196.135:9100';
 		var SECOND_GLOBAL_RECOMMEND_PATH = '/ai/life-manager/api/v1/second_hand/global-recommend';
 		var DEFAULT_REC_RADIUS_KM = 195.69;
 
@@ -1540,33 +1540,19 @@
 				userId: userId
 			};
 
-			return fetch(API_LIFE_AI_BASE + SECOND_GLOBAL_RECOMMEND_PATH, {
-				method: 'POST',
-				mode: 'cors',
-				credentials: 'omit',
-				headers: {
-					Accept: 'application/json',
-					'Content-Type': 'application/json;charset=UTF-8'
-				},
-				body: JSON.stringify(body)
-			}).then(function (res) {
-				if (!res.ok) throw new Error('HTTP ' + res.status);
-				return res.json();
-			});
+			if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.fetchLifeAiPostJson) {
+				return LifeAiFetch.fetchLifeAiPostJson(
+					API_BASE,
+					SECOND_GLOBAL_RECOMMEND_PATH,
+					body
+				);
+			}
+			return Promise.reject(new Error('LifeAiFetch unavailable'));
 		}
 
 		function normalizeGlobalRecommendList(res) {
-			if (!res || typeof res !== 'object') return [];
-			var raw = null;
-			if (isApiOk(res)) {
-				raw = res.data != null ? res.data : res.result;
-			}
-			if (Array.isArray(raw)) return raw;
-			if (raw && typeof raw === 'object') {
-				if (Array.isArray(raw.list)) return raw.list;
-				if (Array.isArray(raw.records)) return raw.records;
-				if (Array.isArray(raw.rows)) return raw.rows;
-				if (Array.isArray(raw.content)) return raw.content;
+			if (typeof LifeAiFetch !== 'undefined' && LifeAiFetch.extractRecommendList) {
+				return LifeAiFetch.extractRecommendList(res);
 			}
 			return [];
 		}
@@ -1899,6 +1885,7 @@
 					'</div>';
 				var coverIm = card.querySelector('img.rec-card__cover');
 				if (coverIm) {
+					coverIm.setAttribute('referrerpolicy', 'no-referrer');
 					coverIm.src = imgUrl;
 					coverIm.onerror = function () {
 						this.onerror = null;
@@ -1907,6 +1894,7 @@
 				}
 				var avIm = card.querySelector('img.rec-card__avatar');
 				if (avIm) {
+					avIm.setAttribute('referrerpolicy', 'no-referrer');
 					avIm.src = userImg || 'images/demouser.png';
 					avIm.onerror = function () {
 						this.onerror = null;
@@ -1927,7 +1915,17 @@
 					renderRecommended(list);
 				})
 				.catch(function (e) {
-					console.error(e);
+					console.error('[global-recommend]', e);
+					if (
+						typeof LifeAiFetch !== 'undefined' &&
+						LifeAiFetch.isPageHttps() &&
+						LifeAiFetch.isIosWxBrowser()
+					) {
+						console.warn(
+							'[global-recommend] iOS HTTPS 页需通过同源 alienStore 访问;请确认网关已放行匿名 POST ' +
+								SECOND_GLOBAL_RECOMMEND_PATH
+						);
+					}
 					renderRecommended([]);
 				});
 		}