diff --git a/backend/routers/config.py b/backend/routers/config.py index 8ee8562..600eef6 100644 --- a/backend/routers/config.py +++ b/backend/routers/config.py @@ -67,22 +67,26 @@ DEFAULT_SERVERS = { "测试环境": { "api": "https://api3-dev.readoor.cn", "assDom": "applinks:dev-data1.readoor.cn", - "universalLink": "https://dev-data1.readoor.cn" + "universalLink": "https://dev-data1.readoor.cn", + "app_id_prefix": 1, }, "正式环境": { "api": "https://api3.readoor.cn", "assDom": "applinks:data1.readoor.cn", - "universalLink": "https://data1.readoor.cn" + "universalLink": "https://data1.readoor.cn", + "app_id_prefix": 2, }, "华师大环境": { "api": "https://api3.ecnupress.com.cn", "assDom": "applinks:data1.ecnupress.com.cn", - "universalLink": "https://data1.ecnupress.com.cn" + "universalLink": "https://data1.ecnupress.com.cn", + "app_id_prefix": 3, }, "外教环境": { "api": "https://weread-api3.sflep.com/api3", "assDom": "applinks:wereadossda.sflep.com", - "universalLink": "https://wereadossda.sflep.com" + "universalLink": "https://wereadossda.sflep.com", + "app_id_prefix": 4, } } @@ -91,6 +95,84 @@ DEFAULT_VERSIONS = { "build_ver": "2.180.0.0", } +# 为已有环境迁移的固定前缀;后续环境从配置中的 next_app_id_prefix 自动分配。 +LEGACY_SERVER_PREFIXES = { + "测试环境": 1, + "正式环境": 2, + "华师大环境": 3, + "外教环境": 4, +} + + +def _ensure_server_id_prefixes(config: dict) -> bool: + """为旧配置补齐环境 ID 前缀和下一个可分配前缀。""" + changed = False + servers = config.get("servers", {}) + used_prefixes = set() + for name, server in servers.items(): + prefix = server.get("app_id_prefix") + if prefix is None and name in LEGACY_SERVER_PREFIXES: + prefix = LEGACY_SERVER_PREFIXES[name] + server["app_id_prefix"] = prefix + changed = True + if isinstance(prefix, int) and prefix > 0: + used_prefixes.add(prefix) + + next_prefix = max(used_prefixes, default=0) + 1 + for server in servers.values(): + prefix = server.get("app_id_prefix") + if not isinstance(prefix, int) or prefix <= 0: + server["app_id_prefix"] = next_prefix + used_prefixes.add(next_prefix) + next_prefix += 1 + changed = True + if not isinstance(config.get("next_app_id_prefix"), int) or config["next_app_id_prefix"] < next_prefix: + config["next_app_id_prefix"] = next_prefix + changed = True + return changed + + +def _ensure_special_app_prefix_overrides(config: dict) -> bool: + """将英汉大词典的历史 1xx 规则迁移为显式的 App 前缀覆盖。""" + changed = False + for app in config.get("apps", {}).values(): + if "英汉大词典" in app.get("name", "") and not app.get("app_id_prefix_override"): + app["app_id_prefix_override"] = 1 + changed = True + return changed + + +def _apply_special_app_prefix_override(app: dict) -> None: + """英汉大词典沿用历史 1xx 编号段。""" + if "英汉大词典" in app.get("name", "") and not app.get("app_id_prefix_override"): + app["app_id_prefix_override"] = 1 + + +def _next_app_id(apps: dict, servers: dict, app: dict) -> str: + """根据环境前缀或 App 特殊覆盖生成下一个配置 ID。""" + prefix = app.get("app_id_prefix_override") + if prefix in (None, ""): + server = servers.get(app.get("server", ""), {}) + prefix = server.get("app_id_prefix") + try: + prefix = int(prefix) + except (TypeError, ValueError): + prefix = 0 + if prefix <= 0: + raise HTTPException(status_code=400, detail="请选择已配置 Apps ID 规则的服务器环境") + + prefix_text = str(prefix) + serials = [ + int(app_id[len(prefix_text):]) + for app_id in apps + if (app_id.isdigit() and app_id.startswith(prefix_text) + and len(app_id) == len(prefix_text) + 2) + ] + next_serial = max(serials, default=-1) + 1 + if next_serial > 99: + raise HTTPException(status_code=400, detail=f"Apps ID 前缀 {prefix} 的编号已用完") + return f"{prefix}{next_serial:02d}" + def _ensure_upload_keys(config: dict) -> bool: """为没有 upload_key 的 app 自动生成,返回是否有变更""" @@ -152,7 +234,10 @@ def load_config() -> dict: CONFIG_JSON_PATH.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(BOOTSTRAP_CONFIG_PATH, CONFIG_JSON_PATH) else: - return {"apps": {}, "schemes": {}, "servers": DEFAULT_SERVERS, "branches": ["main"], "upload": DEFAULT_UPLOAD} + return { + "apps": {}, "schemes": {}, "servers": DEFAULT_SERVERS, + "next_app_id_prefix": 5, "branches": ["main"], "upload": DEFAULT_UPLOAD, + } with open(CONFIG_JSON_PATH, "r", encoding="utf-8") as f: config = json.load(f) # 确保必要字段存在 @@ -164,8 +249,11 @@ def load_config() -> dict: config["upload"] = DEFAULT_UPLOAD if "versions" not in config: config["versions"] = DEFAULT_VERSIONS.copy() - # 自动为缺少 upload_key 的 app 生成唯一标识 - if _ensure_upload_keys(config): + # 自动补齐旧配置的环境前缀、特殊 App 覆盖和 upload_key。 + prefixes_changed = _ensure_server_id_prefixes(config) + overrides_changed = _ensure_special_app_prefix_overrides(config) + keys_changed = _ensure_upload_keys(config) + if prefixes_changed or overrides_changed or keys_changed: save_config(config) # 迁移旧式目录皮肤为 ZIP if _migrate_old_themes(config): @@ -217,9 +305,9 @@ async def create_app(app: dict): config = load_config() apps = config.get("apps", {}) - # 自动生成 ID - numeric_keys = [int(k) for k in apps.keys() if k.isdigit()] - new_id = str(max(numeric_keys) + 1) if numeric_keys else "1" + # 按服务环境自动生成三位 ID(例如测试环境 1xx、正式环境 2xx)。 + _apply_special_app_prefix_override(app) + new_id = _next_app_id(apps, config.get("servers", {}), app) # 自动生成 upload_key if not app.get("upload_key"): @@ -558,12 +646,15 @@ async def create_server(server_data: dict): if name in servers: raise HTTPException(status_code=400, detail="环境名称已存在") + prefix = config.get("next_app_id_prefix", 1) servers[name] = { "api": server_data.get("api", ""), "assDom": server_data.get("assDom", ""), "universalLink": server_data.get("universalLink", ""), + "app_id_prefix": prefix, } config["servers"] = servers + config["next_app_id_prefix"] = prefix + 1 save_config(config) return {"message": "服务器环境已创建"} @@ -591,6 +682,7 @@ async def update_server(server_name: str, server_data: dict): "api": server_data.get("api", ""), "assDom": server_data.get("assDom", ""), "universalLink": server_data.get("universalLink", ""), + "app_id_prefix": servers[new_name].get("app_id_prefix"), } config["servers"] = servers save_config(config) diff --git a/frontend/src/views/ConfigView.vue b/frontend/src/views/ConfigView.vue index 35052ae..4158bc3 100644 --- a/frontend/src/views/ConfigView.vue +++ b/frontend/src/views/ConfigView.vue @@ -66,6 +66,7 @@