hyapp-h5/yumi_commmon/yumi-common.js
2026-07-09 13:25:54 +08:00

642 lines
21 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function () {
'use strict';
var PROD_API_BASE = 'https://jvapi.haiyihy.com';
var TEST_API_BASE = 'https://jvapi-test.haiyihy.com';
var LOCAL_API_BASE = 'http://127.0.0.1:13000';
var APP_CODE = 'yumi';
var RELATION_TYPE_BY_TAB = {
cp: 'CP',
brother: 'BROTHER',
sister: 'SISTERS',
};
var headers = {
authorization: '',
'req-app-intel':
'version=1.0.0;build=1;model=H5;sysVersion=web;channel=Web',
'req-client': 'H5',
'req-imei': 'H5-STATIC',
'req-lang': 'en',
'req-sys-origin': 'origin=YUMI;originChild=YUMI',
'req-version': 'V2',
'req-zone': 'Asia/Shanghai',
};
var readyPromise = null;
function currentParams() {
var params = new URLSearchParams(window.location.search || '');
var hash = window.location.hash || '';
if (hash.indexOf('?') >= 0) {
new URLSearchParams(hash.split('?').slice(1).join('?')).forEach(
function (value, key) {
if (!params.has(key)) params.set(key, value);
}
);
}
return params;
}
function query(name) {
return currentParams().get(name);
}
function normalizeBaseURL(value) {
return String(value || '').replace(/\/+$/, '');
}
function resolveEnv() {
var env = String(query('env') || '')
.trim()
.toLowerCase();
return env === 'test' || env === 'local' || env === 'dev' ? env : '';
}
function baseURL() {
var config = window.YumiCommonConfig || {};
var override =
query('apiBase') ||
query('api_base') ||
query('yumi_api_base') ||
window.YUMI_API_BASE_URL ||
config.apiBase;
if (override) return normalizeBaseURL(override);
var env = resolveEnv();
if (env === 'local')
return normalizeBaseURL(config.localApi || LOCAL_API_BASE);
if (env === 'test' || env === 'dev')
return normalizeBaseURL(config.testApi || TEST_API_BASE);
return normalizeBaseURL(config.prodApi || PROD_API_BASE);
}
function buildURL(path, queryData) {
var url = new URL(
String(path || '').replace(/^\/+/, ''),
baseURL() + '/'
);
Object.keys(queryData || {}).forEach(function (key) {
var value = queryData[key];
if (value === undefined || value === null || value === '') return;
url.searchParams.set(key, value);
});
return url.toString();
}
function normalizeAuthorization(value) {
var text = String(value || '').trim();
if (!text) return '';
return /^Bearer\s+/i.test(text) ? text : 'Bearer ' + text;
}
function tokenForHyAppAPI(value) {
return String(value || '').replace(/^Bearer\s+/i, '');
}
function splitHeaderPairs(value) {
var result = {};
String(value || '')
.split(';')
.forEach(function (item) {
var index = item.indexOf('=');
if (index > -1)
result[item.slice(0, index)] = item.slice(index + 1);
});
return result;
}
function putHeader(target, key, value) {
if (
value === undefined ||
value === null ||
value === '' ||
target[key]
)
return;
target[key] = String(value);
}
function normalizeHeaderCandidate(value, depth) {
depth = depth || 0;
if (depth > 4 || value === null || value === undefined) return {};
if (typeof value === 'string') {
var trimmed = value.trim();
if (!trimmed) return {};
if (trimmed.charAt(0) === '{' || trimmed.charAt(0) === '[') {
try {
return normalizeHeaderCandidate(
JSON.parse(trimmed),
depth + 1
);
} catch (_) {
return {};
}
}
return trimmed ? { authorization: trimmed } : {};
}
if (Array.isArray(value)) {
return value.reduce(function (result, item) {
return Object.assign(
result,
normalizeHeaderCandidate(item, depth + 1)
);
}, {});
}
if (typeof value !== 'object') return {};
var appIntel = splitHeaderPairs(
value['Req-App-Intel'] || value['req-app-intel']
);
var sysOrigin = splitHeaderPairs(
value['Req-Sys-Origin'] || value['req-sys-origin']
);
var normalized = {};
putHeader(
normalized,
'authorization',
value.Authorization ||
value.authorization ||
value.token ||
value.accessToken
);
putHeader(
normalized,
'reqLang',
value['Req-Lang'] ||
value['req-lang'] ||
value.reqLang ||
value.lang
);
putHeader(
normalized,
'appVersion',
appIntel.version || value.appVersion || value['App-Version']
);
putHeader(
normalized,
'buildVersion',
appIntel.build || value.buildVersion
);
putHeader(normalized, 'model', appIntel.model || value.model);
putHeader(normalized, 'channel', appIntel.channel || value.channel);
putHeader(
normalized,
'reqImei',
value['Req-Imei'] || value['req-imei'] || value.reqImei
);
putHeader(
normalized,
'origin',
sysOrigin.origin || value.sysOrigin || value.origin
);
putHeader(
normalized,
'child',
sysOrigin.originChild ||
sysOrigin.child ||
value.sysOriginChild ||
value.child
);
['headerInfo', 'headers', 'auth', 'data', 'state'].forEach(
function (key) {
Object.assign(
normalized,
normalizeHeaderCandidate(value[key], depth + 1)
);
}
);
return normalized;
}
function setHeadersFromApp(rawOrObject) {
var data = normalizeHeaderCandidate(rawOrObject || {});
var next = {};
if (data.authorization)
next.authorization = normalizeAuthorization(data.authorization);
if (data.reqLang) next['req-lang'] = data.reqLang;
if (
data.appVersion ||
data.buildVersion ||
data.model ||
data.channel
) {
next['req-app-intel'] = [
data.appVersion ? 'version=' + data.appVersion : '',
data.buildVersion ? 'build=' + data.buildVersion : '',
data.model ? 'model=' + data.model : '',
data.channel ? 'channel=' + data.channel : '',
]
.filter(Boolean)
.join(';');
}
if (data.reqImei) next['req-imei'] = data.reqImei;
if (data.origin) {
next['req-sys-origin'] = [
'origin=' + data.origin,
'originChild=' + (data.child || data.origin),
].join(';');
}
headers = Object.assign(headers, next);
if (window.HyAppAPI) {
if (next.authorization)
window.HyAppAPI.setAccessToken(
tokenForHyAppAPI(next.authorization)
);
window.HyAppAPI.setAppCode(APP_CODE);
}
return headers;
}
function discoverQueryHeaders() {
return {
authorization:
query('authorization') ||
query('Authorization') ||
query('token') ||
query('accessToken') ||
query('access_token'),
reqLang: query('lang') || query('language') || query('locale'),
origin: query('sysOrigin') || query('origin') || 'YUMI',
child: query('sysOriginChild') || query('originChild') || 'YUMI',
channel: query('channel') || query('appChannel'),
};
}
function readStorageHeaders(storage) {
var collected = {};
if (!storage) return collected;
[
'authorization',
'Authorization',
'token',
'accessToken',
'headerInfo',
'headersFromApp',
'appHeaders',
'user',
'userInfo',
].forEach(function (key) {
try {
Object.assign(
collected,
normalizeHeaderCandidate(storage.getItem(key))
);
} catch (_) {}
});
return collected;
}
function applyDiscoveredHeaders() {
setHeadersFromApp(discoverQueryHeaders());
setHeadersFromApp(window.__APP_HEADERS__);
setHeadersFromApp(window.__HEADER_INFO__);
setHeadersFromApp(window.headerInfo);
try {
setHeadersFromApp(readStorageHeaders(window.localStorage));
setHeadersFromApp(readStorageHeaders(window.sessionStorage));
} catch (_) {}
}
function ensureYumiAppCode() {
if (window.HyAppAPI) window.HyAppAPI.setAppCode(APP_CODE);
var params = currentParams();
if (
params.get('app_code') ||
params.get('app') ||
params.get('appCode')
)
return;
if (!window.history || !window.history.replaceState) return;
var url = new URL(window.location.href);
url.searchParams.set('app_code', APP_CODE);
// 目标仓 common/api.js 没有 app_code 会默认走 laluYumi 页入口必须显式落到 Yumi 租户。
window.history.replaceState(null, '', url.toString());
}
function isAppEnvironment() {
return Boolean(
window.app ||
window.FlutterApp ||
window.FlutterPageControl ||
window.webkit
);
}
function requestAccessOrigin() {
return new Promise(function (resolve) {
var settled = false;
var timeoutId = null;
function finish(raw) {
if (raw) setHeadersFromApp(raw);
if (settled) return;
settled = true;
if (timeoutId) window.clearTimeout(timeoutId);
resolve(raw || null);
}
window.renderData = finish;
window.getIosAccessOriginParam = finish;
if (!isAppEnvironment()) {
finish(null);
return;
}
// Android 普通 WebView 会注入 window.app.getAccessOriginiOS/旧页可能只通过回调异步回填。
if (
window.app &&
typeof window.app.getAccessOrigin === 'function'
) {
try {
finish(window.app.getAccessOrigin());
return;
} catch (_) {}
}
if (
window.FlutterApp &&
typeof window.FlutterApp.postMessage === 'function'
) {
try {
window.FlutterApp.postMessage('requestAccessOrigin');
} catch (_) {}
}
var attempt = 0;
var poll = function () {
attempt += 1;
if (
window.app &&
typeof window.app.getAccessOrigin === 'function'
) {
try {
finish(window.app.getAccessOrigin());
return;
} catch (_) {}
}
if (!settled && attempt < 30) window.setTimeout(poll, 60);
};
poll();
timeoutId = window.setTimeout(function () {
finish(null);
}, 1800);
});
}
function connectToApp() {
if (readyPromise) return readyPromise;
ensureYumiAppCode();
applyDiscoveredHeaders();
readyPromise = requestAccessOrigin().then(function () {
return {
baseURL: baseURL(),
headers: Object.assign({}, headers),
authenticated: Boolean(headers.authorization),
};
});
return readyPromise;
}
function parseResponse(response) {
return response.text().then(function (text) {
var payload = text ? JSON.parse(text) : {};
if (!response.ok) {
var httpError = new Error(
payload.message || response.statusText || 'request_failed'
);
httpError.status = response.status;
httpError.payload = payload;
throw httpError;
}
if (payload && payload.status === false) {
var apiError = new Error(
payload.message || payload.errorMsg || 'api_request_failed'
);
apiError.payload = payload;
throw apiError;
}
if (
payload &&
Object.prototype.hasOwnProperty.call(payload, 'body')
)
return payload.body;
if (
payload &&
Object.prototype.hasOwnProperty.call(payload, 'data')
)
return payload.data;
return payload;
});
}
function request(path, options) {
var opts = options || {};
var body = opts.body;
if (body && typeof body === 'object' && !(body instanceof FormData)) {
body = JSON.stringify(body);
}
return connectToApp().then(function () {
var requestHeaders = Object.assign({}, headers, opts.headers || {});
if (!requestHeaders.authorization)
delete requestHeaders.authorization;
if (body)
requestHeaders['content-type'] =
requestHeaders['content-type'] || 'application/json';
return window
.fetch(buildURL(path, opts.query), {
method: opts.method || 'GET',
headers: requestHeaders,
body: body,
credentials: 'omit',
cache: 'no-store',
})
.then(parseResponse);
});
}
function readNumber(value, fallback) {
var number = Number(value);
return Number.isFinite(number) ? number : fallback || 0;
}
function relationType(tabOrType) {
var key = String(tabOrType || 'cp')
.trim()
.toLowerCase();
return (
RELATION_TYPE_BY_TAB[key] || String(tabOrType || 'CP').toUpperCase()
);
}
function userAvatar(profile) {
profile = profile || {};
return (
profile.userAvatar ||
profile.avatar ||
profile.headImg ||
profile.headImage ||
''
);
}
function userId(profile) {
profile = profile || {};
return profile.id || profile.userId || profile.uid || '';
}
function choosePair(list, type) {
var rows = Array.isArray(list) ? list : list ? [list] : [];
for (var i = 0; i < rows.length; i += 1) {
if (relationType(rows[i].relationType) === type) return rows[i];
}
return rows[0] || null;
}
function sortedLevels(levels) {
return (Array.isArray(levels) ? levels : [])
.slice()
.sort(function (a, b) {
return readNumber(a.level, 0) - readNumber(b.level, 0);
});
}
function levelProgress(rights) {
var intimacy = readNumber(rights && rights.intimacyValue, 0);
var levels = sortedLevels(rights && rights.levels);
var current = levels[0] || { level: 0, requiredIntimacyValue: 0 };
var next = levels[1] || current;
levels.forEach(function (level, index) {
if (readNumber(level.requiredIntimacyValue, 0) <= intimacy) {
current = level;
next = levels[index + 1] || level;
}
});
var currentThreshold = readNumber(current.requiredIntimacyValue, 0);
var nextThreshold = Math.max(
readNumber(next.requiredIntimacyValue, currentThreshold),
currentThreshold
);
var currentExp = Math.max(0, intimacy - currentThreshold);
var nextExp = Math.max(0, nextThreshold - currentThreshold);
var progressPercent = nextExp ? (currentExp / nextExp) * 100 : 100;
return {
currentLevel: readNumber(current.level, 0),
nextLevel: readNumber(next.level, current.level),
currentExp: currentExp,
nextExp: nextExp || intimacy,
neededExp: Math.max(0, nextThreshold - intimacy),
nextExpLevel: readNumber(next.level, current.level),
progressPercent: Math.max(0, Math.min(100, progressPercent)),
};
}
function normalizeRelation(pair, rights) {
pair = pair || {};
rights = rights || {};
var progress = levelProgress(rights);
var me = pair.meUserProfile || pair.me || {};
var partner = pair.cpUserProfile || pair.partner || {};
return Object.assign(progress, {
relationType: relationType(
pair.relationType || rights.relationType
),
status: rights.relationshipStatus || pair.status || 'NONE',
cpUserId: rights.cpUserId || userId(partner),
days: readNumber(pair.days, 0),
togetherDays: readNumber(pair.days, 0),
me: { avatar: userAvatar(me), id: userId(me) },
partner: { avatar: userAvatar(partner), id: userId(partner) },
raw: { pair: pair, rights: rights },
});
}
function pair(options) {
var opts = options || {};
return request('/user/cp-relationship/pair', {
query: {
relationType: relationType(opts.relationType || opts.tab),
userId: opts.userId,
},
});
}
function rights(options) {
var opts = options || {};
return request('/user/cp/rights/config', {
query: {
relationType: relationType(opts.relationType || opts.tab),
cpUserId: opts.cpUserId,
},
});
}
function loadRelation(options) {
var opts = options || {};
var type = relationType(opts.relationType || opts.tab);
var selectedPair = null;
var firstError = null;
return pair({ relationType: type, userId: opts.userId })
.then(function (pairList) {
selectedPair = choosePair(pairList, type);
var cpUserId =
opts.cpUserId ||
(selectedPair && userId(selectedPair.cpUserProfile)) ||
'';
return rights({ relationType: type, cpUserId: cpUserId });
})
.catch(function (error) {
firstError = error;
return rights({ relationType: type, cpUserId: opts.cpUserId });
})
.then(function (rightsData) {
return normalizeRelation(selectedPair, rightsData);
})
.catch(function (error) {
throw firstError || error;
});
}
function back() {
if (
window.FlutterPageControl &&
window.FlutterPageControl.postMessage
) {
window.FlutterPageControl.postMessage('close_page');
return;
}
if (window.HyAppBridge && window.HyAppBridge.back) {
window.HyAppBridge.back();
return;
}
window.history.back();
}
window.YumiCommon = {
baseURL: baseURL,
buildURL: buildURL,
headers: function () {
return Object.assign({}, headers);
},
connectToApp: connectToApp,
setHeadersFromApp: setHeadersFromApp,
request: request,
get: function (path, queryData) {
return request(path, { method: 'GET', query: queryData });
},
post: function (path, body, queryData) {
return request(path, {
method: 'POST',
body: body,
query: queryData,
});
},
back: back,
cpSpace: {
relationType: relationType,
pair: pair,
rights: rights,
loadRelation: loadRelation,
normalizeRelation: normalizeRelation,
},
};
})();