You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

351 lines
12 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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',
CountryCode: 'country_code',
CityId: 'city_id',
Address: 'address',
Phone: 'phone',
Latitude: 'latitude',
Longitude: 'longitude',
Rating: 'rating',
HotelType: 'hotel_type',
Brand: 'brand',
PostalCode: 'postal_code',
HeroImg: 'hero_img',
SupplierType: 'supplier_type',
});
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)) {
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 };
}),
}));
return res;
};
module.exports = { resolveDetails, resolveRatePlans };