|
|
const { objectMapper: _objectMapper, isNotEmpty, isEmpty } = require('../utils/commons');
|
|
|
const { DEFAULT_LGC, LGC_MAPPED } = require('../config/constants');
|
|
|
|
|
|
const objectMapper = (input, keyMap) => _objectMapper(input || {}, keyMap, false);
|
|
|
|
|
|
const infoDataMapper = (row) => {
|
|
|
const item = objectMapper(row, {
|
|
|
HotelId: 'hotel_id',
|
|
|
// HotelName: 'hotel_name',
|
|
|
LocaleName: 'hotel_name',
|
|
|
CountryCode: 'country_code',
|
|
|
CityId: 'city_id',
|
|
|
// Address: 'address',
|
|
|
AddressLocale: 'address',
|
|
|
Phone: 'phone',
|
|
|
Latitude: 'latitude',
|
|
|
Longitude: 'longitude',
|
|
|
Rating: 'rating',
|
|
|
HotelType: 'hotel_type',
|
|
|
Brand: 'brand',
|
|
|
PostalCode: 'postal_code',
|
|
|
HeroImg: 'hero_img',
|
|
|
SupplierType: 'supplier_type',
|
|
|
});
|
|
|
// item.hotel_name = isEmpty(item.hotel_name) ? row.LocaleName : item.hotel_name;
|
|
|
const review = objectMapper(row.Review, {
|
|
|
Score: 'review_score',
|
|
|
ReviewCount: 'review_count',
|
|
|
Desc: 'review_desc',
|
|
|
});
|
|
|
const location = objectMapper(row.Location, {
|
|
|
PoiScore: 'location_poi_score',
|
|
|
AirportScore: 'location_airport_score',
|
|
|
TransportationScore: 'location_transportation_score',
|
|
|
});
|
|
|
return {...item, ...review, ...location};
|
|
|
}
|
|
|
|
|
|
const info2DataMapper = (row, lgcObj) => {
|
|
|
const item = objectMapper(row, {
|
|
|
HotelId: 'hotel_id',
|
|
|
LocaleName: 'hi2_hotel_name',
|
|
|
Description: 'hi2_description',
|
|
|
AddressLocale: 'hi2_address',
|
|
|
});
|
|
|
const instruction = objectMapper(row.Instruction, {
|
|
|
Desc: 'h2_instruction_desc',
|
|
|
Special: 'h2_instruction_special',
|
|
|
FeesDesc: 'h2_instruction_fees_desc',
|
|
|
});
|
|
|
const location = objectMapper((row.Location?.WalkablePlaces), {
|
|
|
Title: 'h2_location_walkable_places_title',
|
|
|
Desc: 'h2_location_walkable_places_desc',
|
|
|
});
|
|
|
const highlight = objectMapper(row.Highlight, {
|
|
|
LocationHighlightDesc: 'h2_location_highlight_desc',
|
|
|
});
|
|
|
const review = objectMapper(row.Review, {
|
|
|
Desc: 'h2_review_desc',
|
|
|
});
|
|
|
return { ...item, ...instruction, ...location, ...highlight, ...lgcObj, ...review };
|
|
|
};
|
|
|
|
|
|
const roomsMapper = (row, lgcObj) => {
|
|
|
const rooms = (row.Rooms || []).map((rowRoom) => ({
|
|
|
hotel_id: row.HotelId,
|
|
|
...lgcObj,
|
|
|
...(objectMapper(rowRoom, {
|
|
|
RoomId: 'room_id',
|
|
|
RoomName: 'room_name',
|
|
|
LocaleName: 'locale_name',
|
|
|
BedTypeDesc: 'bed_type_desc',
|
|
|
Area: 'area',
|
|
|
Views: 'views',
|
|
|
Window: 'window',
|
|
|
Floor: 'floor',
|
|
|
WirelessWideband: 'wireless_wideband',
|
|
|
WiredBroadband: 'wired_broadband',
|
|
|
Smoking: 'smoking',
|
|
|
BathRoomType: 'bathroom_type',
|
|
|
// Images: 'images',
|
|
|
})),
|
|
|
max_occupancy: rowRoom.MaxOccupancy ? JSON.stringify(rowRoom.MaxOccupancy) : null,
|
|
|
bedrooms: rowRoom.BedRooms ? JSON.stringify(rowRoom.BedRooms) : null,
|
|
|
}));
|
|
|
return rooms;
|
|
|
};
|
|
|
const imagesDaraMapper = (row, lgcObj) => {
|
|
|
const images = (row.Rooms || []).reduce(
|
|
|
(r, rowRoom) =>
|
|
|
r.concat(
|
|
|
(rowRoom.Images || []).map((imgItem) => ({
|
|
|
hotel_id: row.HotelId,
|
|
|
...lgcObj,
|
|
|
info_source: 'rooms',
|
|
|
info_source_id: rowRoom.RoomId,
|
|
|
type: null,
|
|
|
url: imgItem.Url,
|
|
|
caption: null,
|
|
|
category: imgItem.Category,
|
|
|
}))
|
|
|
),
|
|
|
[]
|
|
|
);
|
|
|
const urls = (row.Urls || []).reduce(
|
|
|
(r, rowUrl) =>
|
|
|
r.concat(
|
|
|
rowUrl.Urls.map((imgItem) => ({
|
|
|
hotel_id: row.HotelId,
|
|
|
...lgcObj,
|
|
|
info_source: 'hotelinfo',
|
|
|
info_source_id: row.HotelId,
|
|
|
type: imgItem.Type,
|
|
|
url: imgItem.Url,
|
|
|
caption: rowUrl.Caption,
|
|
|
category: rowUrl.Category,
|
|
|
}))
|
|
|
),
|
|
|
[]
|
|
|
);
|
|
|
return [].concat(images, urls);
|
|
|
};
|
|
|
const mainhighlightsMapper = (row, lgcObj) => {
|
|
|
return (row.Highlight?.MainHighlights || []).map((mhl) => ({
|
|
|
hotel_id: row.HotelId, // hotel_id:
|
|
|
...lgcObj,
|
|
|
type: 'MainHighlights', // type:
|
|
|
category: mhl.Category, // category:
|
|
|
category_name: '', // category_name:
|
|
|
id: null, // id:
|
|
|
name: mhl.Name, // name:
|
|
|
symbol: null, // symbol:
|
|
|
tooltip: mhl.Tooltip, // tooltip:
|
|
|
}));
|
|
|
};
|
|
|
const facilityCMapper = (row, lgcObj) => {
|
|
|
return (row.Facility?.Categories || []).reduce(
|
|
|
(r, fc) =>
|
|
|
r.concat(
|
|
|
fc.Items.map((fcItem) => ({
|
|
|
hotel_id: row.HotelId, // hotel_id:
|
|
|
...lgcObj,
|
|
|
type: 'Facility', // type:
|
|
|
category: fc.Category, // category:
|
|
|
category_name: fc.Name, // category_name:
|
|
|
id: fcItem.Id, // id:
|
|
|
name: fcItem.Name, // name:
|
|
|
symbol: fcItem.Symbol, // symbol:
|
|
|
tooltip: null, // tooltip:
|
|
|
}))
|
|
|
),
|
|
|
[]
|
|
|
);
|
|
|
};
|
|
|
const facilityHLMapper = (row, lgcObj) =>
|
|
|
(row.Facility?.Highlights || []).map((fl) => ({
|
|
|
hotel_id: row.HotelId, // hotel_id:
|
|
|
...lgcObj,
|
|
|
type: 'Highlights', // type:
|
|
|
category: '', // category:
|
|
|
category_name: '', // category_name:
|
|
|
id: fl.Id, // id:
|
|
|
name: fl.Name, // name:
|
|
|
symbol: fl.Symbol, // symbol:
|
|
|
tooltip: null, // tooltip:
|
|
|
}));
|
|
|
|
|
|
const informationsMapper = (row, lgcObj) =>
|
|
|
(row.Informations || []).reduce(
|
|
|
(r, fc) =>
|
|
|
r.concat(
|
|
|
fc.Items.map((fcItem) => ({
|
|
|
hotel_id: row.HotelId, // hotel_id:
|
|
|
...lgcObj,
|
|
|
category: fc.Category, // category:
|
|
|
category_name: fc.CategoryName, // category_name:
|
|
|
id: fcItem.Id, // id:
|
|
|
name: fcItem.Name, // name:
|
|
|
value: fcItem.Value, // value:
|
|
|
}))
|
|
|
),
|
|
|
[]
|
|
|
);
|
|
|
const locationPlacesMapper = (sourceName, hotelId, places, lgcObj) =>
|
|
|
places.map((item) => ({
|
|
|
hotel_id: hotelId, // hotel_id:
|
|
|
...lgcObj,
|
|
|
location_type: sourceName, // location_type:
|
|
|
category: null, // category:
|
|
|
category_name: null, // category_name:
|
|
|
id: item.Id || null, // id:
|
|
|
name: item.Name, // name:
|
|
|
distance: item.Distance, // distance:
|
|
|
latitude: item.Latitude || null, // latitude:
|
|
|
longitude: item.Longitude || null, // longitude:
|
|
|
type_name: item.TypeName || null, // type_name:
|
|
|
type_id: item.TypeId || null, // type_id:
|
|
|
min_distance: null, // min_distance:
|
|
|
}));
|
|
|
const scoresMapper = (sourceName, hotelId, scores, lgcObj) => scores.map((item) => ({
|
|
|
hotel_id: hotelId, // hotel_id:
|
|
|
...lgcObj,
|
|
|
type: sourceName, // type:
|
|
|
name: sourceName==='ScoreDetails' ? item.Name : null, // name:
|
|
|
category: item.Category || null, // category:
|
|
|
score: item.Score || null, // score:
|
|
|
city_average: item.CityAverage || null, // city_average:
|
|
|
mention_name: sourceName==='PositiveMentions' ? item.Name : null, // mention_name:
|
|
|
mention_count: sourceName==='PositiveMentions' ? item.Count : null, // mention_count:
|
|
|
}));
|
|
|
|
|
|
/**
|
|
|
* 解析Details数据
|
|
|
* todo: BedRoom MaxOccupancy
|
|
|
*/
|
|
|
const resolveDetails = (res, lgcObj) => {
|
|
|
return res.reduce(
|
|
|
(rd, c) => {
|
|
|
if (isEmpty(c.HotelName) && isEmpty(c.LocaleName)) {
|
|
|
return rd;
|
|
|
}
|
|
|
rd.info.push(infoDataMapper(c));
|
|
|
rd.info2.push(info2DataMapper(c, lgcObj));
|
|
|
rd.rooms = rd.rooms.concat(roomsMapper(c, lgcObj));
|
|
|
|
|
|
rd.images = rd.images.concat(imagesDaraMapper(c, lgcObj));
|
|
|
// rd.images = rd.images.concat(urls);
|
|
|
|
|
|
// rd.test = rd.test.concat([c.Highlight.LocationHighlightDesc]); // test:
|
|
|
|
|
|
const MainHighlights = mainhighlightsMapper(c, lgcObj);
|
|
|
const facilityC = facilityCMapper(c, lgcObj);
|
|
|
const facilityHL = facilityHLMapper(c, lgcObj);
|
|
|
rd.facility = rd.facility.concat(MainHighlights, facilityC, facilityHL);
|
|
|
|
|
|
const infos = informationsMapper(c, lgcObj);
|
|
|
rd.infos = rd.infos.concat(infos);
|
|
|
|
|
|
const reviewScores = scoresMapper('ScoreDetails', c.HotelId, c.Review?.ScoreDetails || [], lgcObj);
|
|
|
const mentions = scoresMapper('PositiveMentions', c.HotelId, c.Review?.PositiveMentions || [], lgcObj);
|
|
|
rd.reviews = rd.reviews.concat(reviewScores, mentions);
|
|
|
|
|
|
const reviewSummaries = (c.Review?.Summaries || []).map((item) => ({
|
|
|
hotel_id: c.HotelId, // hotel_id:
|
|
|
...lgcObj,
|
|
|
country: item.Country, // country:
|
|
|
reviewer: item.Reviewer, // reviewer:
|
|
|
review_rating: item.ReviewRating, // review_rating:
|
|
|
desc: item.Desc, // desc:
|
|
|
review_date: item.ReviewDate, // review_date:
|
|
|
}));
|
|
|
rd.review_summaries = rd.review_summaries.concat(reviewSummaries);
|
|
|
|
|
|
const TopPlaces = locationPlacesMapper('TopPlaces', c.HotelId, c.Location?.TopPlaces || [], lgcObj);
|
|
|
const Shops = locationPlacesMapper('Shops', c.HotelId, c.Location?.Shops || [], lgcObj);
|
|
|
const Places = locationPlacesMapper('Places', c.HotelId, c.Location?.Places || [], lgcObj);
|
|
|
const NearbyCategories = (c.Location?.NearbyCategories || []).reduce(
|
|
|
(r, nc) =>
|
|
|
r.concat(
|
|
|
locationPlacesMapper('NearbyCategories', c.HotelId, nc.Places || [], lgcObj).map((item) => ({
|
|
|
...item,
|
|
|
category: nc.Category,
|
|
|
category_name: nc.Name,
|
|
|
min_distance: nc.MinDistance,
|
|
|
}))
|
|
|
),
|
|
|
[]
|
|
|
);
|
|
|
const WalkablePlaces = (c.Location?.WalkablePlaces?.Categories || []).reduce(
|
|
|
(r, nc) =>
|
|
|
r.concat(
|
|
|
locationPlacesMapper('WalkablePlaces', c.HotelId, nc.Places || [], lgcObj).map((item) => ({
|
|
|
...item,
|
|
|
category: nc.Name,
|
|
|
category_name: nc.Name,
|
|
|
}))
|
|
|
),
|
|
|
[]
|
|
|
);
|
|
|
rd.locations = rd.locations.concat(TopPlaces, Shops, Places, NearbyCategories, WalkablePlaces);
|
|
|
|
|
|
return rd;
|
|
|
},
|
|
|
{ info: [], info2: [], rooms: [], images: [], facility: [], infos: [], reviews: [], review_summaries: [], locations: [], test: [] }
|
|
|
);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 解析Availability数据
|
|
|
*/
|
|
|
|
|
|
/**
|
|
|
* 餐食类型 1明确数量(早中晚餐食看Breakfast Lunch Dinner 数量);2半包;3全包;4午/晚二选一;5早+ 午/晚二选一;
|
|
|
*/
|
|
|
const MealTypeMapped = {
|
|
|
'1': { value: 1, label: ({ Breakfast, Lunch, Dinner }) => `${Breakfast}份早餐` },
|
|
|
'2': { value: 2, label: ({ Breakfast, Lunch, Dinner }) => '半包' },
|
|
|
'3': { value: 3, label: ({ Breakfast, Lunch, Dinner }) => '全包' },
|
|
|
'4': { value: 4, label: ({ Breakfast, Lunch, Dinner }) => '午/晚二选一' },
|
|
|
'5': { value: 5, label: ({ Breakfast, Lunch, Dinner }) => '早+ 午/晚二选一' },
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* 付款方式 1现付 2预付
|
|
|
*/
|
|
|
const PayTypeMapped = {
|
|
|
'1': { value: 1, label: () => '现付' },
|
|
|
'2': { value: 2, label: () => '预付' },
|
|
|
};
|
|
|
/**
|
|
|
* 开票方式:0 Unknown 、1 提供方开票 、2 酒店开票、3 供应商开票
|
|
|
*/
|
|
|
const InvoiceTypeMapped = {
|
|
|
'0': { value: 0, label: () => '未知' },
|
|
|
'1': { value: 1, label: () => '提供方开票' },
|
|
|
'2': { value: 2, label: () => '酒店开票' },
|
|
|
'3': { value: 3, label: () => '供应商开票' },
|
|
|
};
|
|
|
/**
|
|
|
* 取消手续费类型:0 未知 1扣首日 2扣全额 3按价格多少百分比扣 4免费取消 5扣几晚 6扣多少钱
|
|
|
*/
|
|
|
const cancelDeductTypeMapped = {
|
|
|
'0': { value: 0, label: () => '未知' },
|
|
|
'1': { value: 1, label: () => '扣首日' },
|
|
|
'2': { value: 2, label: () => '扣全额' },
|
|
|
'3': { value: 3, label: (value) => `扣款${value * 100}%` },
|
|
|
'4': { value: 4, label: () => '免费取消' },
|
|
|
'5': { value: 5, label: (value) => `扣${value}晚` },
|
|
|
'6': { value: 6, label: (value) => `扣款${value}` },
|
|
|
};
|
|
|
/**
|
|
|
* 解析报价数据
|
|
|
*/
|
|
|
const resolveRatePlans = (plans) => {
|
|
|
const res = plans.map((room) => ({
|
|
|
...room,
|
|
|
RatePlans: room.RatePlans.map((rp) => {
|
|
|
const PriceUnit = rp.Dailys[0].Price;
|
|
|
const CancelableText = rp.Cancelable === true ? '限时取消' : '不可取消';
|
|
|
const CancelRulesMapped = (rp.CancelRules || []).map((citem) => {
|
|
|
let OutputText = isNotEmpty(citem.StartTime) ? `北京时间: ${citem.StartTime}至${citem.EndTime}, ` : '';
|
|
|
OutputText += cancelDeductTypeMapped[citem.DeductType].label(citem.DeductValue);
|
|
|
OutputText += citem.Desc;
|
|
|
return OutputText;
|
|
|
}); // 限时取消
|
|
|
rp.Cancelable === true ? CancelRulesMapped.push('其他时间不可取消') : false;
|
|
|
return { ...rp,
|
|
|
PriceUnit, PriceTotal: rp.Price,
|
|
|
CancelRulesText: CancelRulesMapped, CancelableText,
|
|
|
PayTypeText: PayTypeMapped[rp.PayType]?.label(),
|
|
|
};
|
|
|
}),
|
|
|
}));
|
|
|
return res;
|
|
|
};
|
|
|
|
|
|
module.exports = { resolveDetails, resolveRatePlans };
|