fix(growth): normalize nested marketplace packs
Some checks failed
CD Pipeline / deploy (push) Has been cancelled

This commit is contained in:
ogt
2026-07-15 10:01:55 +08:00
parent 05c7cd8421
commit 954c729da2
4 changed files with 115 additions and 14 deletions

View File

@@ -858,6 +858,8 @@ PRODUCT_TYPES = {
COUNT_UNITS = {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "刀把", "刀片", "刀頭", ""}
COUNT_UNIT_PATTERN = r"(?:刀把|刀片|刀頭|入|組|瓶|支|條|盒|包|袋|片|張|顆|粒|錠|枚|件|罐|杯|本|蕊)"
PIECE_UNITS = {"", "", "", "", "", "", "", ""}
ATOMIC_PIECE_UNITS = {"", "", "", "", "", ""}
PACK_MULTIPLIER_UNITS = {"", ""}
CONTAINER_UNITS = {"", "", "", "", "", "", "", "", "", ""}
COUNT_UNIT_FAMILIES = {
"刀片": "blade",
@@ -1210,14 +1212,32 @@ def _extract_specs(
counts.append((2, ""))
total_piece_count = None
explicit_total = re.search(r"\s*(\d+)\s*([包袋片顆粒錠枚])", text)
explicit_total = re.search(r"\s*(\d+)\s*([包袋片顆粒錠枚])", text)
if explicit_total:
total_piece_count = int(explicit_total.group(1))
else:
atomic_piece_counts = [
count for count, unit in counts if unit in ATOMIC_PIECE_UNITS
]
piece_counts = [count for count, unit in counts if unit in PIECE_UNITS]
container_counts = [count for count, unit in counts if unit in CONTAINER_UNITS]
if piece_counts and container_counts:
total_piece_count = max(piece_counts) * max(container_counts)
container_multiplier_by_unit = {
unit: max(count for count, candidate_unit in counts if candidate_unit == unit)
for unit in CONTAINER_UNITS
if any(candidate_unit == unit for _, candidate_unit in counts)
}
multiplier_by_unit = {
unit: max(count for count, candidate_unit in counts if candidate_unit == unit)
for unit in PACK_MULTIPLIER_UNITS | CONTAINER_UNITS
if any(candidate_unit == unit for _, candidate_unit in counts)
}
if atomic_piece_counts and multiplier_by_unit:
total_piece_count = max(atomic_piece_counts)
for multiplier in multiplier_by_unit.values():
total_piece_count *= multiplier
elif piece_counts and container_multiplier_by_unit:
total_piece_count = max(piece_counts)
for multiplier in container_multiplier_by_unit.values():
total_piece_count *= multiplier
elif piece_counts:
total_piece_count = max(piece_counts)
@@ -1782,6 +1802,17 @@ def _has_overlapping_base_spec(left: ProductIdentity, right: ProductIdentity) ->
return False
return _close_number(left_weights[0], right_weights[0])
if left.total_piece_count or right.total_piece_count:
if not left.total_piece_count or not right.total_piece_count:
return False
left_units = {
unit for _, unit in left.counts if unit in ATOMIC_PIECE_UNITS
}
right_units = {
unit for _, unit in right.counts if unit in ATOMIC_PIECE_UNITS
}
return bool(left_units & right_units)
return False
@@ -1798,7 +1829,11 @@ def _single_unit_total(identity: ProductIdentity) -> tuple[Optional[str], Option
multiplier = identity.total_piece_count or _pack_multiplier(identity)
return "g", weights[0] * multiplier, "ok"
if identity.total_piece_count:
return "", float(identity.total_piece_count), "ok"
atomic_units = {
unit for _, unit in identity.counts if unit in ATOMIC_PIECE_UNITS
}
unit = next(iter(atomic_units)) if len(atomic_units) == 1 else ""
return unit, float(identity.total_piece_count), "ok"
return None, None, "missing_single_unit"