"""Local, editable order test data. This repository can later be replaced by MCP.""" import json import re import sqlite3 from datetime import datetime from .config import get_setting from .order_tracking import build_tracking from .visitor_service import customer_tenant_ids from .customer_service import order_login_response FIELDS = ('source_order_id', 'tenant_id', 'order_no', 'inbound_code', 'shipment_id', 'status', 'country_code', 'transport_mode', 'warehouse_code', 'order_role', 'parent_order_no', 'package_count', 'gross_weight_kg', 'volume_cbm', 'history_json', 'tracking_json', 'data_note', 'source_updated_at', 'snapshot_at') STATUS_LABELS = {'DRAFT': '草稿', 'SUBMITTED': '已提交', 'APPROVED': '审核通过', 'ACCEPTED': '已接单', 'REJECTED': '审核驳回', 'PROCESSING': '履约中', 'COMPLETED': '头程订单已完成', 'CANCELLED': '已取消'} FIELD_LABELS = {'inbound':'入仓码', 'number':'订单号', 'shipment':'Shipment ID', 'country':'目的国', 'warehouse':'目的仓', 'transport':'运输方式', 'packages':'件数', 'weight':'申报毛重', 'volume':'申报体积', 'role':'订单层级', 'parent':'父订单号', 'updated':'来源更新时间'} IDENTIFIER = re.compile(r'(? 1)} def answer_count_query(intent, tenants): if not tenants: return order_login_response() if intent['unsupported']: return {'t':'暂不支持按这个条件统计。可以查询订单总数,或按草稿、已提交、履约中、已完成、已取消等业务状态统计。'} try: counts = count_orders(tenants,intent['status']) except OrderDataUnavailable: return {'t':'订单信息暂时无法读取,请稍后重试。'} if intent['status']: text = f"您当前有 {counts['total']} 票业务状态为“{STATUS_LABELS[intent['status']]}”的订单" else: text = f"您当前共有 {counts['total']} 票订单" return {'t':text + f",其中 {counts['with_inbound']} 票已登记入仓码。", '_customer_data':True} def answer_list_query(message, tenants): """Reuse bounded status parsing; list reads require an explicit customer scope.""" if is_lookup_help(message) or not re.search(r'订单|运单|\borders?\b|\bshipments?\b',message,re.I): return None if not re.search(r'列出|列表|全部|所有|\blist\b',message,re.I): return None if not tenants: return order_login_response() question=re.sub(r'列出|列表|全部|所有|\blist\b','多少',message,flags=re.I) intent=parse_count_query(question) if intent is None or intent['unsupported']: return {'t':'暂不支持按这个条件列出订单。可查看全部订单或按履约中、已完成等业务状态查询。'} scope=tenant_filter(tenant_ids=tenants) if scope is None: return order_login_response() from .db import connect connection=None try: connection=connect() sql='SELECT order_no,inbound_code,status,warehouse_code FROM order_test_data WHERE 1=1'+scope[0] values=scope[1] if intent['status']: sql+=' AND status=?';values=values+[intent['status']] rows=connection.execute(sql+' ORDER BY id DESC LIMIT 21',values).fetchall() if not rows:return {'t':'当前账号下没有符合条件的订单。','_customer_data':True} lines=['您当前的订单列表'+('(先列出20票)' if len(rows)>20 else '')+':'] for number,inbound,status,warehouse in rows[:20]: lines.append(f'- 入仓码:{inbound or "未登记"};订单号:{number};业务状态:{STATUS_LABELS.get(status,status)};目的仓:{warehouse or "未登记"}') lines.append('发送入仓码可查询该票订单和物流轨迹。') return {'t':'\n'.join(lines),'_customer_data':True} except sqlite3.Error: return {'t':'订单信息暂时无法读取,请稍后重试。'} finally: if connection is not None:connection.close() def extract_reference(message): # Explicit postal/monetary fields are not order identifiers, even if invalid. message = re.sub(r'(?:邮编|zip(?:\s*code)?|post\s*code)\s*[::为是]?\s*[0-9-]+', ' ', message, flags=re.I) message = re.sub(r'(? 1: return {'t':'请一次提供一个入仓码,避免查错订单。'} reference = references[0] if references else None if not reference and conv_id and is_order_followup(message): from .db import connect connection = connect() try: recent = connection.execute('SELECT role,content FROM messages WHERE conv_id=? ORDER BY id DESC LIMIT 12', (conv_id,)).fetchall() finally: connection.close() for role, content in recent: if role == 'user': earlier_references = extract_reference(content) if earlier_references: reference = earlier_references[0] if len(earlier_references) == 1 else None break if not is_order_followup(content): break if role == 'assistant' and content.startswith('__CARD__'): try: previous = json.loads(content[8:]) if previous.get('kind') == 'order': reference = previous['no'] break except (ValueError, KeyError, TypeError): pass if not reference: if mutation: return order_login_response() if not tenants else {'t':'当前支持查询订单与物流轨迹;改址、取消等操作请联系人工客服办理,尚未修改订单。请提供入仓码。'} if is_tracking_request(message) or (not is_lookup_help(message) and re.search(r'(?:查|搜|看|找).*(?:订单|运单|物流轨迹|入仓码|入仓号)|(?:入仓码|入仓号).*(?:查|轨迹|状态)|订单状态|物流轨迹|shipment\s*id|' r'(?:列出|搜索|所有|全部|多少|我的).*(?:订单|运单|入仓)|(?:订单|运单).*(?:多少|列表|查询|在哪|到哪|进度)|' r'^\s*(?:查单|查询|查一下|帮我查一下)[。?!?!\s]*$|' r'\b(?:my|find|lookup|track)\s+(?:orders?|shipments?)\b|\b(?:order|shipment)\s+(?:status|tracking|lookup)\b|' r'\b(?:track|lookup|find|search)\b.*\binbound\b|\binbound\s+(?:code|number)\s+(?:lookup|tracking|status)\b', message, re.I)): return order_login_response() if not tenants else {'t':'请提供入仓码查询订单和物流轨迹。'} return None if not tenants: return order_login_response() try: matches = lookup_order(reference,tenant_id=tenants[0]) if len(tenants) == 1 else lookup_order(reference,tenant_ids=tenants) except OrderDataUnavailable: return {'t':'订单信息暂时无法读取,请稍后重试。'} if not matches: return {'t':f'未找到“{reference}”对应的订单。请核对入仓码,或联系人工客服。'} if len(matches) > 1: return {'t':'该标识匹配到多个订单,请提供完整入仓码或联系人工客服确认。'} if mutation: return {'t':f'已查到订单“{reference}”。当前支持查询订单与物流轨迹;改址、取消等操作请联系人工客服办理,尚未修改订单。','_customer_data':True} if re.search(r'运费|费用|多少钱|价格', message): return {'t':'该订单未记录实际运费,请联系人工客服核实。','_customer_data':True} return {'card': build_card(matches[0])}