This commit is contained in:
zjf committed 2026-11-08 00:01:56 +08:00
1 parent 708352cfcb
commit 2225e74650
2 files changed
+51 -80

No files matched your search

@@ -1736,8 +1736,8 @@ public class SmHotelMidServiceImpl implements SmHotelMidService {
String productCode = e.getKey();
List<Map<String, Object>> ranges = mergeDailyRanges(rows);
for (List<Map<String, Object>> batch : CollUtil.split(ranges, 50)) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("extHotelId", extHotelId);
Map<String, Object> body = new LinkedHashMap<>();
body.put("extHotelId", extHotelId);
body.put("extApartmentId", roomCode);
body.put("extProductId", productCode);
body.put("list", batch);
@@ -3256,9 +3256,9 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
}
/**
* 组装玉编电云 order/add 请求体。
* 组装玉编电云 order/add 请求体(对齐 docs/shangyou/订单相关接口模式.pdf 请求示例)。
* <p>
* 字段名按官方文档:linkFirstName/linkMobile、guestInfoList、bookerPersonFirstName 等;
* 姓名用整字段 {@code linkName} / {@code bookerPersonName},不要拆 Last/First。
* dailyPriceList 必须覆盖入住夜(checkOut-checkIn),缺夜用产品底价补齐。
*/
private com.alibaba.fastjson.JSONObject buildUpstreamOrderBody(SmHotelOrderEntity order,
@@ -3274,11 +3274,17 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
req.put("checkOutDate", order.getCheckOutDate() == null ? null : order.getCheckOutDate().toString());
req.put("roomCount", order.getCount() == null ? 1 : order.getCount());
// 文档必填:联系人 linkLastName + linkFirstName(中文须拆姓/名,不能整名塞 FirstName)
NameParts link = splitPersonName(order.getContactName());
req.put("linkLastName", link.lastName());
req.put("linkFirstName", link.firstName());
// 联系人:文档字段为 linkName + linkMobile(整名,非 Last/First)
String linkName = resolveUpstreamPersonName(order);
req.put("linkName", linkName);
req.put("linkMobile", order.getContactPhone());
String linkEmail = str(downstreamBody.get("linkEmail"));
if (StrUtil.isBlank(linkEmail) && downstreamBody.get("contact") instanceof Map<?, ?> contactMap) {
linkEmail = str(contactMap.get("email"));
}
if (StrUtil.isNotBlank(linkEmail)) {
req.put("linkEmail", linkEmail);
}
boolean isGroup = resolveOrderIsGroup(downstreamBody);
String guarantee = StrUtil.blankToDefault(str(downstreamBody.get("guaranteeMode")),
@@ -3291,50 +3297,19 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
req.put("bookerPriceType", "COMPANY");
}
req.put("isNeedRegisterMember", false);
// 文档:bookerPersonId 必填;无员工号时用手机号兜底
// 预订人:文档为 bookerPersonId / bookerPersonName / bookerPersonMobile
req.put("bookerPersonId",
StrUtil.blankToDefault(str(downstreamBody.get("bookerPersonId")), order.getContactPhone()));
NameParts booker = splitPersonName(order.getContactName());
req.put("bookerPersonLastName", booker.lastName());
req.put("bookerPersonFirstName", booker.firstName());
req.put("bookerPersonName", linkName);
req.put("bookerPersonMobile", order.getContactPhone());
String idNumber = str(downstreamBody.get("bookerPersonIDNumber"));
if (StrUtil.isNotBlank(idNumber)) {
req.put("bookerPersonIDNumber", idNumber);
}
req.put("adults", downstreamBody.get("adults") == null ? 2
: Integer.parseInt(String.valueOf(downstreamBody.get("adults"))));
String guestRawName = order.getContactName();
String guestMobile = order.getContactPhone();
String guestLastOverride = null;
String guestFirstOverride = null;
if (StrUtil.isNotBlank(order.getGuestJson())) {
try {
JSONArray guests = JSONUtil.parseArray(order.getGuestJson());
if (guests != null && !guests.isEmpty()) {
JSONObject g0 = guests.getJSONObject(0);
guestLastOverride = g0.getStr("guestLastName");
guestFirstOverride = g0.getStr("guestFirstName");
guestRawName = StrUtil.blankToDefault(g0.getStr("name"),
StrUtil.blankToDefault(guestFirstOverride, guestRawName));
if (StrUtil.isNotBlank(guestLastOverride) && StrUtil.isNotBlank(guestFirstOverride)) {
guestRawName = guestLastOverride + guestFirstOverride;
}
guestMobile = StrUtil.blankToDefault(
StrUtil.blankToDefault(g0.getStr("guestPhone"), g0.getStr("mobile")), guestMobile);
}
}
catch (Exception ignored) {
}
}
NameParts guest = (StrUtil.isNotBlank(guestLastOverride) && StrUtil.isNotBlank(guestFirstOverride))
? new NameParts(guestLastOverride, guestFirstOverride)
: splitPersonName(guestRawName);
com.alibaba.fastjson.JSONArray guestInfoList = new com.alibaba.fastjson.JSONArray();
com.alibaba.fastjson.JSONObject guestRow = new com.alibaba.fastjson.JSONObject();
guestRow.put("guestLastName", guest.lastName());
guestRow.put("guestFirstName", guest.firstName());
guestRow.put("guestPhone", guestMobile);
guestRow.put("guestRoomNo", 1);
guestInfoList.add(guestRow);
req.put("guestInfoList", guestInfoList);
req.put("childs", downstreamBody.get("childs") == null ? 0
: Integer.parseInt(String.valueOf(downstreamBody.get("childs"))));
Object remark = downstreamBody.get("remark");
if (remark != null) {
@@ -3349,6 +3324,35 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
return req;
}
/**
* 上游联系人/预订人整名:优先入住人 name,否则联系人;对齐文档 linkName / bookerPersonName。
*/
private static String resolveUpstreamPersonName(SmHotelOrderEntity order) {
String name = order == null ? null : StrUtil.trim(order.getContactName());
if (order != null && StrUtil.isNotBlank(order.getGuestJson())) {
try {
JSONArray guests = JSONUtil.parseArray(order.getGuestJson());
if (guests != null && !guests.isEmpty()) {
JSONObject g0 = guests.getJSONObject(0);
String guestName = StrUtil.trim(g0.getStr("name"));
if (StrUtil.isBlank(guestName)) {
String last = StrUtil.trim(g0.getStr("guestLastName"));
String first = StrUtil.trim(g0.getStr("guestFirstName"));
if (StrUtil.isNotBlank(last) || StrUtil.isNotBlank(first)) {
guestName = StrUtil.blankToDefault(last, "") + StrUtil.blankToDefault(first, "");
}
}
if (StrUtil.isNotBlank(guestName)) {
name = guestName;
}
}
}
catch (Exception ignored) {
}
}
return StrUtil.blankToDefault(name, "客人");
}
private boolean resolveOrderIsGroup(Map<String, Object> body) {
Object v = body == null ? null : body.get("isGroup");
if (v instanceof Boolean) {
@@ -3360,39 +3364,6 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
return channelConfigService.resolveShangyouRuntime().isOrderIsGroup();
}
/**
* 上游姓名格式:LastName=姓 / FirstName=名。
* <ul>
* <li>中文:首字为姓,其余为名(复姓未特殊处理;单字名时名为 "/")</li>
* <li>英文空格分隔:取末段为姓、首段为名</li>
* <li>英文/拼音单段:姓、名都用整段(勿填 "/",商游 order/add 易直接 500)</li>
* </ul>
*/
private static NameParts splitPersonName(String raw) {
String name = StrUtil.trim(raw);
if (StrUtil.isBlank(name)) {
return new NameParts("测", "试");
}
// 去掉中间空格后的纯中文
String compact = name.replace(" ", "").replace(" ", "");
if (compact.chars().allMatch(ch -> Character.UnicodeScript.of(ch) == Character.UnicodeScript.HAN)) {
if (compact.length() == 1) {
return new NameParts(compact, "/");
}
return new NameParts(compact.substring(0, 1), compact.substring(1));
}
// 英文:含空格则拆成两段(名 姓 → first last;若多段取首为名、末为姓)
String[] parts = name.trim().split("\\s+");
if (parts.length >= 2) {
return new NameParts(parts[parts.length - 1], parts[0]);
}
// 拼音/英文单段:避免 firstName="/" 被上游拒
return new NameParts(name, name);
}
private record NameParts(String lastName, String firstName) {
}
/**
* 按住店夜补齐 dailyPriceList;本地日历缺夜则用产品底价兜底。
*/