define(function (require, exports, module) { var ajax = require("../platform/webRequest.js"); var param = require("../data.js"); var parser = require("../platform/parser.js"); var ev = require("../platform/EventObject.js"); var dynamicjs = require("../otn/dynamicjs.js"); var sessmgr = require("../account/sessionMgr.js"); var stationData = require("../station/station_data.js"); var utility = require('../utility.js'); var extensionVersion = parseInt(document.body.dataset["targetExtensionVersion"].replace(/\./g, "")); exports.events = new ev(); //最后二十次的查询平均时间 var lastQueryTimes = []; var averageQueryTime = null; var calculateQueryTime = function (time) { lastQueryTimes.push(time); while (lastQueryTimes.length > 20) lastQueryTimes.shift(); averageQueryTime = lastQueryTimes.length > 0 ? _.reduce(lastQueryTimes, function (x, y) { return x + y; }) / lastQueryTimes.length : 0; }; Object.defineProperty(exports, "averageQueryTime", { get: function () { return averageQueryTime; } }); //记录参数 var queryTicketUrl = null; var lastLogTime = null; var isLogOpen = false; var detectSuggest = function (result) { result.suggestTransit = result.available.length > 0 && result.available.length <= 3; var notFeCount = _.count(result.available, function (t) { return t.from.endpoint && t.to.endpoint; }); result.suggestMoreStat = notFeCount < 4 && notFeCount < result.available.length / 3; result.hasSuggest = result.suggestMoreStat | result.suggestTransit; }; var getTicketInfo = function (v) { var data = [], info = v.indexOf("#") === -1 ? v : /getSelected\(['"](.*?)['"]\)/i.exec(v)[1].split('#')[11], match = info.match(/([A-Z\d])0*?([\*\d]{5})0*?(\d{4})/gi); for (var j in match) { if (match.hasOwnProperty(j)) { var m = /([A-Z\d])0*?([\*\d]{5})0*?(\d{4})/i.exec(match[j]); var sc = m[1]; var sp = m[2][0] === '*' ? null : parseInt(m[2], 10); var st = parseInt(m[3], 10); var seat = { code: sc, name: param.toSeatTypeName(sc), price: sp, selected: false }; if (st < 3000) { seat.count = st; //TODO 二等软座提交是否和二等座一样都用的MO? //一等软座 7, 二等软座 8 if (sc === "7") { data.push({ code: "M", name: param.toSeatTypeName("M"), price: sp, count: st, selected: false }); } else if (sc === "8") { data.push({ code: "O", name: param.toSeatTypeName("O"), price: sp, count: st, selected: false }); } else { data.push(seat); } } else { seat.count = st - 3000; seat.code = '0'; seat.name = "无座"; data.push(seat); } } } ; return data; }; var transferData = function (query, data, noProcess, noAction) { var result = data; if (!Array.isArray(result.data) || !result.data.length || !result.data[0].queryLeftNewDTO) { exports.events.fireEvent("requestFailed"); } ; var date = query.date; var trainData = { query: query, raw: result, rawTrainMap: {}, trainMap: {}, auto: null, nextTime: null, noProcess: noProcess, noAction: noAction }; var messages = (result.messages || []).join(""); trainData.notInSellTime = messages.indexOf("在预售日期范围") !== -1; _.each(result.data, function (t) { var train = { id: t.queryLeftNewDTO.train_no, code: t.queryLeftNewDTO.station_train_code, available: t.queryLeftNewDTO.canWebBuy === 'Y' ? 1 : 0, start: { code: t.queryLeftNewDTO.start_station_telecode, name: t.queryLeftNewDTO.start_station_name }, from: { code: t.queryLeftNewDTO.from_station_telecode, fromStationNo: t.queryLeftNewDTO.from_station_no, name: t.queryLeftNewDTO.from_station_name, endpoint: t.queryLeftNewDTO.from_station_telecode === t.queryLeftNewDTO.start_station_telecode, time: t.queryLeftNewDTO.start_time }, to: { code: t.queryLeftNewDTO.to_station_telecode, toStationNo: t.queryLeftNewDTO.to_station_no, name: t.queryLeftNewDTO.to_station_name, endpoint: t.queryLeftNewDTO.end_station_telecode === t.queryLeftNewDTO.to_station_telecode, time: t.queryLeftNewDTO.arrive_time }, elapsedTime: { days: t.queryLeftNewDTO.day_difference, total: t.queryLeftNewDTO.lishi }, end: { code: t.queryLeftNewDTO.end_station_telecode, name: t.queryLeftNewDTO.end_station_name }, ypinfo: t.queryLeftNewDTO.yp_info, ypinfo_ex: t.queryLeftNewDTO.yp_ex, locationCode: t.queryLeftNewDTO.location_code, controlDay: t.queryLeftNewDTO.control_day, supportCard: t.queryLeftNewDTO.is_support_card, saleTime: t.queryLeftNewDTO.sale_time, secureStr: t.secretStr, selltime: null, date: date, limitSellInfo: t.buttonTextInfo && t.buttonTextInfo.indexOf("暂售") !== -1 ? t.buttonTextInfo.replace(/<[^>]+>/i, "") : null, controlFlag: parseInt(t.queryLeftNewDTO.controlled_train_flag || "0", 10), controlMessage: t.queryLeftNewDTO.controlled_train_message, maybeIllegal: !t.queryLeftNewDTO.controlled_train_flag }; var kp = getTicketInfo(t.queryLeftNewDTO.yp_info); train.tickets = kp; train.ticketMap = _.mapObject(kp, function (e) { return e.code; }); //起售时间 var selltimem = /(0*(\d+)月0*(\d+)日)?(\d+)\s*点\s*((\d+)分)?\s*起售/i.exec(t.buttonTextInfo.replace(/<.*?>/g, '')); if (selltimem) { train.available = -1; train.selltime = new Date(); train.selltime.setHours(parseInt(selltimem[4])); train.selltime.setMinutes(parseInt(selltimem[6] || "0", 10)); train.selltime.setSeconds(0); if (selltimem[1]) { //带日期 train.selltime.setDate(parseInt(selltimem[3])); train.selltime.setMonth(parseInt(selltimem[2]) - 1); if (train.selltime.getMonth() < new Date().getMonth()) { train.selltime.setFullYear(train.selltime.getFullYear() + 1); } } } trainData.rawTrainMap[train.id] = t; trainData.trainMap[train.id] = train; }); trainData.original = _.values(trainData.trainMap); trainData.available = trainData.original; trainData.trainCodes = _.pluck(trainData.original, "code"); trainData.filtered = []; trainData.include = trainData.available; if (!noProcess) { //执行车次预过滤。过滤内容:出发地,目标地,时间 exports.events.fireEvent("filterTrains", trainData); //处理车次 exports.events.fireEvent("processTrains", trainData); //过滤数据 if (trainData.filtered && trainData.filtered.length) { //从原始数据中删除 var orgMap = _.mapObject(result.data, function (e) { return e.queryLeftNewDTO.train_no; }); var filtered = _.map(trainData.filtered, function (t) { return orgMap[t.id]; }); trainData.rawDataFiltered = _.without.apply(null, [result.data].concat(filtered)); result.data = trainData.rawDataFiltered; } } //处理跨站购票预售提醒 _.each(trainData.available, function (t) { //非预售,或预售的非当天的,始发站的,则跳过 if (!t.selltime || Math.floor(new Date().getTime() / 86400000) !== Math.floor(t.selltime.getTime() / 86400000)) return; //相同的,跳过 if (t.start.code === t.from.code) return; //查询始发站起售时间 var firstStationSellTime = stationData.sellTime[t.start.code]; if (!firstStationSellTime) return; //判断始发站和当前站时间间隔。如果间隔超过指定的时间,则放弃建议 var spanSellCurrent = Math.floor((t.selltime.getTime() % 86400000) / 1000) + 8 * 3600; //需要加上时区 var spanSellNow = Math.floor((new Date().getTime() % 86400000) / 1000) + 8 * 3600; var spanSellStart = utility.toTimeSpan(firstStationSellTime, true); var timeDifference = Math.abs((spanSellCurrent - spanSellStart) / 60); var isEarly = spanSellStart < spanSellCurrent; var isStartInSell = spanSellNow >= spanSellStart; var tCode = t.code[0]; //if ( // (tCode === 'G' && timeDifference > 120) //高铁,超过2小时 // || // (tCode === 'D' && timeDifference > 180) //动车,超过3小时 // || // ((tCode === 'Z' || tCode === 'T') && timeDifference > 360) //直达/特快,超过6小时 // || // timeDifference > 600 //其它类型车次大于10小时 // ) // return; t.preStationInfo = ""; t.preStationInfo += "始发站【" + t.start.name + "】起售时间为【" + firstStationSellTime + "】," + (isStartInSell ? "已经在售" : isEarly ? "比当前车站起售时间早,可先去看看" : "可稍后留意"); t.preStationInfo += ""; }); detectSuggest(trainData); return trainData; }; exports.queryTicket = function (from, fromName, to, toName, date, student, resign, noProcess, noAction, byAuto) { var def = new $.Deferred(); var queryCount = 0; var queryLogSubmited = false; var queryStartTime, queryEndTime; def.fail(function () { exports.events.fireEvent("requestFailed"); }); //before query event. if (!noAction && !noProcess) { var evdata = { from: from, fromName: fromName, to: to, toName: toName, auto: byAuto || false }; exports.events.fireEvent("beforeQueryTicket", evdata); from = evdata.from; to = evdata.to; fromName = evdata.fromName; toName = evdata.toName; } var queryFail = function () { if (queryCount++ < 5) { def.notify({ action: "requery" }); //def.notify("12306出问题了,正在第 " + queryCount + " 次重连..."); setTimeout(sendQueryRequest, 2000); } else { def.reject("12306出问题了,请稍后重试..."); } }; var sendQueryRequest = function () { var p = { "leftTicketDTO.train_date": date, "leftTicketDTO.from_station": from, "leftTicketDTO.to_station": to, "purpose_codes": student ? "0X00" : "ADULT" }; var cookie = (function (obj) { var arr = []; _.forEach(obj, function (v, k) { arr.push(k + '=' + v); }); return arr.join("; "); })(extensionVersion >= 720 ? { "_jc_save_fromStation": escape(fromName + "," + from), "_jc_save_toStation": escape(toName + "," + to), "_jc_save_fromDate": date, "_jc_save_toDate": date, "_jc_save_wfdc_flag": resign ? "gc" : "dc" } : {}); if (isLogOpen) { exports.log(p, cookie); } queryStartTime = new Date().getTime(); ajax.sendGet(queryTicketUrl, "leftTicket/init", p, "json", function () { if (!this.model) { queryFail(); return; } //url changed? if (!this.model.status && this.model.c_url) { queryTicketUrl = this.model.c_url; sendQueryRequest(); } else if (!this.model.status) { var error = parser.getError(this.model); if (error && error.message && error.message.indexOf("查询失败") !== -1) { exports.log(p, cookie, true).always(queryFail); return; } queryFail(); } else { queryEndTime = new Date().getTime(); calculateQueryTime(queryEndTime - queryStartTime); var data = transferData({ from: from, fromName: fromName, to: to, toName: toName, auto: byAuto || false, date: date, resign: resign, student: student || false }, this.model, noProcess, noAction); def.resolveWith(data); } }, queryFail, false, { "Cookie": cookie, "If-Modified-Since": "0" }); }; var fetchTicketUrl = function () { ajax.sendGet("leftTicket/init", "/otn/", null, "text") .done(function () { var html = this.model; dynamicjs.checkDynamicJs(html, "leftTicket/init"); var r = /(CLeftTicketUrl|isSaveQueryLog)\s*=\s*['"]([^'"]+)['"]/gi; var m; while (m = r.exec(html)) { if (m[1] === "CLeftTicketUrl") queryTicketUrl = m[2]; else if (m[1] === "isSaveQueryLog") isLogOpen = m[2] === "Y"; } if (!queryTicketUrl) { document.dispatchEvent(new CustomEvent("platformError")); def.reject("未能初始化查询地址"); } else postFetchTicketUrl(); }).fail(function () { def.reject("网络错误"); }); }; var postFetchTicketUrl = function () { //加载查询页之后,必须要加载一次联系人,否则妥妥地出票失败。这傻逼逻辑啊。。 if (sessmgr.isLogined) { sessmgr.reloadPassengers(); sessmgr.getPassengers().always(sendQueryRequest); } else { sendQueryRequest(); } }; if (queryTicketUrl) { sendQueryRequest(); } else { fetchTicketUrl(); } return def.promise(); }; exports.log = function (data, cookies, force) { var def = new $.Deferred(); if (!isLogOpen || ((lastLogTime && (new Date() - lastLogTime) < 5000) && !force)) { def.reject(); return def.promise(); } lastLogTime = new Date(); ajax.sendGet("leftTicket/log", "leftTicket/init", data, "json", function () { def.resolve(); }, function () { def.reject(); }, false, { "Cookie": cookies }); return def.promise(); }; exports.queryTrainStop = function (id, from, to, date) { var data = { train_no: id, from_station_telecode: from, to_station_telecode: to, depart_date: date }; var def = new $.Deferred(); ajax.sendGet("czxx/queryByTrainNo", "leftTicket/init", data, "json", function (json) { if (!json || !json.data || !json.data.data) { def.reject((parser.getError(json) || { message: "查询失败" }).message); } else { def.resolve(json.data.data); } }, function () { def.reject("网络错误"); }); return def.promise(); }; });