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 };