空运协同里手工改费用、有起运地、锁舱失败或缺参会被误判,报价单已出后再激活又重发模板。按销售侧口径收紧这些分支,并让价格卡和未成交回执带上真实工单号。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+72
@@ -207,6 +207,26 @@ public class InquiryTmsCabinService {
|
||||
req.put("requestedUntil", text(body, "requestedUntil"));
|
||||
}
|
||||
}
|
||||
// 航线口令里的航班号盖过报价。三项没齐就地拦住,不去调 TMS 锁舱。
|
||||
String flightFromCaller = firstNonBlank(
|
||||
text(body, "flightNo", "flight_no"),
|
||||
flightNoFromInstruction(text(body, "instructionText", "instruction_text")));
|
||||
if (StringUtils.isNotBlank(flightFromCaller)) {
|
||||
req.put("flightNo", flightFromCaller);
|
||||
}
|
||||
if (StringUtils.isBlank(text(req, "airline"))) {
|
||||
String fromFlight = airlineFromFlight(text(req, "flightNo"));
|
||||
if (StringUtils.isNotBlank(fromFlight)) {
|
||||
req.put("airline", fromFlight);
|
||||
}
|
||||
}
|
||||
String incomplete = missingLockParamReason(
|
||||
text(req, "optionNo"), text(req, "airline"), text(req, "flightNo"));
|
||||
if (StringUtils.isNotBlank(incomplete)) {
|
||||
log.info("tms.lock skip incomplete wo={} optionNo={} airline={} flightNo={}",
|
||||
no, text(req, "optionNo"), text(req, "airline"), text(req, "flightNo"));
|
||||
return fail("lock_params_incomplete", incomplete);
|
||||
}
|
||||
return invokeLock(ticket, req, quoteId, resolvedOption);
|
||||
}
|
||||
|
||||
@@ -432,6 +452,14 @@ public class InquiryTmsCabinService {
|
||||
req.remove("quoteId");
|
||||
}
|
||||
fillLockQuoteExtras(req, quote, option);
|
||||
if (StringUtils.isBlank(text(req, "validUntil"))) {
|
||||
// 航线手工价经常不写有效期。TMS 人工锁舱必填 validUntil,
|
||||
// 用工单报价日(调用前已放进 flightDate)的当天结束,不再空着出站。
|
||||
String iso = normalizeValidUntilIso(text(req, "flightDate"));
|
||||
if (StringUtils.isNotBlank(iso)) {
|
||||
req.put("validUntil", iso);
|
||||
}
|
||||
}
|
||||
String transfer = firstNonBlank(text(option, "transferAirport", "transferAirportCode"),
|
||||
text(quote, "transferAirport", "transferAirportCode"));
|
||||
if (StringUtils.isNotBlank(transfer)) {
|
||||
@@ -1049,6 +1077,50 @@ public class InquiryTmsCabinService {
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁舱三项没齐时的群回复。不叫锁舱失败,不提 TMS 查价结果。
|
||||
* 三项都缺时是:锁舱参数不完整,缺少:舱位编号、航司、航班号。请补充后再锁,例如:AIR-OPT01,南航,航班CZ327。
|
||||
*/
|
||||
static String missingLockParamReason(String optionNo, String airline, String flightNo) {
|
||||
java.util.List<String> missing = new ArrayList<>();
|
||||
if (StringUtils.isBlank(optionNo)) {
|
||||
missing.add("舱位编号");
|
||||
}
|
||||
if (StringUtils.isBlank(airline)) {
|
||||
missing.add("航司");
|
||||
}
|
||||
if (StringUtils.isBlank(flightNo)) {
|
||||
missing.add("航班号");
|
||||
}
|
||||
if (missing.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return "锁舱参数不完整,缺少:" + String.join("、", missing)
|
||||
+ "。请补充后再锁,例如:AIR-OPT01,南航,航班CZ327。";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从「AIR-OPT01 CZ 航班CZ327」抽出航班号。没有就不猜。
|
||||
*/
|
||||
static String flightNoFromInstruction(String instruction) {
|
||||
if (StringUtils.isBlank(instruction)) {
|
||||
return "";
|
||||
}
|
||||
java.util.regex.Matcher named = java.util.regex.Pattern
|
||||
.compile("(?i)(?:航班号|航班)\\s*[::]?\\s*(?:是|为)?\\s*([A-Za-z]{2}\\s*\\d{2,4})")
|
||||
.matcher(instruction);
|
||||
if (named.find()) {
|
||||
return named.group(1).toUpperCase();
|
||||
}
|
||||
java.util.regex.Matcher trailed = java.util.regex.Pattern
|
||||
.compile("(?i)AIR-?OPT-?\\d+\\s+(?:[A-Za-z]{2}\\s+)?([A-Za-z]{2}\\d{2,4})")
|
||||
.matcher(instruction);
|
||||
if (trailed.find()) {
|
||||
return trailed.group(1).toUpperCase();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从锁舱口令抽出两位航司码,例如「锁AIR-OPT01 TH」→ TH。
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,13 @@
|
||||
{"source_name": "北京首都", "source_code": "PEK"},
|
||||
{"source_name": "北京大兴", "source_code": "PKX"},
|
||||
{"source_name": "珠海", "source_code": "ZUH"},
|
||||
{"source_name": "克拉克", "source_code": "CRK"}
|
||||
{"source_name": "克拉克", "source_code": "CRK"},
|
||||
{"source_name": "南京", "source_code": "NKG"},
|
||||
{"source_name": "吉隆坡", "source_code": "KUL"},
|
||||
{"source_name": "北京", "source_code": "PEK"},
|
||||
{"source_name": "杭州", "source_code": "HGH"},
|
||||
{"source_name": "萧山", "source_code": "HGH"},
|
||||
{"source_name": "杭州萧山", "source_code": "HGH"}
|
||||
],
|
||||
"air_packaging_type": [
|
||||
{"source_name": "全部", "source_code": "1"},
|
||||
|
||||
+28
@@ -197,6 +197,34 @@ public class InquiryTmsCabinServiceTest {
|
||||
assertEquals("空运费", fees.get(0).get("feeName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void manualLockFillsValidUntilFromFlightDateWhenQuoteHasNone() {
|
||||
Map<String, Object> req = new HashMap<>();
|
||||
req.put("flightDate", "2026-10-11");
|
||||
InquiryTmsCabinService.fillManualLock(req, new HashMap<>(), new HashMap<>(), new HashMap<>(), false);
|
||||
assertEquals("2026-10-11T23:59:59+08:00", req.get("validUntil"));
|
||||
assertEquals("2026-10-11T23:59:59+08:00", req.get("requestedUntil"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingLockParamsAsksWithoutCallingTmsWording() {
|
||||
assertEquals(
|
||||
"锁舱参数不完整,缺少:舱位编号、航司、航班号。请补充后再锁,例如:AIR-OPT01,南航,航班CZ327。",
|
||||
InquiryTmsCabinService.missingLockParamReason("", "", ""));
|
||||
assertEquals(
|
||||
"锁舱参数不完整,缺少:航班号。请补充后再锁,例如:AIR-OPT01,南航,航班CZ327。",
|
||||
InquiryTmsCabinService.missingLockParamReason("AIR-OPT01", "CZ", ""));
|
||||
assertEquals("", InquiryTmsCabinService.missingLockParamReason("AIR-OPT01", "CZ", "CZ327"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flightNoFromInstructionReadsSampleFormat() {
|
||||
assertEquals("CZ327", InquiryTmsCabinService.flightNoFromInstruction(
|
||||
"AIR-OPT01 CZ 航班CZ327"));
|
||||
assertEquals("CZ327", InquiryTmsCabinService.flightNoFromInstruction("AIR-OPT01 CZ CZ327"));
|
||||
assertEquals("", InquiryTmsCabinService.flightNoFromInstruction("WO202610080023锁舱"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void airlineFromInstructionReadsCodeAfterOption() {
|
||||
assertEquals("TH", InquiryTmsCabinService.airlineFromInstruction(
|
||||
|
||||
+7
@@ -32,4 +32,11 @@ public class TmsExactProjectorTest {
|
||||
assertTrue(hit.ok());
|
||||
assertEquals("SHA", hit.code);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hangzhouMapsToXiaoshan() {
|
||||
TmsExactProjector.Projection hit = projector.resolveAirport("杭州");
|
||||
assertTrue(hit.ok());
|
||||
assertEquals("HGH", hit.code);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user