增加环境配置

This commit is contained in:
chen wei bo
2025-08-15 14:30:09 +08:00
parent f928e7fabe
commit e64327bf09
15 changed files with 269 additions and 16 deletions
+9
View File
@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "78089db8-0ee4-4f29-9a3b-8ee927360d57",
"files": [],
"subMetas": {},
"userData": {}
}
+9
View File
@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "d82373f7-a30d-43a3-84b1-e4d7e37c8e09",
"files": [],
"subMetas": {},
"userData": {}
}
+126
View File
@@ -0,0 +1,126 @@
/**
* 整个APP的环境配置逻辑
*/
import { sys } from "cc";
import { DEBUG, EDITOR, PREVIEW } from "cc/env";
import { Env, AppConfigShape } from "./env";
import { devConfig } from "./dev";
import { testConfig } from "./test";
import { prodConfig } from "./prod";
type PartialEndpoints = Partial<AppConfigShape["endpoints"]>;
const LS_ENV_KEY = "app_env";
const LS_OVERRIDE_KEY = "app_endpoints_override"; // 存JSON字符串
export class AppConfig {
private static _I: AppConfig | null = null;
static get I(): AppConfig {
if (!AppConfig._I) AppConfig._I = new AppConfig();
return AppConfig._I;
}
private _cfg!: AppConfigShape;
private constructor() {
const env = this.detectEnv();
this._cfg = this.byEnv(env);
// 运行时覆盖:query > localStorage
// this.applyQueryOverride();
// this.applyLocalOverride();
}
/** 外部读取用 */
get env() { return this._cfg.env; }
get endpoints() { return this._cfg.endpoints; }
/** 切换环境(开发工具用) */
public switchEnv(env: Env) {
this._cfg = this.byEnv(env);
sys.localStorage.setItem(LS_ENV_KEY, env);
}
/** 运行时局部覆盖 endpoints(紧急切换) */
public overrideEndpoints(patch: PartialEndpoints) {
this._cfg = {
...this._cfg,
endpoints: { ...this._cfg.endpoints, ...patch, cdn: { ...this._cfg.endpoints.cdn, ...(patch.cdn || {}) } }
};
sys.localStorage.setItem(LS_OVERRIDE_KEY, JSON.stringify(patch));
}
/** 清除覆盖,回到纯环境配置 */
public clearOverrides() {
sys.localStorage.removeItem(LS_OVERRIDE_KEY);
this._cfg = this.byEnv(this._cfg.env);
}
/** 获取 HTTP 基础地址
* @param stripTrailingSlash 是否去掉末尾斜杠,默认 true
*/
public getHttpBase(stripTrailingSlash: boolean = true): string {
const raw = this._cfg.endpoints.httpBase || "";
if (stripTrailingSlash) {
return raw.replace(/\/+$/, "");
}
// 保证有一个结尾斜杠
return raw.endsWith("/") ? raw : raw + "/";
}
// ------------ 内部 ------------
private byEnv(env: Env): AppConfigShape {
switch (env) {
case Env.Dev: return devConfig;
case Env.Test: return testConfig;
default: return prodConfig;
}
}
private isEnv(x: any): x is Env {
return x === Env.Dev || x === Env.Test || x === Env.Prod;
}
/** 默认环境:优先 localStorage,再看 EDITOR/DEBUG,最后 prod */
private detectEnv(): Env {
const saved = sys.localStorage.getItem(LS_ENV_KEY) as Env | null;
if (saved && this.isEnv(saved)) {
return saved;
}
// 编辑器/预览时默认 dev
if (EDITOR || (PREVIEW && DEBUG)) return Env.Dev;
// 调试构建默认 test
if (DEBUG) return Env.Test;
// 发行构建默认 prod
return Env.Prod;
}
private applyLocalOverride() {
const raw = sys.localStorage.getItem(LS_OVERRIDE_KEY);
if (raw) {
try {
this.overrideEndpoints(JSON.parse(raw));
} catch { /* ignore */ }
}
}
/** 支持浏览器 query 覆盖:?env=test&http=...&socket=...&cdn_bgm=... */
private applyQueryOverride() {
if (!sys.isBrowser) return;
const qs = new URLSearchParams(window.location.search);
const qEnv = qs.get("env") as Env | null;
if (qEnv && this.isEnv(qEnv)) {
this._cfg = this.byEnv(qEnv);
sys.localStorage.setItem(LS_ENV_KEY, qEnv);
}
const patch: PartialEndpoints = {};
if (qs.get("http")) (patch.httpBase = qs.get("http")!);
if (qs.get("socket")) (patch.socketBase = qs.get("socket")!);
const cdnPatch: any = {};
if (qs.get("cdn_bgm")) cdnPatch.bgm = qs.get("cdn_bgm");
if (qs.get("cdn_zhen")) cdnPatch.zhenCang = qs.get("cdn_zhen");
if (qs.get("cdn_bgvideo")) cdnPatch.bgVideo = qs.get("cdn_bgvideo");
if (Object.keys(cdnPatch).length) (patch.cdn = cdnPatch);
if (Object.keys(patch).length) this.overrideEndpoints(patch);
}
}
+9
View File
@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "56e9ec0c-c2ef-4ace-89f7-73829135c361",
"files": [],
"subMetas": {},
"userData": {}
}
+14
View File
@@ -0,0 +1,14 @@
/**
* 开发环境配置
*/
import { AppConfigShape, Env } from "./env";
export const devConfig: AppConfigShape = {
env: Env.Dev,
endpoints: {
httpBase: "http://43.139.27.67:8081/",
socketBase: "ws://127.0.0.1:9001/ws/",
cdn: {
bgm: "http://127.0.0.1:8081/BGM/",
},
},
};
+9
View File
@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "c1151830-fdb3-40f2-a317-9a5f37aed7d8",
"files": [],
"subMetas": {},
"userData": {}
}
+25
View File
@@ -0,0 +1,25 @@
/**
* 环境的类型,接口定义
*/
export enum Env {
Dev = "dev",
Test = "test",
Prod = "prod",
}
export interface CDNConfig {
bgm: string;
}
export interface Endpoints {
httpBase: string; // 业务 HTTP
socketBase: string; // WebSocket
cdn: CDNConfig; // 各类静态资源
}
export interface AppConfigShape {
env: Env;
endpoints: Endpoints;
// 其他:开关、埋点、灰度、版本号……
}
+9
View File
@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "4d45e3c5-2567-4426-a736-0329df3f0549",
"files": [],
"subMetas": {},
"userData": {}
}
+14
View File
@@ -0,0 +1,14 @@
/**
* 生产环境配置
*/
import { AppConfigShape, Env } from "./env";
export const prodConfig: AppConfigShape = {
env: Env.Prod,
endpoints: {
httpBase: "https://xqmnyx.vip.hnhxzkj.com/api/",
socketBase: "wss://www.confessioncontract.com/game/ai/response/",
cdn: {
bgm: "https://hxzgame.vip.hnhxzkj.com/lzx/XiangQin/BGM/",
},
},
};
+9
View File
@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "80fc473d-912a-4b60-824f-f6de8db5e4c3",
"files": [],
"subMetas": {},
"userData": {}
}
+14
View File
@@ -0,0 +1,14 @@
/**
* 测试环境配置
*/
import { AppConfigShape, Env } from "./env";
export const testConfig: AppConfigShape = {
env: Env.Test,
endpoints: {
httpBase: "http://43.139.27.67:8081/",
socketBase: "wss://www.confessioncontract.com/test/game/ai/response/",
cdn: {
bgm: "https://test-cdn.example.com/BGM/",
},
},
};
+9
View File
@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "9cbbaeca-8a67-49c7-8c2c-75afedc8d8c0",
"files": [],
"subMetas": {},
"userData": {}
}
@@ -1,4 +1,3 @@
import { HttpManager, HttpMethod } from "../transport/HttpManager";
import { JsonCodec } from "../codec/JsonCodec";
import { ProtoCodec } from "../codec/ProtoCodec";
@@ -6,6 +5,7 @@ import type { Endpoint } from "./endpoints";
import { ApiCode, type ApiResponse } from "./types";
import { DataManager, DataId } from "db://assets/Scripts/chat18x/data/DataManager";
import { LoginData } from "db://assets/Scripts/chat18x/data/LoginData";
import { AppConfig } from "db://assets/Scripts/chat18x/config/env/appConfig";
export class ApiClient {
private static _I: ApiClient | null = null;
@@ -14,8 +14,7 @@ export class ApiClient {
return ApiClient._I;
}
private constructor(private http = HttpManager.I) {}
// TODO
private get baseUrl() { return "http://43.139.27.67:8081" }
private get baseUrl() { return AppConfig.I.getHttpBase() }
private get token() { return DataManager.I.getDataById<LoginData>(DataId.Login).getToken();}
public async call<Req, Res>(ep: Endpoint<Req, Res>, req: Req): Promise<ApiResponse<Res>> {
@@ -6,9 +6,8 @@ export type CodecKind = "json" | "proto";
export interface Endpoint<Req, Res> {
path: string;
method: HttpMethod;
codec: CodecKind;
reqType?: Type; // codec=proto 时必填
resType?: Type; // codec=proto 时必填
needsAuth?: boolean;
lock?: boolean;
codec: CodecKind; // 编解码类型,"json", "proto"
reqType?: Type; // codec=proto 时必填
resType?: Type; // codec=proto 时必填
needsAuth?: boolean; // 是否需要token
}
@@ -11,15 +11,14 @@ export class AuthService {
}
private constructor(private api = ApiClient.I) {}
private EP_LOGIN: Endpoint<proto.ProtoMsg.ILoginReq, proto.ProtoMsg.ILoginRsp> = {
path: "login",
method: "POST",
codec: "json",
needsAuth: false
};
// 登录
public async login(req: proto.ProtoMsg.ILoginReq): Promise<ApiResponse<proto.ProtoMsg.ILoginRsp>> {
return this.api.call(this.EP_LOGIN, req);
let EP_LOGIN: Endpoint<proto.ProtoMsg.ILoginReq, proto.ProtoMsg.ILoginRsp> = {
path: "login",
method: "POST",
codec: "json",
needsAuth: false
};
return this.api.call(EP_LOGIN, req);
}
}