update
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 10m49s

This commit is contained in:
2026-09-03 09:28:28 +08:00
parent dbfeeee253
commit 370fd3e67f
150 changed files with 64402 additions and 247 deletions

View File

@@ -142,6 +142,16 @@ class AgentMapping:
no_downtime: bool = False # 迁移时免停机:有生效套餐的卡直接以 network_status=1 导入
@dataclass(frozen=True)
class PackageLifecycleOverride:
"""单卡旧套餐生命周期的人工裁决。"""
iccid: str
active_life_id: str
pending_life_ids: tuple[str, ...] = ()
skipped_life_ids: tuple[str, ...] = ()
@dataclass
class Mapping:
"""整体映射(对应单个 mapping.yaml)。"""
@@ -155,6 +165,7 @@ class Mapping:
packages: dict[str, PackageMapping] = field(default_factory=dict)
series: dict[str, SeriesMapping] = field(default_factory=dict)
agents: dict[str, AgentMapping] = field(default_factory=dict)
package_lifecycle_overrides: dict[str, PackageLifecycleOverride] = field(default_factory=dict)
def lookup_carrier(self, legacy_account_id: str) -> Optional[CarrierMapping]:
return self.carriers.get(str(legacy_account_id or "").strip())
@@ -167,6 +178,9 @@ class Mapping:
return None
return self.series.get(str(legacy_series_id).strip())
def lookup_package_lifecycle_override(self, iccid: str) -> Optional[PackageLifecycleOverride]:
return self.package_lifecycle_overrides.get(str(iccid or "").strip())
def is_no_downtime_agent(self, legacy_agent_id: str) -> bool:
"""判断代理是否配置了免停机迁移。"""
agent = self.agents.get(str(legacy_agent_id or "").strip())
@@ -261,6 +275,8 @@ def load_mapping(config_dir: Path) -> Mapping:
)
m.series[s.legacy_series_id] = s
m.package_lifecycle_overrides = _load_package_lifecycle_overrides(raw.get("package_lifecycle_overrides") or [])
for item in raw.get("agents") or []:
a = AgentMapping(
legacy_agent_id=str(item["legacy_agent_id"]).strip(),
@@ -323,6 +339,39 @@ def _load_package_rules(raw: dict) -> PackageRules:
return PackageRules(migrate_statuses=statuses)
def _load_package_lifecycle_overrides(raw: Any) -> dict[str, PackageLifecycleOverride]:
if not isinstance(raw, list):
raise ValueError("mapping.yaml.package_lifecycle_overrides 必须是列表")
overrides: dict[str, PackageLifecycleOverride] = {}
for idx, item in enumerate(raw, start=1):
if not isinstance(item, dict):
raise ValueError(f"mapping.yaml.package_lifecycle_overrides[{idx}] 必须是 mapping 类型")
iccid = _opt_str(item.get("iccid"))
active = _opt_str(item.get("active_life_id"))
if not iccid or len(iccid) != 20 or not iccid.isdigit():
raise ValueError(f"mapping.yaml.package_lifecycle_overrides[{idx}].iccid 必须是 20 位数字")
if not active:
raise ValueError(f"mapping.yaml.package_lifecycle_overrides[{idx}].active_life_id 不能为空")
pending = _life_ids(item.get("pending_life_ids") or [], f"package_lifecycle_overrides[{idx}].pending_life_ids")
skipped = _life_ids(item.get("skipped_life_ids") or [], f"package_lifecycle_overrides[{idx}].skipped_life_ids")
all_ids = (active,) + pending + skipped
if len(all_ids) != len(set(all_ids)):
raise ValueError(f"mapping.yaml.package_lifecycle_overrides[{idx}] 生命周期 ID 不可重复或跨状态重复")
if iccid in overrides:
raise ValueError(f"mapping.yaml.package_lifecycle_overrides iccid 重复: {iccid}")
overrides[iccid] = PackageLifecycleOverride(iccid, active, pending, skipped)
return overrides
def _life_ids(raw: Any, field_name: str) -> tuple[str, ...]:
if not isinstance(raw, list):
raise ValueError(f"mapping.yaml.{field_name} 必须是列表")
values = tuple(_opt_str(value) or "" for value in raw)
if not all(values):
raise ValueError(f"mapping.yaml.{field_name} 不可包含空生命周期 ID")
return values
def _load_overrides(raw: dict) -> Overrides:
"""加载资产覆盖项。"""
if not isinstance(raw, dict):
@@ -420,6 +469,23 @@ def _render_commented_mapping_yaml(mapping: Mapping) -> str:
for status in mapping.package_rules.migrate_statuses:
lines.append(f" - {_yaml_scalar(status)}")
lines.extend([
"",
"# ============== 套餐生命周期特殊裁决 ==============",
"# life_id 来自 tbl_card_life.id每项必须明确裁决该卡所有可迁移正式套餐。",
])
if not mapping.package_lifecycle_overrides:
lines.append("package_lifecycle_overrides: []")
else:
lines.append("package_lifecycle_overrides:")
for item in sorted(mapping.package_lifecycle_overrides.values(), key=lambda x: x.iccid):
lines.extend([
f" - iccid: {_yaml_scalar(item.iccid)}",
f" active_life_id: {_yaml_scalar(item.active_life_id)}",
f" pending_life_ids: {json.dumps(list(item.pending_life_ids), ensure_ascii=False)}",
f" skipped_life_ids: {json.dumps(list(item.skipped_life_ids), ensure_ascii=False)}",
])
lines.extend([
"",
"# ============== 少量资产级覆盖 ==============",