迁移脚本的一些修复
This commit is contained in:
@@ -54,7 +54,7 @@ class DeviceRow:
|
||||
line_no: int
|
||||
imei: str
|
||||
virtual_no: str # 留空时用 imei 兜底,见 _load_devices
|
||||
sim_iccids: dict[int, str] # slot → iccid(只保留非空)
|
||||
sim_iccids: dict[int, str] # slot → 完整 ICCID(只保留非空)
|
||||
device_name: str = ""
|
||||
device_model: str = ""
|
||||
device_type: str = ""
|
||||
@@ -159,7 +159,7 @@ def _load_cards(path: Path, result: LoadResult) -> None:
|
||||
if "iccid_20" not in header:
|
||||
raise ValueError(f"{path} 必须包含 iccid_20 列(20 位运营商卡填写,CTCC 留空)")
|
||||
|
||||
seen_iccid_19: dict[str, int] = {}
|
||||
seen_iccid_full: dict[str, int] = {}
|
||||
for idx, raw in enumerate(reader, start=2):
|
||||
iccid_19 = _normalize_cell(raw.get("iccid_19", ""))
|
||||
iccid_20 = _normalize_cell(raw.get("iccid_20", ""))
|
||||
@@ -184,13 +184,14 @@ def _load_cards(path: Path, result: LoadResult) -> None:
|
||||
result.errors.append(ErrorRow("cards.csv", idx, "iccid_20", iccid_20, err.code, err.message))
|
||||
continue
|
||||
|
||||
if iccid_19 in seen_iccid_19:
|
||||
iccid_full = iccid_20 or iccid_19
|
||||
if iccid_full in seen_iccid_full:
|
||||
result.errors.append(
|
||||
ErrorRow("cards.csv", idx, "iccid_19", iccid_19, "duplicate",
|
||||
f"与第 {seen_iccid_19[iccid_19]} 行 iccid_19 重复")
|
||||
ErrorRow("cards.csv", idx, "iccid", iccid_full, "duplicate",
|
||||
f"与第 {seen_iccid_full[iccid_full]} 行 ICCID 重复")
|
||||
)
|
||||
continue
|
||||
seen_iccid_19[iccid_19] = idx
|
||||
seen_iccid_full[iccid_full] = idx
|
||||
|
||||
is_industry = _parse_bool(raw.get("is_industry", ""))
|
||||
if is_industry is None:
|
||||
@@ -292,27 +293,59 @@ def _load_devices(path: Path, result: LoadResult) -> None:
|
||||
def _cross_check(result: LoadResult) -> None:
|
||||
"""校验设备与卡之间的一致性,并把 bound_device_virtual_no 回填到 CardRow。
|
||||
|
||||
devices.csv 里 sim_iccid_X 业务方可能填 19 位也可能填 20 位,
|
||||
统一截取前 19 位回查 cards.iccid_19。
|
||||
devices.csv 里 sim_iccid_X 业务方可能填 19 位也可能填 20 位。
|
||||
20 位按完整 ICCID 精确匹配;19 位只有在 cards.csv 中唯一对应一张卡时才允许。
|
||||
"""
|
||||
from . import iccid_utils
|
||||
|
||||
cards_by_iccid_19: dict[str, CardRow] = {c.iccid_19: c for c in result.cards}
|
||||
cards_by_full: dict[str, CardRow] = {c.iccid_full: c for c in result.cards}
|
||||
cards_by_i19: dict[str, list[CardRow]] = {}
|
||||
for card in result.cards:
|
||||
cards_by_i19.setdefault(card.iccid_19, []).append(card)
|
||||
device_card_refs: dict[str, tuple[str, int]] = {}
|
||||
|
||||
for device in result.devices:
|
||||
for slot, raw_iccid in device.sim_iccids.items():
|
||||
key19 = iccid_utils.prefix_19(raw_iccid)
|
||||
if key19 not in cards_by_iccid_19:
|
||||
resolved_card: Optional[CardRow] = None
|
||||
if len(raw_iccid) == 20:
|
||||
resolved_card = cards_by_full.get(raw_iccid)
|
||||
if resolved_card is None:
|
||||
prefix = iccid_utils.prefix_19(raw_iccid)
|
||||
candidates = cards_by_i19.get(prefix, [])
|
||||
if len(candidates) == 1 and not candidates[0].iccid_20:
|
||||
resolved_card = candidates[0]
|
||||
elif len(raw_iccid) == 19:
|
||||
candidates = cards_by_i19.get(raw_iccid, [])
|
||||
if len(candidates) > 1:
|
||||
result.errors.append(
|
||||
ErrorRow(
|
||||
"devices.csv", device.line_no, f"sim_iccid_{slot}", raw_iccid,
|
||||
"ambiguous_iccid_19", "该 19 位前缀对应多张 20 位卡,请填写完整 20 位 ICCID",
|
||||
)
|
||||
)
|
||||
continue
|
||||
if candidates:
|
||||
resolved_card = candidates[0]
|
||||
else:
|
||||
result.errors.append(
|
||||
ErrorRow(
|
||||
"devices.csv", device.line_no, f"sim_iccid_{slot}", raw_iccid,
|
||||
"card_not_found", "引用的 ICCID(前 19 位)在 cards.csv 中不存在",
|
||||
"sim_iccid_length", "设备槽位 ICCID 必须是 19 或 20 位",
|
||||
)
|
||||
)
|
||||
continue
|
||||
if key19 in device_card_refs:
|
||||
prev_vno, prev_line = device_card_refs[key19]
|
||||
|
||||
if resolved_card is None:
|
||||
result.errors.append(
|
||||
ErrorRow(
|
||||
"devices.csv", device.line_no, f"sim_iccid_{slot}", raw_iccid,
|
||||
"card_not_found", "引用的 ICCID 在 cards.csv 中不存在",
|
||||
)
|
||||
)
|
||||
continue
|
||||
card_key = resolved_card.iccid_full
|
||||
if card_key in device_card_refs:
|
||||
prev_vno, prev_line = device_card_refs[card_key]
|
||||
result.errors.append(
|
||||
ErrorRow(
|
||||
"devices.csv", device.line_no, f"sim_iccid_{slot}", raw_iccid,
|
||||
@@ -320,7 +353,7 @@ def _cross_check(result: LoadResult) -> None:
|
||||
)
|
||||
)
|
||||
continue
|
||||
device_card_refs[key19] = (device.virtual_no, device.line_no)
|
||||
cards_by_iccid_19[key19].bound_device_virtual_no = device.virtual_no
|
||||
# 标准化:slot 里的值统一改写为 iccid_19,sql_builder 直接用它查 cards / card_metas
|
||||
device.sim_iccids[slot] = key19
|
||||
device_card_refs[card_key] = (device.virtual_no, device.line_no)
|
||||
resolved_card.bound_device_virtual_no = device.virtual_no
|
||||
# 标准化:slot 里的值统一改写为完整 ICCID,sql_builder 直接用它查 cards / card_metas
|
||||
device.sim_iccids[slot] = card_key
|
||||
|
||||
Reference in New Issue
Block a user