落地空运单聊询价字段与线路选择。

必填核对后再建单;多条线路走 H5 点选;费用只展示该线路 TMS 回包,不套用整单空运费。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-22 15:02:32 +08:00
co-authored by Cursor
parent 8afe909230
commit cfbce2fd69
53 changed files with 3470 additions and 372 deletions
@@ -43,6 +43,8 @@ class TestSystemExceptionCopy(unittest.TestCase):
self.assertEqual(copy.system_exception_recovery(STEP_TMS_QUOTE), copy.SYS_EXC_RECOVERY_TMS)
self.assertEqual(copy.system_exception_recovery(STEP_LLM), copy.SYS_EXC_RECOVERY_LLM)
self.assertEqual(copy.system_exception_recovery(STEP_TMS_CABIN), copy.SYS_EXC_RECOVERY_CABIN)
self.assertEqual(copy.system_exception_recovery(copy.STEP_TMS_LOCK), copy.SYS_EXC_RECOVERY_CABIN)
self.assertEqual(copy.system_exception_recovery(copy.STEP_TMS_RELEASE), copy.SYS_EXC_RECOVERY_CABIN)
self.assertEqual(copy.system_exception_recovery(STEP_GROUP), copy.SYS_EXC_RECOVERY_GROUP)
def test_impact_by_step(self) -> None:
@@ -52,6 +54,99 @@ class TestSystemExceptionCopy(unittest.TestCase):
self.assertEqual(copy.system_exception_impact(STEP_TMS_CABIN), copy.IMPACT_TMS_CABIN)
self.assertEqual(copy.system_exception_impact(STEP_GROUP), copy.IMPACT_GROUP)
def test_cabin_impact_lock_and_release_are_separate(self) -> None:
"""锁舱失败和释放失败要分开说,不能再写成「锁舱/释放暂停」。"""
self.assertEqual(
copy.system_exception_impact(copy.STEP_TMS_LOCK),
"锁舱失败,工单状态保持不变",
)
self.assertEqual(
copy.system_exception_impact(copy.STEP_TMS_RELEASE),
"释放舱位失败,工单状态保持不变",
)
def test_alert_title_never_has_test_prefix(self) -> None:
"""系统异常通知标题不要 [test] / 【test】。"""
payload = copy.system_exception_alert_payload(
work_order_no="WO202609210002",
exception_type=TYPE_TMS,
service="空运锁舱",
error_code="LOCK_FAIL",
step=copy.STEP_TMS_LOCK,
occurred_at="2026-09-21 16:00:00",
test_prefix=True,
)
title = payload["template_card"]["main_title"]["title"]
self.assertEqual(title, "系统异常通知")
self.assertNotIn("[test]", title)
self.assertNotIn("【test】", title)
text = copy.system_exception_alert_text(
work_order_no="WO202609210002",
exception_type=TYPE_TMS,
service="空运锁舱",
error_code="LOCK_FAIL",
step=copy.STEP_TMS_LOCK,
occurred_at="2026-09-21 16:00:00",
test_prefix=True,
)
self.assertTrue(text.startswith("询价机器人系统告警"))
self.assertNotIn("[test]", text)
self.assertNotIn("【test】", text)
def test_lock_fail_copy_has_operator_reason_and_it_notice(self) -> None:
text = copy.air_lock_fail(
work_order_no="WO202609210001",
operator="吴季林",
reason="该航班日期不存在可锁定的配载记录",
)
self.assertEqual(
text,
"工单WO202609210001 空运锁舱失败。\n"
"操作人:吴季林\n"
"锁舱失败的原因:该航班日期不存在可锁定的配载记录\n"
"已通知IT运维排查,请稍等。",
)
def test_release_fail_copy_has_operator_reason_and_it_notice(self) -> None:
text = copy.air_release_fail(
work_order_no="WO202609210001",
operator="吴季林",
reason="舱位不存在",
)
self.assertEqual(
text,
"工单WO202609210001 空运释放舱位失败。\n"
"操作人:吴季林\n"
"释放舱位失败的原因:舱位不存在\n"
"已通知IT运维排查,请稍等。",
)
def test_cabin_fail_reason_keeps_tms_text(self) -> None:
self.assertEqual(
copy.air_cabin_fail_reason(
tms_reason="该航班日期不存在可锁定的配载记录",
flight_date="2026-09-20",
today="2026-09-21",
),
"该航班日期不存在可锁定的配载记录",
)
self.assertNotIn(
"报价选项不存在",
copy.air_cabin_fail_reason(
tms_reason="该航班日期不存在可锁定的配载记录",
flight_date="2026-09-20",
today="2026-09-21",
),
)
self.assertEqual(
copy.air_cabin_fail_reason(
tms_reason="",
flight_date="2026-09-20",
today="2026-09-21",
),
"",
)
class TestSystemExceptionRules(unittest.TestCase):
def test_tms_no_price_is_not_tech(self) -> None:
@@ -145,6 +240,9 @@ class TestConfirmSystemException(unittest.TestCase):
self.assertEqual(card["main_title"]["desc"], "关联工单 无")
values = {row["keyname"]: row["value"] for row in card["horizontal_content_list"]}
self.assertEqual(values["影响范围"], copy.IMPACT_LLM_NO_TICKET)
self.assertEqual(card["main_title"]["title"], "系统异常通知")
self.assertNotIn("[test]", card["main_title"]["title"])
self.assertNotIn("[test]", queued[0]["content"])
def test_mark_ticket_and_pause(self) -> None:
replies: list[str] = []
@@ -171,6 +269,59 @@ class TestConfirmSystemException(unittest.TestCase):
self.assertTrue(ticket_is_paused(ticket))
self.assertEqual(replies[0], copy.SYS_EXC_TMS_FALLBACK)
def test_air_lock_alert_impact_keeps_status(self) -> None:
queued: list[dict] = []
def enqueue(*, touser, content, dedupe_key, payload):
queued.append({"payload": payload, "content": content})
return True, "ok"
confirm_system_exception(
event=SystemExceptionEvent(
exception_type=TYPE_TMS,
step=copy.STEP_TMS_LOCK,
reason="空运锁舱接口调用失败",
service="空运锁舱",
error_code="LOCK_FAIL",
work_order_no=self.wo,
ticket_status="已报价",
),
ledger=self.ledger,
reply=lambda _t: None,
enqueue=enqueue,
notify_ready=True,
)
card = queued[0]["payload"]["template_card"]
values = {row["keyname"]: row["value"] for row in card["horizontal_content_list"]}
self.assertEqual(values["影响范围"], "锁舱失败,工单状态保持不变")
self.assertEqual(card["main_title"]["title"], "系统异常通知")
def test_air_release_alert_impact_keeps_status(self) -> None:
queued: list[dict] = []
def enqueue(*, touser, content, dedupe_key, payload):
queued.append({"payload": payload})
return True, "ok"
confirm_system_exception(
event=SystemExceptionEvent(
exception_type=TYPE_TMS,
step=copy.STEP_TMS_RELEASE,
reason="空运释放舱位接口调用失败",
service="空运舱位",
error_code="RELEASE_FAIL",
work_order_no=self.wo,
ticket_status="已锁舱",
),
ledger=self.ledger,
reply=lambda _t: None,
enqueue=enqueue,
notify_ready=True,
)
card = queued[0]["payload"]["template_card"]
values = {row["keyname"]: row["value"] for row in card["horizontal_content_list"]}
self.assertEqual(values["影响范围"], "释放舱位失败,工单状态保持不变")
def test_closed_ticket_not_marked(self) -> None:
self.ledger.transition(work_order_no=self.wo, to_status="已关闭")
out = confirm_system_exception(