server
parent
96fe313e96
commit
2f6fc68b76
@ -0,0 +1,28 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
/package-lock.json
|
||||||
|
|
||||||
|
temp
|
@ -0,0 +1,57 @@
|
|||||||
|
const Koa = require('koa')
|
||||||
|
const app = new Koa()
|
||||||
|
const views = require('koa-views')
|
||||||
|
const cors = require('koa2-cors')
|
||||||
|
const json = require('koa-json')
|
||||||
|
const onerror = require('koa-onerror')
|
||||||
|
const bodyparser = require('koa-bodyparser')
|
||||||
|
const logger = require('koa-logger')
|
||||||
|
|
||||||
|
const index = require('./routes/index')
|
||||||
|
|
||||||
|
const { Aids: syncAids, hotelLgcDetails: syncHotelDetails, chinaHotelDetails: syncChinas } = require('./jobs/syncHeytripJobs');
|
||||||
|
const rlog = require('./middleware/request_log');
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
onerror(app)
|
||||||
|
|
||||||
|
// middlewares
|
||||||
|
app.use(bodyparser({
|
||||||
|
enableTypes:['json', 'form', 'text']
|
||||||
|
}))
|
||||||
|
app.use(cors({
|
||||||
|
origin: function(ctx){ return '*' }
|
||||||
|
}))
|
||||||
|
app.use(json())
|
||||||
|
app.use(logger())
|
||||||
|
app.use(require('koa-static')(__dirname + '/public'))
|
||||||
|
|
||||||
|
app.use(views(__dirname + '/views', {
|
||||||
|
extension: 'ejs'
|
||||||
|
}))
|
||||||
|
|
||||||
|
app.use(rlog);
|
||||||
|
|
||||||
|
// logger
|
||||||
|
app.use(async (ctx, next) => {
|
||||||
|
const start = new Date()
|
||||||
|
await next()
|
||||||
|
const ms = new Date() - start
|
||||||
|
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// schedule jobs
|
||||||
|
// syncAids();
|
||||||
|
// syncHotelDetails();
|
||||||
|
// syncChinas();
|
||||||
|
|
||||||
|
// routes
|
||||||
|
app.use(index.routes(), index.allowedMethods())
|
||||||
|
// app.use(routes(), allowedMethods())
|
||||||
|
|
||||||
|
// error-handling
|
||||||
|
app.on('error', (err, ctx) => {
|
||||||
|
console.error('server error', err, ctx)
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app
|
@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var app = require('../app');
|
||||||
|
var debug = require('debug')('demo:server');
|
||||||
|
var http = require('http');
|
||||||
|
/**
|
||||||
|
* Get port from environment and store in Express.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var port = normalizePort(process.env.PORT || '3020');
|
||||||
|
// app.set('port', port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create HTTP server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var server = http.createServer(app.callback());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on provided port, on all network interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
server.on('error', onError);
|
||||||
|
server.on('listening', onListening);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a port into a number, string, or false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalizePort(val) {
|
||||||
|
var port = parseInt(val, 10);
|
||||||
|
|
||||||
|
if (isNaN(port)) {
|
||||||
|
// named pipe
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port >= 0) {
|
||||||
|
// port number
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "error" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
if (error.syscall !== 'listen') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bind = typeof port === 'string'
|
||||||
|
? 'Pipe ' + port
|
||||||
|
: 'Port ' + port;
|
||||||
|
|
||||||
|
// handle specific listen errors with friendly messages
|
||||||
|
switch (error.code) {
|
||||||
|
case 'EACCES':
|
||||||
|
console.error(bind + ' requires elevated privileges');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case 'EADDRINUSE':
|
||||||
|
console.error(bind + ' is already in use');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "listening" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onListening() {
|
||||||
|
var addr = server.address();
|
||||||
|
var bind = typeof addr === 'string'
|
||||||
|
? 'pipe ' + addr
|
||||||
|
: 'port ' + addr.port;
|
||||||
|
debug('Listening on ' + bind);
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
const HEYTRIP_API = 'http://distapi-sandbox.heytripgo.com/Accommodation';
|
||||||
|
const HEYTRIP_API_PROD = 'http://distapi.heytripgo.com/Accommodation';
|
||||||
|
const LGC_MAPPED = { '1': { 'lgc': '1', locale: 'en-US' }, '2': { 'lgc': '2', locale: 'zh-CN' } };
|
||||||
|
const DEFAULT_LGC = '1';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
HEYTRIP_API,
|
||||||
|
HEYTRIP_API_PROD,
|
||||||
|
LGC_MAPPED,
|
||||||
|
DEFAULT_LGC,
|
||||||
|
};
|
@ -0,0 +1,53 @@
|
|||||||
|
const { Sequelize, DataTypes, Op } = require('sequelize');
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 配置数据库
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const testDB = new Sequelize('hotel_hub', 'admin', 'admin', {
|
||||||
|
host: 'localhost',
|
||||||
|
dialect: 'mysql',
|
||||||
|
operatorsAliases: false,
|
||||||
|
dialectOptions: {
|
||||||
|
charset: 'utf8mb4',
|
||||||
|
collate: 'utf8mb4_unicode_ci',
|
||||||
|
supportBigNumbers: true,
|
||||||
|
bigNumberStrings: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
pool: {
|
||||||
|
max: 5,
|
||||||
|
min: 0,
|
||||||
|
acquire: 30000,
|
||||||
|
idle: 10000,
|
||||||
|
},
|
||||||
|
timezone: '+08:00', //东八时区
|
||||||
|
});
|
||||||
|
|
||||||
|
const DB = new Sequelize('hotel_hub_p', 'admin', 'admin', {
|
||||||
|
host: 'localhost',
|
||||||
|
dialect: 'mysql',
|
||||||
|
operatorsAliases: false,
|
||||||
|
dialectOptions: {
|
||||||
|
charset: 'utf8mb4',
|
||||||
|
collate: 'utf8mb4_unicode_ci',
|
||||||
|
supportBigNumbers: true,
|
||||||
|
bigNumberStrings: true,
|
||||||
|
},
|
||||||
|
// logging: false,
|
||||||
|
|
||||||
|
pool: {
|
||||||
|
max: 5,
|
||||||
|
min: 0,
|
||||||
|
acquire: 30000,
|
||||||
|
idle: 10000,
|
||||||
|
},
|
||||||
|
timezone: '+08:00', //东八时区
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
DataTypes, Op,
|
||||||
|
// sequelize: testDB,
|
||||||
|
sequelize: DB,
|
||||||
|
};
|
@ -0,0 +1,29 @@
|
|||||||
|
class BaseController {
|
||||||
|
response = (code = 200, msg = '', data = {}, page = 1, limit = 10) => {
|
||||||
|
const res = this.getPagingData(data, page, limit);
|
||||||
|
return {
|
||||||
|
'errcode': code,
|
||||||
|
'msg': msg,
|
||||||
|
'title': msg,
|
||||||
|
// 'data': data,
|
||||||
|
...res,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
getPagination = (page, size = 10) => {
|
||||||
|
const limit = size ? +size : 3;
|
||||||
|
const offset = page ? (page-1) * limit : 0;
|
||||||
|
|
||||||
|
return { limit, offset };
|
||||||
|
};
|
||||||
|
getPagingData = (_data, page, limit) => {
|
||||||
|
const { count: totalCount, rows: data } = _data;
|
||||||
|
if ( !data || !totalCount) {
|
||||||
|
return { data: _data };
|
||||||
|
}
|
||||||
|
const currentPage = page ? +page : 0;
|
||||||
|
const totalPages = Math.ceil(totalCount / limit);
|
||||||
|
|
||||||
|
return { totalCount, data, totalPages, currentPage };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
module.exports = BaseController;
|
@ -0,0 +1,31 @@
|
|||||||
|
const BaseController = require('./BaseController');
|
||||||
|
const heytripService = require('../services/heytripService');
|
||||||
|
|
||||||
|
class Heytrip extends BaseController {
|
||||||
|
|
||||||
|
getAids = async (ctx) => {
|
||||||
|
try {
|
||||||
|
const { nextPage } = await heytripService.syncAids();
|
||||||
|
ctx.body = this.response(0, 'get_heytrip_ids', nextPage);
|
||||||
|
} catch (error) {
|
||||||
|
// console.error(error);
|
||||||
|
ctx.body = this.response(1, error.message || 'An error occurred.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getHotelInfo = async (ctx) => {
|
||||||
|
const { data } = await heytripService.syncHotelDetails();
|
||||||
|
ctx.body = this.response(0, 'get_hotel_info', data);
|
||||||
|
};
|
||||||
|
|
||||||
|
getAvailability = async (ctx) => {
|
||||||
|
try {
|
||||||
|
const data = await heytripService.getHotelAvailability(ctx.query);
|
||||||
|
ctx.body = this.response(0, 'get_hotel_availability', data);
|
||||||
|
} catch (error) {
|
||||||
|
ctx.body = this.response(1, error.message || 'An error occurred.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new Heytrip();
|
@ -0,0 +1,46 @@
|
|||||||
|
const BaseController = require('./BaseController');
|
||||||
|
const heytripService = require('../services/heytripService');
|
||||||
|
const { QuotedHotelsPrice } = require('../vendor/heytrip');
|
||||||
|
|
||||||
|
class Api extends BaseController {
|
||||||
|
|
||||||
|
hotelSearch = async(ctx) => {
|
||||||
|
const { keyword, checkin, checkout } = ctx.query;
|
||||||
|
const page = ctx.query.page || 1;
|
||||||
|
const size = ctx.query.pagesize || 10;
|
||||||
|
try {
|
||||||
|
// if (!keyword || !checkin || !checkout) {
|
||||||
|
if (!keyword) {
|
||||||
|
ctx.throw(400, 'Missing required parameters`keyword`.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { limit, offset } = this.getPagination(page, size);
|
||||||
|
|
||||||
|
const { rows, count } = await heytripService.hotelSearch(keyword, { limit, offset });
|
||||||
|
|
||||||
|
let quoteRes = [];
|
||||||
|
|
||||||
|
if (checkin && checkout) {
|
||||||
|
const allIds = rows.map((item) => item.hotel_id);
|
||||||
|
quoteRes = await QuotedHotelsPrice({
|
||||||
|
hotelIds: allIds,
|
||||||
|
nationality: 'CN', // 默认取中国报价
|
||||||
|
CheckInDate: checkin,
|
||||||
|
CheckOutDate: checkout,
|
||||||
|
adultNum: 1,
|
||||||
|
roomCount: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const quoteMapped = quoteRes.reduce((r, c) => ({ ...r, [c.Id]: c }), {});
|
||||||
|
const res = rows.map((item) => ({ ...item, base_price: quoteMapped[item.hotel_id] || {} }));
|
||||||
|
|
||||||
|
ctx.body = this.response(0, 'hotel_search', { rows: res, count }, page, size);
|
||||||
|
} catch (error) {
|
||||||
|
// console.error(error);
|
||||||
|
ctx.statusCode = error.statusCode;
|
||||||
|
ctx.body = this.response(1, error.message || 'An error occurred.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = new Api();
|
@ -0,0 +1,41 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
var date = new Date();
|
||||||
|
// var dat= date.getDate()+"-"+(date.getMonth()+1)+"-"+date.getFullYear();
|
||||||
|
var dat= `${date.getFullYear()}-${(date.getMonth()+1)}-${date.getDate()}`;
|
||||||
|
|
||||||
|
var err_log = "./pm2/logs/err.log";
|
||||||
|
var out_log = "./pm2/logs/out.log";
|
||||||
|
|
||||||
|
fs.mkdirSync(`./pm2/logs/${dat}`, { recursive: true });
|
||||||
|
|
||||||
|
err_log = './pm2/logs/' + dat + '/error.log';
|
||||||
|
out_log = './pm2/logs/' + dat + '/output.log';
|
||||||
|
combined_log = './pm2/logs/' + dat + '/combined.log';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
apps: [
|
||||||
|
{
|
||||||
|
name: 'hotelhub',
|
||||||
|
script: 'bin/www',
|
||||||
|
max_memory_restart: "500M",
|
||||||
|
merge_logs: true,
|
||||||
|
max_restarts: 20,
|
||||||
|
error_file: err_log,
|
||||||
|
out_file: out_log,
|
||||||
|
// instances: 1,
|
||||||
|
watch: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
// export const apps = [{
|
||||||
|
// name: 'hotelhub',
|
||||||
|
// script: 'bin/www',
|
||||||
|
// max_memory_restart: "500M",
|
||||||
|
// merge_logs: true,
|
||||||
|
// max_restarts: 20,
|
||||||
|
// error_file: err_log,
|
||||||
|
// out_file: out_log,
|
||||||
|
// // instances: 1,
|
||||||
|
// watch: '.'
|
||||||
|
// }];
|
@ -0,0 +1,58 @@
|
|||||||
|
const { scheduleJob } = require('node-schedule');
|
||||||
|
const heytripService = require('../services/heytripService');
|
||||||
|
const { LGC_MAPPED } = require('../config/constants');
|
||||||
|
|
||||||
|
const Aids = () => {
|
||||||
|
const job = scheduleJob('*/2 * * * * *', async function () {
|
||||||
|
console.log('syncing heytrip, get available accommodation ids.');
|
||||||
|
|
||||||
|
const res = await heytripService.syncAids();
|
||||||
|
if (res.nextPage !== true) {
|
||||||
|
job.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const hotelLgcDetails = () => {
|
||||||
|
const job2 = scheduleJob('*/4 * * * * *', async function () {
|
||||||
|
console.log('-------------------------syncing heytrip, get accommodation details.-------------------------');
|
||||||
|
|
||||||
|
const isRunning = job2.pendingInvocations[0]?.job?.running == 1;
|
||||||
|
if (!isRunning) {
|
||||||
|
// const res = await heytripService.syncHotelDetails();
|
||||||
|
// const res = await heytripService.syncInitHotelLgcDetailsAction(LGC_MAPPED['1']);
|
||||||
|
const res = await heytripService.newHotelsLgc('1');
|
||||||
|
|
||||||
|
job2.cancel(); // debug: 0
|
||||||
|
if (res.next !== true) {
|
||||||
|
job2.cancel();
|
||||||
|
console.log('job completed! canceled job!');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('pre job running! cancelNext');
|
||||||
|
job2.cancelNext();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const chinaHotelDetails = () => {
|
||||||
|
const job3 = scheduleJob('*/4 * * * * *', async function () {
|
||||||
|
console.log('syncing heytrip, get china accommodation details.');
|
||||||
|
const isRunning = job3.pendingInvocations[0]?.job?.running == 1;
|
||||||
|
if (!isRunning) {
|
||||||
|
const res = await heytripService.chinaHotelsLgc2('2');
|
||||||
|
if (res.next !== true) {
|
||||||
|
job3.cancel();
|
||||||
|
console.log('job completed! canceled job!');
|
||||||
|
// job3.reschedule('0 0 0 * * *');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('pre job running! cancelNext');
|
||||||
|
job2.cancelNext();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Aids, hotelLgcDetails, chinaHotelDetails
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
const requestLogsService = require('./../services/requestLogsService');
|
||||||
|
const rlog = async (ctx, next) => {
|
||||||
|
try {
|
||||||
|
await next();
|
||||||
|
} catch (err) {
|
||||||
|
} finally {
|
||||||
|
await requestLogsService.create({
|
||||||
|
method: ctx.method,
|
||||||
|
path: ctx.method === 'GET' ? ctx.path : ctx.url,
|
||||||
|
request_data: ctx.method === 'GET' ? JSON.stringify(ctx.query) : JSON.stringify(ctx.request.body),
|
||||||
|
ip: ctx.ip,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
module.exports = rlog;
|
@ -0,0 +1,36 @@
|
|||||||
|
import Sequelize from 'sequelize';
|
||||||
|
export default function(sequelize, DataTypes) {
|
||||||
|
return sequelize.define('cache_availability', {
|
||||||
|
sn: {
|
||||||
|
autoIncrement: true,
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
hotel_id: {
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
tableName: 'cache_availability',
|
||||||
|
timestamps: false,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "PRIMARY",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "sn" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cache_availability_hotel_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "hotel_id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,56 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
return sequelize.define('city', {
|
||||||
|
sn: {
|
||||||
|
autoIncrement: true,
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: DataTypes.STRING(10),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
latitude: {
|
||||||
|
type: DataTypes.DOUBLE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
longitude: {
|
||||||
|
type: DataTypes.DOUBLE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
lgc: {
|
||||||
|
type: DataTypes.TINYINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
tableName: 'city',
|
||||||
|
timestamps: false,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "PRIMARY",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "sn" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "city_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,56 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
return sequelize.define('country', {
|
||||||
|
sn: {
|
||||||
|
autoIncrement: true,
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: DataTypes.STRING(10),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
latitude: {
|
||||||
|
type: DataTypes.DOUBLE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
longitude: {
|
||||||
|
type: DataTypes.DOUBLE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
lgc: {
|
||||||
|
type: DataTypes.TINYINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
tableName: 'country',
|
||||||
|
timestamps: false,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "PRIMARY",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "sn" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "countries_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,72 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
return sequelize.define('facility', {
|
||||||
|
sn: {
|
||||||
|
autoIncrement: true,
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
hotel_id: {
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
lgc: {
|
||||||
|
type: DataTypes.TINYINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
category_name: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
symbol: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
type: DataTypes.STRING(1000),
|
||||||
|
allowNull: true
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
tableName: 'facility',
|
||||||
|
timestamps: false,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "PRIMARY",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "sn" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "facility_hotel_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "hotel_id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,100 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
return sequelize.define('hotelinfo2', {
|
||||||
|
hi2_sn: {
|
||||||
|
autoIncrement: true,
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
hotel_id: {
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
lgc: {
|
||||||
|
type: DataTypes.TINYINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
hi2_hotel_name: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
hi2_address: {
|
||||||
|
type: DataTypes.STRING(1000),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
hi2_description: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
h2_instruction_desc: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
h2_instruction_special: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
h2_instruction_fees_desc: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
h2_location_walkable_places_title: {
|
||||||
|
type: DataTypes.STRING(1000),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
h2_location_walkable_places_desc: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
h2_location_highlight_desc: {
|
||||||
|
type: DataTypes.STRING(1000),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
h2_review_desc: {
|
||||||
|
type: DataTypes.STRING(1000),
|
||||||
|
allowNull: true
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
tableName: 'hotelinfo2',
|
||||||
|
timestamps: false,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "PRIMARY",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "hi2_sn" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hotelinfo2_unique",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "hotel_id" },
|
||||||
|
{ name: "lgc" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hotelinfo2_hotel_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "hotel_id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hotelinfo2_lgc_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "lgc" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,83 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
return sequelize.define('images', {
|
||||||
|
sn: {
|
||||||
|
autoIncrement: true,
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
hotel_id: {
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
info_source: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
info_source_id: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true,
|
||||||
|
comment: "Mid小图; Max大图;"
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: DataTypes.STRING(1000),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
caption: {
|
||||||
|
type: DataTypes.STRING(1000),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
lgc: {
|
||||||
|
type: DataTypes.TINYINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
tableName: 'images',
|
||||||
|
timestamps: false,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "PRIMARY",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "sn" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "image_urls_hotel_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "hotel_id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "image_urls_source_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "info_source_id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "images_lgc_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "lgc" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,64 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
return sequelize.define('informations', {
|
||||||
|
sn: {
|
||||||
|
autoIncrement: true,
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
hotel_id: {
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
lgc: {
|
||||||
|
type: DataTypes.TINYINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
category_name: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: DataTypes.STRING(1000),
|
||||||
|
allowNull: true
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
tableName: 'informations',
|
||||||
|
timestamps: false,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "PRIMARY",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "sn" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "facility_hotel_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "hotel_id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,49 @@
|
|||||||
|
var DataTypes = require("sequelize").DataTypes;
|
||||||
|
var _city = require("./city");
|
||||||
|
var _country = require("./country");
|
||||||
|
var _facility = require("./facility");
|
||||||
|
var _heytrip_ids = require("./heytrip_ids");
|
||||||
|
var _hotelinfo = require("./hotelinfo");
|
||||||
|
var _hotelinfo2 = require("./hotelinfo2");
|
||||||
|
var _images = require("./images");
|
||||||
|
var _informations = require("./informations");
|
||||||
|
var _locations = require("./locations");
|
||||||
|
var _request_logs = require("./request_logs");
|
||||||
|
var _reviews = require("./reviews");
|
||||||
|
var _reviews_summaries = require("./reviews_summaries");
|
||||||
|
var _rooms = require("./rooms");
|
||||||
|
|
||||||
|
function initModels(sequelize) {
|
||||||
|
var cityModel = _city(sequelize, DataTypes);
|
||||||
|
var countryModel = _country(sequelize, DataTypes);
|
||||||
|
var facilityModel = _facility(sequelize, DataTypes);
|
||||||
|
var heytripIdsModel = _heytrip_ids(sequelize, DataTypes);
|
||||||
|
var hotelinfoModel = _hotelinfo(sequelize, DataTypes);
|
||||||
|
var hotelinfo2Model = _hotelinfo2(sequelize, DataTypes);
|
||||||
|
var imagesModel = _images(sequelize, DataTypes);
|
||||||
|
var informationsModel = _informations(sequelize, DataTypes);
|
||||||
|
var locationsModel = _locations(sequelize, DataTypes);
|
||||||
|
var requestLogsModel = _request_logs(sequelize, DataTypes);
|
||||||
|
var reviewsModel = _reviews(sequelize, DataTypes);
|
||||||
|
var reviewsSummariesModel = _reviews_summaries(sequelize, DataTypes);
|
||||||
|
var roomsModel = _rooms(sequelize, DataTypes);
|
||||||
|
|
||||||
|
return {
|
||||||
|
cityModel,
|
||||||
|
countryModel,
|
||||||
|
facilityModel,
|
||||||
|
heytripIdsModel,
|
||||||
|
hotelinfoModel,
|
||||||
|
hotelinfo2Model,
|
||||||
|
imagesModel,
|
||||||
|
informationsModel,
|
||||||
|
locationsModel,
|
||||||
|
requestLogsModel,
|
||||||
|
reviewsModel,
|
||||||
|
reviewsSummariesModel,
|
||||||
|
roomsModel,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
module.exports = initModels;
|
||||||
|
module.exports.initModels = initModels;
|
||||||
|
module.exports.default = initModels;
|
@ -0,0 +1,54 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
return sequelize.define('request_logs', {
|
||||||
|
sn: {
|
||||||
|
autoIncrement: true,
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
method: {
|
||||||
|
type: DataTypes.STRING(15),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
type: DataTypes.STRING(1000),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
request_data: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
snapshots: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
ip: {
|
||||||
|
type: DataTypes.STRING(200),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
createtime: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: Sequelize.Sequelize.literal('CURRENT_TIMESTAMP')
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
tableName: 'request_logs',
|
||||||
|
timestamps: false,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "PRIMARY",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "sn" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,74 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
return sequelize.define('reviews', {
|
||||||
|
sn: {
|
||||||
|
autoIncrement: true,
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
hotel_id: {
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
lgc: {
|
||||||
|
type: DataTypes.TINYINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true,
|
||||||
|
comment: "ScoreDetails; Summaries; PositiveMentions"
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true,
|
||||||
|
comment: "种类 cleanliness(环境和清洁度) facilities(设施) location(位置) roomComfort(客房舒适度) staffPerformance(服务)valueForMoney(性价比)"
|
||||||
|
},
|
||||||
|
score: {
|
||||||
|
type: DataTypes.FLOAT,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
city_average: {
|
||||||
|
type: DataTypes.FLOAT,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
mention_name: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
mention_count: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
tableName: 'reviews',
|
||||||
|
timestamps: false,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "PRIMARY",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "sn" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "reviews_hotel_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "hotel_id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,64 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
return sequelize.define('reviews_summaries', {
|
||||||
|
sn: {
|
||||||
|
autoIncrement: true,
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
hotel_id: {
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
lgc: {
|
||||||
|
type: DataTypes.TINYINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
country: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
reviewer: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
review_rating: {
|
||||||
|
type: DataTypes.FLOAT,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
desc: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
review_date: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
tableName: 'reviews_summaries',
|
||||||
|
timestamps: false,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "PRIMARY",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "sn" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "reviews_hotel_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "hotel_id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,112 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
module.exports = function(sequelize, DataTypes) {
|
||||||
|
return sequelize.define('rooms', {
|
||||||
|
sn: {
|
||||||
|
autoIncrement: true,
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
hotel_id: {
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
lgc: {
|
||||||
|
type: DataTypes.TINYINT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
room_id: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
room_name: {
|
||||||
|
type: DataTypes.STRING(500),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
locale_name: {
|
||||||
|
type: DataTypes.STRING(500),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
bed_type_desc: {
|
||||||
|
type: DataTypes.STRING(1000),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
area: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
views: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
comment: "窗户信息 0未知 1有窗 2无窗 3部分有窗 4内窗 5封闭窗 6部分内窗"
|
||||||
|
},
|
||||||
|
floor: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
wireless_wideband: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
comment: "无线网络信息 0未知 1无 2免费 3收费 4部分收费 5部分有且收费 6部分有且免费"
|
||||||
|
},
|
||||||
|
wired_broadband: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
comment: "有线网络信息 0未知 1无 2免费 3收费 4部分收费 5部分有且收费 6部分有且免费"
|
||||||
|
},
|
||||||
|
smoking: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
comment: "吸烟信息 0未知 1禁烟 2部分禁烟 3可吸烟"
|
||||||
|
},
|
||||||
|
bathroom_type: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
comment: "卫浴信息 0未知 1独立卫浴 2公共卫浴"
|
||||||
|
},
|
||||||
|
max_occupancy: {
|
||||||
|
type: DataTypes.JSON,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
bedrooms: {
|
||||||
|
type: DataTypes.JSON,
|
||||||
|
allowNull: true
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
tableName: 'rooms',
|
||||||
|
timestamps: false,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: "PRIMARY",
|
||||||
|
unique: true,
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "sn" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rooms_room_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "room_id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rooms_hotel_id_IDX",
|
||||||
|
using: "BTREE",
|
||||||
|
fields: [
|
||||||
|
{ name: "hotel_id" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "HotelHub",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "node bin/www",
|
||||||
|
"dev": "nodemon bin/www",
|
||||||
|
"prd": "pm2 start bin/www",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.3",
|
||||||
|
"debug": "^4.1.1",
|
||||||
|
"ejs": "~2.3.3",
|
||||||
|
"koa": "^2.7.0",
|
||||||
|
"koa-bodyparser": "^4.2.1",
|
||||||
|
"koa-convert": "^1.2.0",
|
||||||
|
"koa-json": "^2.0.2",
|
||||||
|
"koa-logger": "^3.2.0",
|
||||||
|
"koa-onerror": "^4.1.0",
|
||||||
|
"koa-router": "^7.4.0",
|
||||||
|
"koa-static": "^5.0.0",
|
||||||
|
"koa-views": "^6.2.0",
|
||||||
|
"koa2-cors": "^2.0.6",
|
||||||
|
"mysql2": "^3.11.0",
|
||||||
|
"node-schedule": "^2.1.1",
|
||||||
|
"sequelize": "^6.37.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"nodemon": "^1.19.1"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
body {
|
||||||
|
padding: 50px;
|
||||||
|
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #00B7FF;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
const router = require('koa-router')();
|
||||||
|
const Heytrip = require('./../controllers/Heytrip');
|
||||||
|
const Api = require('./../controllers/Api');
|
||||||
|
|
||||||
|
// router.get('/get_heytrip_ids', Heytrip.getAids);
|
||||||
|
|
||||||
|
// router.get('/get_hotel_info', Heytrip.getHotelInfo);
|
||||||
|
|
||||||
|
router.get('/search_hotel', Api.hotelSearch);
|
||||||
|
|
||||||
|
router.get('/availability', Heytrip.getAvailability);
|
||||||
|
|
||||||
|
module.exports = router;
|
@ -0,0 +1,14 @@
|
|||||||
|
import Router from 'koa-router';
|
||||||
|
const router = new Router();
|
||||||
|
|
||||||
|
router.prefix('/users')
|
||||||
|
|
||||||
|
router.get('/', function (ctx, next) {
|
||||||
|
ctx.body = 'this is a users response!'
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/bar', function (ctx, next) {
|
||||||
|
ctx.body = 'this is a users/bar response'
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
@ -0,0 +1,307 @@
|
|||||||
|
const db = require('../config/db');
|
||||||
|
const initModels = require('./../models/init-models');
|
||||||
|
const { AvailableAccommodationIds, AccommodationsDetails, Availability } = require('../vendor/heytrip');
|
||||||
|
const { isEmpty } = require('../utils/commons');
|
||||||
|
const { resolveDetails, resolveRatePlans } = require('../helper/heytripDataHelper');
|
||||||
|
const { DEFAULT_LGC, LGC_MAPPED } = require('../config/constants');
|
||||||
|
|
||||||
|
const { sequelize: Sequelize, Op } = db;
|
||||||
|
|
||||||
|
const models = initModels(Sequelize);
|
||||||
|
|
||||||
|
const Country = models.countryModel;
|
||||||
|
const City = models.cityModel;
|
||||||
|
|
||||||
|
const HeytripIds = models.heytripIdsModel;
|
||||||
|
const Hotelinfo = models.hotelinfoModel;
|
||||||
|
const Hotelinfo2 = models.hotelinfo2Model;
|
||||||
|
const Rooms = models.roomsModel;
|
||||||
|
const Facility = models.facilityModel;
|
||||||
|
const Images = models.imagesModel;
|
||||||
|
const Informations = models.informationsModel;
|
||||||
|
const Reviews = models.reviewsModel;
|
||||||
|
const ReviewsSummaries = models.reviewsSummariesModel;
|
||||||
|
const Locations = models.locationsModel;
|
||||||
|
// const CacheAvailability = models.cacheAvailabilityModel;
|
||||||
|
|
||||||
|
// HeytripIds.hasMany(Hotelinfo, {
|
||||||
|
// foreignKey: 'hotel_id',
|
||||||
|
// onDelete: 'NO ACTION',
|
||||||
|
// onUpdate: 'NO ACTION',
|
||||||
|
// });
|
||||||
|
// Hotelinfo.belongsTo(HeytripIds, { as: 'aid', foreignKey: 'hotel_id', onDelete: 'NO ACTION', onUpdate: 'NO ACTION' });
|
||||||
|
// Hotelinfo.hasMany(Rooms, { sourceKey: 'hotel_id', foreignKey: 'hotel_id', onDelete: 'NO ACTION', onUpdate: 'NO ACTION' });
|
||||||
|
// Rooms.belongsTo(Hotelinfo, { targetKey: 'hotel_id', foreignKey: 'hotel_id', onDelete: 'NO ACTION', onUpdate: 'NO ACTION', });
|
||||||
|
Hotelinfo.hasMany(Hotelinfo2, { as: 'lgc_info', sourceKey: 'hotel_id', foreignKey: 'hotel_id', onDelete: 'NO ACTION', onUpdate: 'NO ACTION' });
|
||||||
|
Hotelinfo.hasMany(City, { as: 'city', sourceKey: 'city_id', foreignKey: 'id', onDelete: 'NO ACTION', onUpdate: 'NO ACTION', }); // 多语种, 所以实际是 hasMany , 用 hasOne 要指定 lgc= 1 或者2
|
||||||
|
Hotelinfo.hasMany(Country, { as: 'country', sourceKey: 'country_code', foreignKey: 'id', onDelete: 'NO ACTION', onUpdate: 'NO ACTION', }); // 多语种, 所以实际是 hasMany , 用 hasOne 要指定 lgc= 1 或者2
|
||||||
|
// Hotelinfo2.belongsTo(Hotelinfo, { targetKey: 'hotel_id', foreignKey: 'hotel_id', onDelete: 'NO ACTION', onUpdate: 'NO ACTION', });
|
||||||
|
|
||||||
|
class Heytrip {
|
||||||
|
/**
|
||||||
|
* 搜索酒店
|
||||||
|
*/
|
||||||
|
hotelSearch = async (keyword, options) => {
|
||||||
|
const keywordSplit = keyword.split(' ');
|
||||||
|
const keywordWhere = (field) => keywordSplit.map((word) => Sequelize.where(Sequelize.fn('instr', Sequelize.col(field), word), { [Op.gt]: 0 }));
|
||||||
|
// 'hotelinfo.address'
|
||||||
|
const keywordSearch = ['hotelinfo.hotel_name', ].map((field) => ({ [Op.and]: keywordWhere(field) }));
|
||||||
|
const keywordOrder = ['hotelinfo.hotel_name',].reduce((ro, field) => ro.concat(keywordSplit.map((word) => Sequelize.fn('instr', Sequelize.col(field), word))), []);
|
||||||
|
|
||||||
|
// const keywordSearchCount = ['hi2_hotel_name', 'hi2_address'].map((field) => ({ [Op.and]: keywordWhere(field) }));
|
||||||
|
// const countRows = await Hotelinfo2.count({
|
||||||
|
// where: {
|
||||||
|
// [Op.or]: keywordSearchCount,
|
||||||
|
// },
|
||||||
|
// // distinct: true,
|
||||||
|
// group: ['hotel_id'],
|
||||||
|
// });
|
||||||
|
// const count = countRows.length;
|
||||||
|
// const findIds = countRows.map((item) => item.hotel_id);
|
||||||
|
|
||||||
|
const { count, rows } = await Hotelinfo.findAndCountAll({
|
||||||
|
// const [ rows ] = await Hotelinfo.findAll({
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: Hotelinfo2,
|
||||||
|
as: 'lgc_info',
|
||||||
|
attributes: [
|
||||||
|
['hi2_sn', 'sn'],
|
||||||
|
'hotel_id',
|
||||||
|
'lgc',
|
||||||
|
'locale',
|
||||||
|
['hi2_hotel_name', 'hotel_name'],
|
||||||
|
['hi2_address', 'address'],
|
||||||
|
['hi2_description', 'description'],
|
||||||
|
['h2_review_desc', 'review_desc'],
|
||||||
|
['h2_location_highlight_desc', 'location_highlight_desc'],
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
separate: true,
|
||||||
|
},
|
||||||
|
{ model: City, as: 'city', attributes: ['id', 'name'], where: { lgc: 2 }, required: false, separate: true, },
|
||||||
|
{ model: Country, as: 'country', attributes: ['id', 'name'], where: { lgc: 2 }, required: false, separate: true, },
|
||||||
|
],
|
||||||
|
// include: [{ model: Hotelinfo2, as: 'h2', required: true }],
|
||||||
|
where: {
|
||||||
|
[Op.or]: keywordSearch,
|
||||||
|
// hotel_id: findIds,
|
||||||
|
},
|
||||||
|
order: keywordOrder,
|
||||||
|
...options,
|
||||||
|
// raw: true,
|
||||||
|
nest: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { count, rows: rows.map((item) => item.dataValues) };
|
||||||
|
// return { count, rows };
|
||||||
|
};
|
||||||
|
|
||||||
|
getLastPageIndex = async () => {
|
||||||
|
const ret = await HeytripIds.max('page_index');
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
syncAids = async () => {
|
||||||
|
const lastPageIndex = await this.getLastPageIndex();
|
||||||
|
const pageIndex = lastPageIndex + 1;
|
||||||
|
|
||||||
|
const ids = await AvailableAccommodationIds(pageIndex);
|
||||||
|
if (isEmpty(ids)) {
|
||||||
|
return {
|
||||||
|
nextPage: false,
|
||||||
|
pageIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const insertRows = ids.map((id) => ({ hotel_id: id, page_index: pageIndex }));
|
||||||
|
await HeytripIds.bulkCreate(insertRows);
|
||||||
|
|
||||||
|
return {
|
||||||
|
nextPage: true,
|
||||||
|
pageIndex,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
newHotels = async () => {
|
||||||
|
const [rows] = await Sequelize.query(
|
||||||
|
'SELECT i.hotel_id, IFNULL(h.hi_sn ,0) info_exists FROM heytrip_ids as i LEFT JOIN hotelinfo AS h ON h.hotel_id = i.hotel_id WHERE h.hi_sn IS NULL AND update_flag != 0 ORDER BY info_exists LIMIT 10'
|
||||||
|
);
|
||||||
|
return rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
newHotelsLgc = async (lgc) => {
|
||||||
|
// const [rows] = await Sequelize.query(
|
||||||
|
// `SELECT h.hotel_id
|
||||||
|
// FROM hotelinfo AS h
|
||||||
|
// LEFT JOIN hotelinfo2 AS h2 ON h.hotel_id =h2.hotel_id
|
||||||
|
// AND h2.lgc = ${lgc}
|
||||||
|
// WHERE h2.hi2_sn IS NULL LIMIT 10`
|
||||||
|
// );
|
||||||
|
const [rows] = await Sequelize.query(
|
||||||
|
`SELECT i.hotel_id ,IFNULL(h.hi2_sn, 0) info_exists
|
||||||
|
FROM heytrip_ids AS i
|
||||||
|
LEFT JOIN hotelinfo2 AS h ON h.hotel_id = i.hotel_id
|
||||||
|
AND h.lgc = ${lgc}
|
||||||
|
WHERE h.hi2_sn IS NULL
|
||||||
|
AND update_flag != 99
|
||||||
|
ORDER BY info_exists LIMIT 10`
|
||||||
|
);
|
||||||
|
const res = await this.syncInitHotelLgcDetailsAction(rows, LGC_MAPPED[lgc]);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
chinaHotels = async () => {
|
||||||
|
const [rows] = await Sequelize.query(
|
||||||
|
'SELECT i.hotel_id, IFNULL(h.hi_sn ,0) info_exists FROM heytrip_ids as i LEFT JOIN hotelinfo AS h ON h.hotel_id = i.hotel_id WHERE h.hi_sn IS NULL AND update_flag != 0 AND i.hotel_id > 20000000 ORDER BY info_exists LIMIT 10'
|
||||||
|
);
|
||||||
|
const res = await this.syncInitHotelDetailsAction(rows, LGC_MAPPED['2']);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
chinaHotelsLgc2 = async (lgc) => {
|
||||||
|
// const [rows] = await Sequelize.query(
|
||||||
|
// `SELECT h.hotel_id
|
||||||
|
// FROM hotelinfo AS h
|
||||||
|
// LEFT JOIN hotelinfo2 AS h2 ON h.hotel_id =h2.hotel_id
|
||||||
|
// AND h2.lgc = ${lgc}
|
||||||
|
// WHERE h2.hi2_sn IS NULL LIMIT 10`
|
||||||
|
// );
|
||||||
|
const [rows] = await Sequelize.query(
|
||||||
|
`SELECT i.hotel_id ,IFNULL(h.hi2_sn, 0) info_exists
|
||||||
|
FROM heytrip_ids AS i
|
||||||
|
INNER JOIN hotelinfo AS h1 ON h1.hotel_id =i.hotel_id
|
||||||
|
LEFT JOIN hotelinfo2 AS h ON h.hotel_id = i.hotel_id
|
||||||
|
AND h.lgc = ${lgc}
|
||||||
|
WHERE h.hi2_sn IS NULL
|
||||||
|
AND update_flag != 99
|
||||||
|
AND h1.country_code ='CN'
|
||||||
|
-- AND i.hotel_id > 20000000
|
||||||
|
ORDER BY info_exists LIMIT 10`
|
||||||
|
);
|
||||||
|
const res = await this.syncInitHotelLgcDetailsAction(rows, LGC_MAPPED[lgc]);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
syncInitHotelDetailsAction = async (rows, lgcObj = LGC_MAPPED[DEFAULT_LGC]) => {
|
||||||
|
let allIds = [];
|
||||||
|
try {
|
||||||
|
|
||||||
|
allIds = rows.map((item) => item.hotel_id);
|
||||||
|
const updateIds = rows.filter((item) => item.info_exists !== 0).map((item) => item.hotel_id);
|
||||||
|
const newIds = rows.filter((item) => item.info_exists === 0).map((item) => item.hotel_id);
|
||||||
|
|
||||||
|
if (isEmpty(rows)) {
|
||||||
|
return { next: !isEmpty(allIds), data: allIds };
|
||||||
|
}
|
||||||
|
const res = await AccommodationsDetails({
|
||||||
|
Language: lgcObj.locale,
|
||||||
|
AccommodationIds: allIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
const resIds = res.map((item) => item.HotelId);
|
||||||
|
// hotel info
|
||||||
|
const insertData = extractDetails(res, lgcObj);
|
||||||
|
// return insertData; // debug: 0
|
||||||
|
|
||||||
|
/** 开始Database */
|
||||||
|
const sequelizeOptions = { logging: false };
|
||||||
|
const result = await Sequelize.transaction(async t => {
|
||||||
|
let Info;
|
||||||
|
if ( ! isEmpty(insertData.info)) Info = await Hotelinfo.bulkCreate(insertData.info, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.info2)) await Hotelinfo2.bulkCreate(insertData.info2, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.rooms)) await Rooms.bulkCreate(insertData.rooms, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.images)) await Images.bulkCreate(insertData.images, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.facility)) await Facility.bulkCreate(insertData.facility, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.infos)) await Informations.bulkCreate(insertData.infos, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.reviews)) await Reviews.bulkCreate(insertData.reviews, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.reviews_summaries)) await ReviewsSummaries.bulkCreate(insertData.reviews_summaries, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.locations)) await Locations.bulkCreate(insertData.locations, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(allIds)) await HeytripIds.update({ update_flag: 0 }, { where: { hotel_id: allIds } });
|
||||||
|
return Info;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { next: !isEmpty(allIds), data: allIds };
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
|
||||||
|
return { next: false, data: allIds };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syncInitHotelLgcDetailsAction = async (rows, lgcObj = LGC_MAPPED[DEFAULT_LGC]) => {
|
||||||
|
let allIds = [];
|
||||||
|
try {
|
||||||
|
|
||||||
|
allIds = rows.map((item) => item.hotel_id);
|
||||||
|
if (isEmpty(rows)) {
|
||||||
|
return { next: !isEmpty(allIds), data: allIds };
|
||||||
|
}
|
||||||
|
const _BaseInfoExists = await Hotelinfo.findAll({ where: { hotel_id: allIds } });
|
||||||
|
const updateIds = _BaseInfoExists.map((item) => `${item.hotel_id}`);
|
||||||
|
console.log('updateIds', updateIds);
|
||||||
|
|
||||||
|
|
||||||
|
const res = await AccommodationsDetails({
|
||||||
|
Language: lgcObj.locale,
|
||||||
|
AccommodationIds: allIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
const resIds = res.map((item) => item.HotelId);
|
||||||
|
// hotel info
|
||||||
|
const insertData = resolveDetails(res, lgcObj);
|
||||||
|
// return insertData; // debug: 0
|
||||||
|
|
||||||
|
/** 开始Database */
|
||||||
|
const result = await Sequelize.transaction(async transaction => {
|
||||||
|
const sequelizeOptions = { logging: false, transaction };
|
||||||
|
let Info;
|
||||||
|
|
||||||
|
const newInfo = insertData.info.filter(iitem => !updateIds.includes(`${iitem.hotel_id}`));
|
||||||
|
console.log('newInfo', newInfo.map(xx => xx.hotel_id));
|
||||||
|
if ( ! isEmpty(newInfo)) Info = await Hotelinfo.bulkCreate(newInfo, sequelizeOptions);
|
||||||
|
|
||||||
|
if ( ! isEmpty(insertData.info2)) await Hotelinfo2.bulkCreate(insertData.info2, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.rooms)) await Rooms.bulkCreate(insertData.rooms, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.images)) await Images.bulkCreate(insertData.images, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.facility)) await Facility.bulkCreate(insertData.facility, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.infos)) await Informations.bulkCreate(insertData.infos, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.reviews)) await Reviews.bulkCreate(insertData.reviews, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.reviews_summaries)) await ReviewsSummaries.bulkCreate(insertData.reviews_summaries, sequelizeOptions);
|
||||||
|
if ( ! isEmpty(insertData.locations)) await Locations.bulkCreate(insertData.locations, sequelizeOptions);
|
||||||
|
|
||||||
|
if ( ! isEmpty(newInfo)) await HeytripIds.update({ update_flag: 0 }, { where: { hotel_id: newInfo.map(x => x.hotel_id) } });
|
||||||
|
|
||||||
|
return Info;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { next: !isEmpty(allIds), data: allIds };
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
|
||||||
|
return { next: false, data: allIds };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getHotelAvailability = async (param) => {
|
||||||
|
const { hotel_id, checkin, checkout, adults, children_ages, rooms, nationality } = param;
|
||||||
|
const paramBody = {
|
||||||
|
Language: 'en-US',
|
||||||
|
// Language: 'zh-CN',
|
||||||
|
AccommodationIds: [Number(hotel_id)],
|
||||||
|
CheckInDate: checkin,
|
||||||
|
CheckOutDate: checkout,
|
||||||
|
Nationality: nationality || 'CN', // 默认取中国报价
|
||||||
|
NumberOfAdults: adults || 1,
|
||||||
|
ChildrenAges: children_ages || null, // 入住每个儿童年龄 [6,8]
|
||||||
|
// ChildrenAges: null,
|
||||||
|
NumberOfRooms: rooms || 1,
|
||||||
|
Currency: 'CNY',
|
||||||
|
};
|
||||||
|
const _quoteRes = await Availability(paramBody);
|
||||||
|
const quoteRes = resolveRatePlans(_quoteRes);
|
||||||
|
return quoteRes;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
module.exports = new Heytrip();
|
@ -0,0 +1,17 @@
|
|||||||
|
const db = require('../config/db');
|
||||||
|
const initModels = require('./../models/init-models');
|
||||||
|
|
||||||
|
const Sequelize = db.sequelize;
|
||||||
|
const models = initModels(Sequelize);
|
||||||
|
|
||||||
|
const Logs = models.requestLogsModel;
|
||||||
|
|
||||||
|
// Logs.sync({ force: false });
|
||||||
|
|
||||||
|
class requestLogs {
|
||||||
|
static async create(data) {
|
||||||
|
return await Logs.create(data, { logging: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = requestLogs;
|
@ -0,0 +1,87 @@
|
|||||||
|
const crypto = require('crypto');
|
||||||
|
const axios = require('axios');
|
||||||
|
const { get, post } = axios;
|
||||||
|
|
||||||
|
const { HEYTRIP_API, HEYTRIP_API_PROD } = require('./../config/constants');
|
||||||
|
const { chunk, isEmpty } = require('../utils/commons');
|
||||||
|
|
||||||
|
const make_token = () => {
|
||||||
|
var apiKey = '18daad53d0ec4003a207c41ddaf63b78';
|
||||||
|
var secret = 'f76e547e55964812bf94cc0d31f74333';
|
||||||
|
var timestamp = Math.round(new Date().getTime() / 1000);
|
||||||
|
var hash = crypto
|
||||||
|
.createHash('sha512')
|
||||||
|
.update(apiKey + secret + timestamp)
|
||||||
|
.digest('hex');
|
||||||
|
var authHeaderValue = 'Bearer apikey=' + apiKey + ',signature=' + hash + ',timestamp=' + timestamp;
|
||||||
|
return authHeaderValue;
|
||||||
|
};
|
||||||
|
const AvailableAccommodationIds = async (pageIndex) => {
|
||||||
|
const response = await get(`${HEYTRIP_API_PROD}/AvailableAccommodationIds?pageIndex=${pageIndex}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: make_token(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('Call pageIndex', pageIndex, response.data.TotalPage);
|
||||||
|
return response.data.Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AccommodationsDetails = async (body) => {
|
||||||
|
if (isEmpty(body.AccommodationIds)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const response = await post(
|
||||||
|
`${HEYTRIP_API_PROD}/AccommodationsDetails`,
|
||||||
|
{ ...body },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: make_token(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return Object.values(response.data.Data || {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const Availability = async (body) => {
|
||||||
|
// console.log('Call Heytrip');
|
||||||
|
const response = await post(
|
||||||
|
`${HEYTRIP_API_PROD}/Availability`,
|
||||||
|
{ ...body },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: make_token(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// console.log(response.config);
|
||||||
|
return response.data.Data || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const QuotedHotelsPrice = async (body) => {
|
||||||
|
const idsChunk = body.hotelIds.length > 10 ? chunk(body.hotelIds, 10) : [body.hotelIds];
|
||||||
|
let quoteRes = [];
|
||||||
|
for await (const piece of idsChunk) {
|
||||||
|
const response = await post(
|
||||||
|
`${HEYTRIP_API_PROD}/QuotedHotelsPrice`,
|
||||||
|
{ ...body, hotelIds: piece },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: make_token(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
quoteRes = quoteRes.concat(response.data.Data || []);
|
||||||
|
}
|
||||||
|
return quoteRes;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Country = async (param) => {};
|
||||||
|
|
||||||
|
const City = async (param) => {};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
AvailableAccommodationIds,
|
||||||
|
AccommodationsDetails,
|
||||||
|
Availability,
|
||||||
|
QuotedHotelsPrice,
|
||||||
|
};
|
@ -0,0 +1,3 @@
|
|||||||
|
<h1><%= message %></h1>
|
||||||
|
<h2><%= error.status %></h2>
|
||||||
|
<pre><%= error.stack %></pre>
|
@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= title %></title>
|
||||||
|
<link rel='stylesheet' href='/stylesheets/style.css' />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1><%= title %></h1>
|
||||||
|
<p>EJS Welcome to <%= title %></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue