feat: 增加环境切换

This commit is contained in:
R524809
2026-10-19 17:49:19 +08:00
parent 997042789d
commit fe1074e3f0
21 changed files with 968 additions and 123 deletions
+112 -27
View File
@@ -1,37 +1,122 @@
export type ApiEnvironment = 'local' | 'online';
export type ApiEnvironment = 'local' | 'staging' | 'production';
type MiniProgramVersion = 'develop' | 'trial' | 'release';
/** 统一接口入口;本地真机调试时将 localBaseUrl 的 IP 改为电脑局域网 IP。 */
const LOCAL_BASE_URL = 'http://127.0.0.1:8800/api/v1';
const STORAGE_KEY = 'doodle_develop_api_environment_v1';
let selectedEnvironment: ApiEnvironment | undefined;
let revision = 0;
/** 暂时共用本地地址;部署后分别替换 staging/production,真机 local 使用电脑局域网 IP。 */
export const apiConfig = {
developmentEnvironment: 'local' as ApiEnvironment,
localBaseUrl: 'http://127.0.0.1:8800/api/v1',
// 线上域名尚未确定:填写完整 HTTPS 地址,例如 https://你的域名/api/v1。
onlineBaseUrl: '',
localBaseUrl: LOCAL_BASE_URL,
stagingBaseUrl: LOCAL_BASE_URL,
productionBaseUrl: LOCAL_BASE_URL,
timeout: 10000,
};
/** develop 可切换本地/线上;体验版、正式版和未知运行环境均只允许线上入口。 */
export function resolveApiConfig(version: MiniProgramVersion | string) {
const environment: ApiEnvironment =
version === 'develop' ? apiConfig.developmentEnvironment : 'online';
const baseUrl = (
environment === 'local'
? apiConfig.localBaseUrl
: apiConfig.onlineBaseUrl
)
.trim()
.replace(/\/+$/, '');
if (!baseUrl) throw new Error('请先配置线上接口地址');
if (
!/^https?:\/\/[^/?#\s]+\/api\/v1$/.test(baseUrl) ||
(environment === 'online' && !baseUrl.startsWith('https://'))
) {
throw new Error('接口地址配置不正确');
}
return { environment, baseUrl, timeout: apiConfig.timeout };
export const API_ENVIRONMENTS: ApiEnvironment[] = [
'local',
'staging',
'production',
];
export function isApiEnvironment(value: unknown): value is ApiEnvironment {
return API_ENVIRONMENTS.includes(value as ApiEnvironment);
}
/** 请求时读取微信包版本,避免依赖 App 启动顺序,也不接受本地缓存覆盖正式版环境。 */
export function getApiConfig() {
return resolveApiConfig(wx.getAccountInfoSync().miniProgram.envVersion);
/** 仅开发包读取本机选择;体验、正式及未知包始终连接 production。 */
export function getApiEnvironment(
version: string = wx.getAccountInfoSync().miniProgram.envVersion,
): ApiEnvironment {
if (version === 'develop' && selectedEnvironment === undefined) {
try {
const stored: unknown = wx.getStorageSync(STORAGE_KEY);
if (isApiEnvironment(stored)) selectedEnvironment = stored;
} catch {
/* 无缓存时使用开发默认值。 */
}
}
return version === 'develop'
? (selectedEnvironment ?? apiConfig.developmentEnvironment)
: 'production';
}
export function resolveApiConfig(version: MiniProgramVersion | string) {
return resolveEnvironment(getApiEnvironment(version), version);
}
function resolveEnvironment(environment: ApiEnvironment, version: string) {
const baseUrl = apiConfig[`${environment}BaseUrl`]
.trim()
.replace(/\/+$/, '');
if (!baseUrl) throw new Error(`请先配置 ${environment} 接口地址`);
// 用户指定的本机占位地址暂允许 HTTP;实际云端入口仍须使用 HTTPS。
if (
!/^https?:\/\/[^/?#\s]+\/api\/v1$/.test(baseUrl) ||
(environment !== 'local' &&
!baseUrl.startsWith('https://') &&
baseUrl !== LOCAL_BASE_URL)
)
throw new Error('接口地址配置不正确');
return {
environment,
baseUrl,
timeout: apiConfig.timeout,
revision,
version,
};
}
export function getApiConfig() {
const { appId = '', envVersion } = wx.getAccountInfoSync().miniProgram;
return { ...resolveApiConfig(envVersion), appId };
}
/** 即使三个地址相同也按环境隔离;develop 不与体验/正式包共享业务缓存。 */
export function apiContextKey(config = getApiConfig()): string {
const scope = config.version === 'develop' ? 'develop' : 'public';
return [config.appId, scope, config.environment, config.baseUrl]
.map(encodeURIComponent)
.join(':');
}
/** 版本号阻止 A→B→A 后旧 A 请求覆盖当前页面。 */
export function isCurrentApiConfig(config: ReturnType<typeof getApiConfig>) {
const current = getApiConfig();
return (
config.revision === current.revision &&
apiContextKey(config) === apiContextKey(current)
);
}
/** 写入成功后才提交内存选择;配置失败保留原环境,不自动回退。 */
export function selectApiEnvironment(environment: ApiEnvironment): boolean {
if (wx.getAccountInfoSync().miniProgram.envVersion !== 'develop')
throw new Error('仅开发版可切换环境');
if (!isApiEnvironment(environment)) throw new Error('未知接口环境');
resolveEnvironment(environment, 'develop');
if (getApiEnvironment() === environment) return false;
wx.setStorageSync(STORAGE_KEY, environment);
selectedEnvironment = environment;
revision++;
return true;
}
export function getApiEnvironmentOptions() {
return API_ENVIRONMENTS.map((environment) => {
try {
return {
environment,
baseUrl: resolveEnvironment(environment, 'develop').baseUrl,
available: true,
};
} catch {
return {
environment,
baseUrl: '尚未配置有效地址',
available: false,
};
}
});
}