"""Extracted tracking rules with unchanged business behavior.""" import json import random import re from datetime import datetime, timedelta CARD_PREFIX = "__CARD__" def card_to_text(content): """把存库的卡片标记转成人话,避免原始 JSON 进入模型上下文""" if not content.startswith(CARD_PREFIX): return content try: card = json.loads(content[len(CARD_PREFIX):]) if card.get('kind') == 'order': return card.get('summary', '') return f"(历史演示轨迹,非真实物流信息:单号 {card.get('no', '')},演示状态:{card.get('status', '')})" except Exception: return "(已为该运单查询过轨迹)" def is_tracking_no(text): """判断一句话是不是"整句运单号" 规则(逐条收紧,避免误判): 1. 去掉空格和横杠后,长度 8~20 位,且只能由字母数字组成 2. 至少包含 3 个数字(英文句子通常只有 1 个数字) 3. 字母数量不能多于数字("i want to go 1t how money" 这类会被排除) 纯数字单号(如 15252125)全部满足上述条件。 """ # 日期格式(如 2026-10-23、2026/10/23)不算运单号 if re.fullmatch(r"\d{4}[-/.]\d{1,2}[-/.]\d{1,2}", text.strip()): return False t = re.sub(r"[\s\-]", "", text) if not (8 <= len(t) <= 20): return False if not re.fullmatch(r"[A-Za-z0-9]+", t): return False digits = sum(c.isdigit() for c in t) letters = sum(c.isalpha() for c in t) if digits < 3: return False if letters > digits: return False return True def extract_tracking_no(text): """从一句话里提取运单号:先看整句是不是,再从句子里找最长的字母数字串""" t = text.strip() if is_tracking_no(t): return t # 找句子里最长的 8~20 位字母数字连续串 candidates = re.findall(r"[A-Za-z0-9]{8,20}", t) best = None for c in candidates: if is_tracking_no(c): if best is None or len(c) > len(best): best = c return best def build_track(no): """Legacy demo helper for old imports; chat queries use order_test_data.""" rnd = random.Random(no) steps = [ ("已揽收", "义乌仓"), ("出口清关放行", "义乌/宁波口岸"), ("已发车", "中欧班列/卡航 · 阿拉山口"), ("到达欧洲中转仓", "波兰 · 华沙"), ("欧盟清关放行", "德国 · 杜伊斯堡"), ("派送中", "收件人所在城市"), ] gaps = [rnd.randint(6, 40) for _ in steps] # 相邻节点间隔(小时) t = datetime.now() - timedelta(hours=sum(gaps)) nodes = [] for (text, place), gap in zip(steps, gaps): t = t + timedelta(hours=gap) nodes.append({"time": t.strftime("%Y-%m-%d %H:%M"), "text": text, "place": place}) nodes.reverse() # 最新的排最上面 return {"no": no, "status": nodes[0]["text"], "nodes": nodes}