落地转人工:三次补问或明确接管后出入口,真正转人工后冻结本票并允许新开询价。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-20 16:42:13 +08:00
co-authored by Cursor
parent 26dd5d72a1
commit 7eedf28e8d
23 changed files with 1499 additions and 132 deletions
+82 -23
View File
@@ -37,7 +37,9 @@ _COLLAB_FILL_ALIASES = {
logger = logging.getLogger(__name__)
TERMINAL = {"已成交", "未成交", "已关闭"}
TERMINAL = {"已成交", "未成交", "已关闭", "转人工"}
CLOSED = {"已成交", "未成交", "已关闭"}
HANDOFF_STATUS = "转人工"
OPEN_EDIT = {"询价中", "已报价", "协商中"}
EXCEL_PDF = {".xlsx", ".xls", ".xlsm", ".pdf"}
@@ -126,6 +128,57 @@ def is_terminal(ticket: object) -> bool:
return ticket_status(ticket) in TERMINAL
def is_handoff_ticket(ticket: object, sess: Any = None) -> bool:
"""这一票已经真正转人工:群里不再跟、也不回锁定话术。"""
if ticket_status(ticket) == HANDOFF_STATUS:
return True
if sess is None:
return False
if str(getattr(sess, "status", "") or "").strip() == HANDOFF_STATUS:
return True
return bool(getattr(sess, "handoff_committed", False))
def halt_closed_group(
*,
flow: Any,
ticket: object,
sess: Any = None,
chat_id: str = "",
file: bool = False,
) -> str:
"""
转人工:静默。已成交/未成交/已关闭:回不能改/不能传附件。
返回空表示可以继续跟。
"""
no = ticket_no(ticket)
room = (chat_id or "").strip() or str(
(sess.collab_chat_id if sess else "")
or ticket_view(ticket).get("collab_chat_id")
or ""
)
if is_handoff_ticket(ticket, sess):
logger.info("group.handoff_ignore wo=%s chat=%s", no or "-", room or "-")
return "handoff_ignore"
st = ticket_status(ticket) or (
str(getattr(sess, "status", "") or "").strip() if sess is not None else ""
)
if st in CLOSED or is_terminal(ticket) or (
sess is not None and str(getattr(sess, "status", "") or "").strip() in CLOSED
):
client = group_client_of(flow)
if file:
send_group_text(
client, room, copy.ticket_locked_file(work_order_no=no, status=st or "已关闭")
)
return "ticket_locked_file"
send_group_text(
client, room, copy.ticket_locked(work_order_no=no, status=st or "已关闭")
)
return "ticket_locked"
return ""
def is_sales(ticket: object, sender_id: str) -> bool:
uid = (sender_id or "").strip()
view = ticket_view(ticket)
@@ -612,9 +665,9 @@ def apply_collab_update(
no = ticket_no(ticket)
chat_id = str((sess.collab_chat_id if sess else "") or ticket_view(ticket).get("collab_chat_id") or "")
client = group_client_of(flow)
if is_terminal(ticket):
send_group_text(client, chat_id, copy.ticket_locked(work_order_no=no, status=ticket_status(ticket)))
return "ticket_locked"
stopped = halt_closed_group(flow=flow, ticket=ticket, sess=sess, chat_id=chat_id)
if stopped:
return stopped
line, keys = _collab_keys_of(ticket, sess)
fields = extract_collab_fields(text, allow_b=True, allowed_keys=keys)
if not fields:
@@ -665,9 +718,9 @@ def apply_product_quote(
view = ticket_view(ticket)
chat_id = str((sess.collab_chat_id if sess else "") or view.get("collab_chat_id") or "")
client = group_client_of(flow)
if is_terminal(ticket):
send_group_text(client, chat_id, copy.ticket_locked(work_order_no=no, status=ticket_status(ticket)))
return "ticket_locked"
stopped = halt_closed_group(flow=flow, ticket=ticket, sess=sess, chat_id=chat_id)
if stopped:
return stopped
if not is_product(ticket, sender_id, flow=flow):
send_group_text(client, chat_id, copy.group_only_product_can_quote())
return "quote_not_product"
@@ -728,9 +781,9 @@ def apply_tms_quote_choice(
view = ticket_view(ticket)
chat_id = str((sess.collab_chat_id if sess else "") or view.get("collab_chat_id") or "")
client = group_client_of(flow)
if is_terminal(ticket):
send_group_text(client, chat_id, copy.ticket_locked(work_order_no=no, status=ticket_status(ticket)))
return "ticket_locked"
stopped = halt_closed_group(flow=flow, ticket=ticket, sess=sess, chat_id=chat_id)
if stopped:
return stopped
if not is_product(ticket, sender_id, flow=flow):
send_group_text(client, chat_id, copy.group_only_product_can_quote())
return "quote_not_product"
@@ -777,9 +830,9 @@ def apply_confirm_tms_quote(
view = ticket_view(ticket)
chat_id = str((sess.collab_chat_id if sess else "") or view.get("collab_chat_id") or "")
client = group_client_of(flow)
if is_terminal(ticket):
send_group_text(client, chat_id, copy.ticket_locked(work_order_no=no, status=ticket_status(ticket)))
return "ticket_locked"
stopped = halt_closed_group(flow=flow, ticket=ticket, sess=sess, chat_id=chat_id)
if stopped:
return stopped
if not is_product(ticket, sender_id, flow=flow):
send_group_text(client, chat_id, copy.group_only_product_can_quote())
return "quote_not_product"
@@ -881,9 +934,11 @@ def handle_group_file(
view = ticket_view(ticket)
chat_id = str((sess.collab_chat_id if sess else "") or view.get("collab_chat_id") or "")
client = group_client_of(flow)
if is_terminal(ticket):
send_group_text(client, chat_id, copy.ticket_locked_file(work_order_no=no, status=ticket_status(ticket)))
return "ticket_locked_file"
stopped = halt_closed_group(
flow=flow, ticket=ticket, sess=sess, chat_id=chat_id, file=True
)
if stopped:
return stopped
if not filename_is_excel_pdf(filename):
send_group_text(client, chat_id, copy.group_only_excel_pdf())
return "file_type_rejected"
@@ -993,10 +1048,9 @@ def apply_group_deal(
view = ticket_view(ticket)
chat_id = str((sess.collab_chat_id if sess else "") or view.get("collab_chat_id") or "")
client = group_client_of(flow)
if is_terminal(ticket) or (sess is not None and getattr(sess, "status", "") in TERMINAL):
status = ticket_status(ticket) or (sess.status if sess else "")
send_group_text(client, chat_id, copy.ticket_locked(work_order_no=no, status=status))
return "ticket_locked"
stopped = halt_closed_group(flow=flow, ticket=ticket, sess=sess, chat_id=chat_id)
if stopped:
return stopped
if not is_sales(ticket, sender_id):
send_group_text(client, chat_id, copy.group_deal_sales_only())
return "not_sales"
@@ -1097,9 +1151,14 @@ def explain_deal_click(
chat_id = (sess.collab_chat_id or "").strip()
client = group_client_of(flow)
no = sess.work_order_no
if sess.status in TERMINAL:
send_group_text(client, chat_id, copy.ticket_locked(work_order_no=no, status=sess.status))
return "ticket_locked"
stopped = halt_closed_group(
flow=flow,
ticket={"work_order_no": no, "status": sess.status, "collab_chat_id": chat_id},
sess=sess,
chat_id=chat_id,
)
if stopped:
return stopped
if sender_id != sess.sender_id:
send_group_text(client, chat_id, copy.group_deal_sales_only())
return "not_sales"