|
@@ -214,6 +214,11 @@ def dedup_tolerance(text: str) -> int:
|
|
|
def is_sale_ended(data: dict, now_ts: int) -> tuple[bool, str]:
|
|
def is_sale_ended(data: dict, now_ts: int) -> tuple[bool, str]:
|
|
|
"""根据详情返回判断商品售卖是否已结束(售罄或到结束时间)。
|
|
"""根据详情返回判断商品售卖是否已结束(售罄或到结束时间)。
|
|
|
|
|
|
|
|
|
|
+ 预售/未开卖的车:详情接口 availableStock 也返回 0(库存未分配)、soldCount=0、saleStartAt 在未来,
|
|
|
|
|
+ 仅凭 availableStock<=0 会把它误判为「售罄」→ 被加入 _ended_codes 拉黑,导致真正开卖后整车漏采
|
|
|
|
|
+ (2026/08/24 修复 #342 预售车:publishAt 21:26 发布预告、saleStartAt 22:45 才开卖,其间详情
|
|
|
|
|
+ availableStock=0)。故加两道护栏:① saleStartAt 未到直接判未结束;② 售罄须 soldCount>0(确实卖过)。
|
|
|
|
|
+
|
|
|
Args:
|
|
Args:
|
|
|
data (dict): 详情接口 data 层。
|
|
data (dict): 详情接口 data 层。
|
|
|
now_ts (int): 当前秒级时间戳。
|
|
now_ts (int): 当前秒级时间戳。
|
|
@@ -221,9 +226,20 @@ def is_sale_ended(data: dict, now_ts: int) -> tuple[bool, str]:
|
|
|
Returns:
|
|
Returns:
|
|
|
tuple[bool, str]: (是否已结束, 原因文本)。未结束时原因为空串。
|
|
tuple[bool, str]: (是否已结束, 原因文本)。未结束时原因为空串。
|
|
|
"""
|
|
"""
|
|
|
|
|
+ # ① 开卖时间未到 → 预售态,绝不算结束(预售车 availableStock=0 是「未分配」而非「卖光」)
|
|
|
|
|
+ start_text = data.get("saleStartAt")
|
|
|
|
|
+ if start_text:
|
|
|
|
|
+ try:
|
|
|
|
|
+ start_ts = time.mktime(time.strptime(start_text, "%Y-%m-%d %H:%M:%S"))
|
|
|
|
|
+ if now_ts < start_ts:
|
|
|
|
|
+ return False, ""
|
|
|
|
|
+ except (ValueError, OverflowError):
|
|
|
|
|
+ pass
|
|
|
|
|
+ # ② 售罄:库存<=0 且确实卖出过(soldCount>0),避免预售/未开卖车(soldCount=0)被误判售罄
|
|
|
stock = data.get("availableStock")
|
|
stock = data.get("availableStock")
|
|
|
- if stock is not None and stock <= 0:
|
|
|
|
|
- return True, f"售罄(availableStock={stock})"
|
|
|
|
|
|
|
+ sold = data.get("soldCount")
|
|
|
|
|
+ if stock is not None and stock <= 0 and sold is not None and sold > 0:
|
|
|
|
|
+ return True, f"售罄(availableStock={stock}, soldCount={sold})"
|
|
|
end_text = data.get("saleEndAt")
|
|
end_text = data.get("saleEndAt")
|
|
|
if end_text:
|
|
if end_text:
|
|
|
try:
|
|
try:
|