sync commit

This commit is contained in:
iFish 2014-08-27 23:05:50 +08:00
parent b19d9012a6
commit 42b83a3035
17 changed files with 693 additions and 211 deletions

View File

@ -1438,7 +1438,7 @@ window.cbl = function (u, h) {
tasks.sort(function (x, y) {
return x.time - y.time;
});
saveAlarm();
if (!checkTimer)
checkForAlarm();
});

View File

@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
//
// 你可以指定所有值,也可以让修订版本和内部版本号采用默认值,
// 方法是按如下所示使用 "*":
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.0.14239.30")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -4,7 +4,9 @@ using System.Drawing;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace Web12306
{
@ -27,9 +29,22 @@ namespace Web12306
{
var ri = JsonConvert.DeserializeObject<RequestInfo>(data);
var suggestion = GetSuggestionResponseContent(ri);
var callback = request.QueryString["calllback"];
var origin = request.Headers["Origin"];
context.Response.ContentType = "application/json";
context.Response.Write(JsonConvert.SerializeObject(suggestion));
if (!string.IsNullOrEmpty(origin))
context.Response.AddHeader("Access-Control-Allow-Origin", origin);
context.Response.ContentType = string.IsNullOrEmpty(callback) ? "application/json" : "application/javascript";
context.Response.BufferOutput = false;
if (!string.IsNullOrEmpty(callback))
{
context.Response.Write(callback + "(");
}
context.Response.Write(suggestion);
if (!string.IsNullOrEmpty(callback))
{
context.Response.Write(callback + ");");
}
}
catch (Exception ex)
{
@ -49,26 +64,322 @@ namespace Web12306
#region
static Dictionary<char, int> _timeRangeLimit = new Dictionary<char, int>()
{
{'D', 240},
{'G', 180},
{'*', 300}
};
static JsonSerializerSettings _camelJsonSetting = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
int GetStopTime(string stopTimeStr)
string GetSuggestionResponseContent(RequestInfo ri)
{
if (ri.CheckIllegal())
return string.Empty;
return GetSuggestionResponseContentCore(ri);
}
string GetSuggestionResponseContentCore(RequestInfo ri)
{
//获得所有的可替换站点
var alllines = ri.Stops.Values.SelectMany(s => GetAlternativeLines(s)).ToArray();
//对线路进行分组排序
var lineGrouped = alllines.GroupBy(s => s.LineName).Select(s => new KeyValuePair<string, List<AlternativeLine>>(s.Key, s.ToList())).ToList();
//过滤
FilteAlternativeLine(ri, lineGrouped);
//填充数据
FillExtraInfo(ri, lineGrouped);
//排序
SortLineRecommand(ri, lineGrouped);
return JsonConvert.SerializeObject(lineGrouped, Formatting.None, _camelJsonSetting);
}
void FilteAlternativeLine(RequestInfo ri, List<KeyValuePair<string, List<AlternativeLine>>> line)
{
for (var i = line.Count - 1; i >= 0; i--)
{
var train = ri.Stops[line[i].Value[0].TrainCode];
var maxAllowElapsedTime = train.TrainInfo.elapsedTime.Value.Add(train.TrainInfo.elapsedTime.MaxAddTime);
for (var j = line[i].Value.Count - 1; j >= 0; j--)
{
var current = line[i].Value[j];
//如果同时是发站和到站,则删除不推荐
if (current.From.IsOriginal && current.To.IsOriginal)
line[i].Value.RemoveAt(j);
//如果时间会超过原来最大的允许值,则排除
else if (current.ElapsedTime > maxAllowElapsedTime)
line[i].Value.RemoveAt(j);
}
if (line[i].Value.Count == 0)
{
line.RemoveAt(i);
}
}
}
/// <summary>
/// 根据停靠站获得权重
/// </summary>
/// <param name="lineList"></param>
/// <returns></returns>
int GetLineStationEndpointWeight(List<AlternativeLine> lineList)
{
var point = 0;
foreach (var line in lineList)
{
if (line.From.IsEndPoint && line.To.IsEndPoint)
point += 2;
else if (line.From.IsEndPoint || line.To.IsEndPoint)
point += 1;
}
return point;
}
void SortLineRecommand(RequestInfo ri, List<KeyValuePair<string, List<AlternativeLine>>> line)
{
line.Sort((x, y) =>
{
var ceo1 = GetLineStationEndpointWeight(x.Value);
var ceo2 = GetLineStationEndpointWeight(y.Value);
if (ceo1 != ceo2)
return ceo2 - ceo1;
if (x.Value.Count != y.Value.Count)
return y.Value.Count - x.Value.Count;
//再看停靠时间
var ep1 = (int)x.Value.Min(s => s.From.StopTime + s.To.StopTime).TotalSeconds;
var ep2 = (int)y.Value.Min(s => s.From.StopTime + s.To.StopTime).TotalSeconds;
if (ep1 != ep2)
return ep2 - ep1;
//再看价格
var ap1 = (int)x.Value.Min(s => s.ExtraPrice);
var ap2 = (int)y.Value.Min(s => s.ExtraPrice);
if (ap1 != ap2)
return ap1 - ap2;
return 0;
});
//再对组内排序
foreach (var current in line)
{
current.Value.Sort((x, y) =>
{
//再看停靠时间
var ep1 = (int)(x.From.StopTime + x.To.StopTime).TotalSeconds;
var ep2 = (int)(y.From.StopTime + y.To.StopTime).TotalSeconds;
if (ep1 != ep2)
return ep2 - ep1;
//再看价格
var ap1 = (int)x.ExtraPrice;
var ap2 = (int)y.ExtraPrice;
if (ap1 != ap2)
return ap1 - ap2;
return 0;
});
}
}
void FillExtraInfo(RequestInfo ri, List<KeyValuePair<string, List<AlternativeLine>>> line)
{
for (var i = line.Count - 1; i >= 0; i--)
{
var train = ri.Stops[line[i].Value[0].TrainCode];
var originalTime = train.TrainInfo.elapsedTime.Value;
for (var j = line[i].Value.Count - 1; j >= 0; j--)
{
var current = line[i].Value[j];
//如果时间会超过原来最大的允许值,则排除
var priceExtraRadio = ((current.ElapsedTime - originalTime).TotalSeconds / (1.0 * originalTime.TotalSeconds));
current.BasePriceSeat = Cn12306SuggestionUtility.BaseSeatCodes.First(s => train.TrainInfo.ticketMap.ContainsKey(s));
current.BasePrice = train.TrainInfo.ticketMap[current.BasePriceSeat].price / 10.0;
current.ExtraPrice = Math.Round(priceExtraRadio * current.BasePrice, 2);
}
}
}
List<AlternativeLine> GetAlternativeLines(TrainInfoItem train)
{
var info = train.TrainInfo;
var stops = train.StopInfos;
var indexFrom = Array.FindIndex(stops, s => s.station_name == info.from.name);
var indexTo = Array.FindIndex(stops, indexFrom + 1, s => s.station_name == info.to.name);
if (indexFrom == -1 || indexTo == -1)
return null;
var altFrom = new List<AlternativeStation>();
var altTo = new List<AlternativeStation>();
var maxAddTime = train.TrainInfo.elapsedTime.MaxAddTime.TotalMinutes;
if (indexFrom != 0)
{
var stopTimeFrom = Cn12306SuggestionUtility.GetStopTimeSpan(stops[indexFrom].stopover_time);
for (var i = 0; i < indexFrom; i++)
{
//如果isEnabled为true则说明会停靠这个站点。不能推荐更短的。
//如果时间过久,则不做推荐
if (stops[i].isEnabled || Cn12306SuggestionUtility.GetTimeSpace(stops[i].start_time, info.from.time) > maxAddTime)
continue;
var stopTime = i == 0 ? TimeSpan.Zero : Cn12306SuggestionUtility.GetStopTimeSpan(stops[i].stopover_time);
if (i != 0)
{
//对于非始发站,停靠时间相同或更短,则不做推荐。
if (stopTime <= stopTimeFrom)
continue;
}
//添加到推荐列表中
altFrom.Add(new AlternativeStation()
{
ArriveTime = Cn12306SuggestionUtility.GetTimeValueSpan(i == 0 ? stops[i].start_time : stops[i].arrive_time),
IsEndPoint = i == 0,
IsFromStation = true,
Name = stops[i].station_name,
StopTime = stopTime,
TrainCode = info.code
});
}
//排序
altFrom.Sort(new AlternativeStationComparer());
if (altFrom.Count > 3)
altFrom.RemoveRange(altFrom.Count - 3, 3);
}
if (indexTo != stops.Length - 1)
{
var stopTimeTo = Cn12306SuggestionUtility.GetStopTimeSpan(stops[indexTo].stopover_time);
for (var i = stops.Length - 1; i >= indexTo; i--)
{
//如果isEnabled为true则说明会停靠这个站点。不能推荐更短的。
//如果时间过久,则不做推荐
if (stops[i].isEnabled || Cn12306SuggestionUtility.GetTimeSpace(info.to.time, stops[i].arrive_time) > maxAddTime)
continue;
var stopTime = i == 0 ? TimeSpan.Zero : Cn12306SuggestionUtility.GetStopTimeSpan(stops[i].stopover_time);
if (i != stops.Length - 1)
{
//对于非终到站,停靠时间相同或更短,则不做推荐。
if (stopTime <= stopTimeTo)
continue;
}
//添加到推荐列表中
altTo.Add(new AlternativeStation()
{
ArriveTime = Cn12306SuggestionUtility.GetTimeValueSpan(stops[i].arrive_time),
IsEndPoint = i == stops.Length - 1,
IsFromStation = false,
Name = stops[i].station_name,
StopTime = stopTime,
TrainCode = info.code
});
}
altTo.Sort(new AlternativeStationComparer());
if (altTo.Count > 3)
altTo.RemoveRange(altFrom.Count - 3, 3);
}
//plus 原始线路
altFrom.Add(new AlternativeStation
{
ArriveTime = Cn12306SuggestionUtility.GetTimeValueSpan(indexFrom == 0 ? stops[indexFrom].start_time : stops[indexFrom].arrive_time),
IsEndPoint = indexFrom == 0,
IsFromStation = true,
Name = stops[indexFrom].station_name,
StopTime = indexFrom == 0 ? TimeSpan.Zero : Cn12306SuggestionUtility.GetStopTimeSpan(stops[indexFrom].stopover_time),
TrainCode = info.code,
IsOriginal = true
});
altTo.Add(new AlternativeStation
{
ArriveTime = Cn12306SuggestionUtility.GetTimeValueSpan(stops[indexTo].arrive_time),
IsEndPoint = indexTo == stops.Length - 1,
IsFromStation = false,
Name = stops[indexTo].station_name,
StopTime = indexTo == stops.Length - 1 ? TimeSpan.Zero : Cn12306SuggestionUtility.GetStopTimeSpan(stops[indexFrom].stopover_time),
TrainCode = info.code,
IsOriginal = true
});
var lines = altFrom.CrossJoin(altTo).Where(s => !s.Key.IsOriginal || !s.Value.IsOriginal).Select(s => new AlternativeLine(s.Key, s.Value) { TrainCode = s.Key.TrainCode }).ToList();
return lines;
}
#endregion
}
public static class Cn12306SuggestionUtility
{
public static double TimeRangeLimitRadio = double.Parse(System.Configuration.ConfigurationManager.AppSettings["12306_trainsuggestion_maxradio"]);
public static readonly char[] BaseSeatCodes = "O219PM6430".ToArray();
public static TimeSpan GetTimeSpace(TimeSpan t1, TimeSpan t2)
{
var t = t2 - t1;
if (t.Ticks < 0)
t = t.Add(new TimeSpan(1, 0, 0, 0));
return t;
}
/// <summary>
/// 生成两个序列的交叉
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t1"></param>
/// <param name="t2"></param>
/// <returns></returns>
public static IEnumerable<KeyValuePair<T, T>> CrossJoin<T>(this IEnumerable<T> t1, IEnumerable<T> t2)
{
foreach (var t in t1)
{
foreach (var tt in t2)
{
yield return new KeyValuePair<T, T>(t, tt);
}
}
}
public static int GetStopTime(string stopTimeStr)
{
var m = Regex.Match(stopTimeStr, @"(\d+)分钟");
return m.Success ? int.Parse(m.Groups[1].Value) : 0;
}
int GetTimeValue(string time)
public static TimeSpan GetStopTimeSpan(string stopTimeStr)
{
var m = Regex.Match(stopTimeStr, @"(\d+)分钟");
return m.Success ? new TimeSpan(0, int.Parse(m.Groups[1].Value), 0) : TimeSpan.Zero;
}
public static int GetTimeValue(string time)
{
var m = Regex.Match(time, @"0?(\d+):0?(\d+)");
return m.Success ? int.Parse(m.Groups[1].Value) * 60 + int.Parse(m.Groups[2].Value) : 0;
}
public static TimeSpan GetTimeValueSpan(string time)
{
var m = Regex.Match(time, @"0?(\d+):0?(\d+)");
return m.Success ? new TimeSpan(int.Parse(m.Groups[1].Value), int.Parse(m.Groups[2].Value), 0) : TimeSpan.Zero;
}
int GetTimeSpace(string time1, string time2)
public static int GetTimeSpace(string time1, string time2)
{
var x1 = GetTimeValue(time1);
var x2 = GetTimeValue(time2);
@ -76,21 +387,94 @@ namespace Web12306
return x1 <= x2 ? x2 - x1 : x2 + 24 * 60 - x1;
}
string GetSuggestionResponseContent(RequestInfo ri)
}
public class AlternativeStation
{
public string TrainCode { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public bool IsFromStation { get; set; }
public bool IsEndPoint { get; set; }
public TimeSpan ArriveTime { get; set; }
public TimeSpan StopTime { get; set; }
public bool IsOriginal { get; set; }
}
public class AlternativeLine
{
public AlternativeStation From { get; private set; }
public AlternativeStation To { get; private set; }
public string LineName { get; private set; }
public string TrainCode { get; set; }
TimeSpan? _elTimeSpan;
public double ExtraPrice { get; set; }
public char BasePriceSeat { get; set; }
public double BasePrice { get; set; }
public TimeSpan ElapsedTime
{
if (ri.CheckIllegal())
return string.Empty;
get
{
if (!_elTimeSpan.HasValue)
{
_elTimeSpan = To.ArriveTime - From.ArriveTime;
if (_elTimeSpan.Value.Ticks < 0)
_elTimeSpan = _elTimeSpan.Value.Add(new TimeSpan(1, 0, 0, 0));
}
return string.Empty;
return _elTimeSpan.Value;
}
}
/// <summary>
/// 创建 <see cref="AlternativeLine" /> 的新实例(AlternativeLine)
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
public AlternativeLine(AlternativeStation from, AlternativeStation to)
{
From = from;
To = to;
LineName = From.Name + "-" + To.Name;
}
}
public class AlternativeStationComparer : IComparer<AlternativeStation>
{
#region Implementation of IComparer<in AlternativeStation>
/// <summary>
/// 比较两个对象并返回一个值,指示一个对象是小于、等于还是大于另一个对象。
/// </summary>
/// <returns>
/// 一个有符号整数,指示 <paramref name="x"/> 与 <paramref name="y"/> 的相对值,如下表所示。 值 含义 小于零 <paramref name="x"/> 小于 <paramref name="y"/>。 零 <paramref name="x"/> 等于 <paramref name="y"/>。 大于零 <paramref name="x"/> 大于 <paramref name="y"/>。
/// </returns>
/// <param name="x">要比较的第一个对象。</param><param name="y">要比较的第二个对象。</param>
public int Compare(AlternativeStation x, AlternativeStation y)
{
if (x.IsEndPoint ^ y.IsEndPoint)
return x.IsEndPoint ? -1 : 1;
return x.StopTime < y.StopTime ? -1 : 1;
}
#endregion
}
public class SuggestItemComparer : IComparer<SuggestItem>
@ -132,11 +516,15 @@ namespace Web12306
public class RequestInfo
{
public string Key { get; set; }
public string From { get; set; }
public string To { get; set; }
public Dictionary<string, TrainInfo> Stops { get; set; }
public string Date { get; set; }
public Dictionary<string, TrainInfoItem> Stops { get; set; }
/// <summary>
/// 检测是否有非法请求有则返回true
@ -144,13 +532,23 @@ namespace Web12306
/// <returns></returns>
public bool CheckIllegal()
{
if (Stops.Values.Any(s => s.from.code == s.start.code && s.to.code == s.end.code))
if (Key != "stupid360")
return true;
if (Stops.Values.Any(s => (s.TrainInfo.code == s.TrainInfo.from.code && s.TrainInfo.to.code == s.TrainInfo.end.code) || s.TrainInfo.available < 0))
return true;
return false;
}
}
public class TrainInfoItem
{
[JsonProperty("info")]
public TrainInfo TrainInfo { get; set; }
[JsonProperty("stops")]
public StopInfo[] StopInfos { get; set; }
}
public class StopInfo
{
@ -208,10 +606,13 @@ namespace Web12306
public string supportCard { get; set; }
public string saleTime { get; set; }
public string secureStr { get; set; }
public object selltime { get; set; }
public DateTime? selltime { get; set; }
public string date { get; set; }
public object limitSellInfo { get; set; }
public string limitSellInfo { get; set; }
public SeatTicketInfo[] tickets { get; set; }
public Dictionary<char, SeatTicketInfo> ticketMap { get; set; }
}
@ -220,6 +621,39 @@ namespace Web12306
{
public string days { get; set; }
public string total { get; set; }
TimeSpan? _elTimeSpan;
[JsonIgnore]
public TimeSpan Value
{
get
{
if (_elTimeSpan == null)
{
_elTimeSpan = Cn12306SuggestionUtility.GetTimeValueSpan(total);
}
return _elTimeSpan.Value;
}
}
TimeSpan? _maxAddTime;
/// <summary>
/// 最大允许加时
/// </summary>
[JsonIgnore]
public TimeSpan MaxAddTime
{
get
{
if (_maxAddTime == null)
{
_maxAddTime = new TimeSpan(0, 0, 0, (int)(Value.TotalSeconds * Cn12306SuggestionUtility.TimeRangeLimitRadio));
}
return _maxAddTime.Value;
}
}
}

View File

@ -9,6 +9,8 @@
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="12306_trainsuggestion_maxradio" value="0.7"/>
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
@ -20,7 +22,9 @@
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="ChatServersHandler" path="ChatHandler.ashx" verb="*" type="Web12306.ChatServers" />
<add name="TrainSuggestHandler" path="GetSuggestion.ashx" verb="*" type="Web12306.TrainSuggestion" />
</handlers>
</system.webServer>
<runtime>

View File

@ -112,9 +112,6 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.Formatting">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.0.0\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference>
@ -235,7 +232,9 @@
<Content Include="js\ui\widget_verifycode.js" />
<None Include="Scripts\_references.js" />
<Content Include="js\utility.js" />
<Content Include="Web.config" />
<Content Include="Web.config">
<SubType>Designer</SubType>
</Content>
<Content Include="Web.Debug.config">
<DependentUpon>Web.config</DependentUpon>
</Content>

View File

@ -60,7 +60,7 @@
}
.result .train-seats {
padding: 10px 0 5px 0;
padding: 6px 0 1px 0;
border: 1px solid #cccccc;
border-left: none;
line-height: 18px;
@ -84,7 +84,6 @@
}
.result .train-seats .row2 {
margin-top: 5px;
}
.result .train-info {
@ -107,7 +106,7 @@
border: 1px solid rgba(0, 0, 0, 0.2);
background: linear-gradient(to bottom, #ffffff, #ffffff 5%, #f5f5f5);
border-radius: 4px;
padding: 11px 16px;
padding: 6px 10px;
color: #4e4e4e;
box-shadow: 0 1px 8px rgba(155, 155, 155, 0.2);
width: 105px;

View File

@ -23,22 +23,26 @@
line-height: 200%;
}
#book_sell_tip > div span {
color: #D1455B;
}
#book_sell_tip > div time {
font-weight: bold;
color: #D1455B;
}
#book_sell_tip > div address {
display: inline;
font-style: normal;
#book_sell_tip > div span {
font-weight: bold;
color: #D1455B;
}
#book_sell_tip p:first-child {
font-size: 110%;
}
#book_sell_tip > footer {
padding-left: 20px;
}
#book_sell_tip a.act-openalarm {
font-weight: bold;
color: #D1455B;
text-decoration: underline;
}

View File

@ -185,15 +185,11 @@
</a>
</header>
<div>
您选择的日期 <time></time> 车票不在预售期内,无法查询普通客票。
<br />
<time></time><address></address> 车票起售时间为 <time></time>(全国动车票为<time>上午11:00</time>、高铁车票为<time>下午2:00</time>)。
<br />
高峰期预售期可能有变动或增开临客因此预售期会有变动详情请密切留意12306公告。
<span>需要在起售前提醒您吗?</span>
<p><time></time><span></span> 起售日期为<time></time><span>普通车次</span><time></time><span>动车/城铁</span><time>上午11:00</time><span>高铁</span><time>下午2:00</time>),建议<a href="javascript:;" class="act-openalarm">开启提醒</a></p>
<p>高峰期预售期可能有变动或增开临客因此预售期会有变动详情请密切留意12306公告。</p>
</div>
<footer>
<button class="button button-primary button-mini">
<button class="button button-primary button-mini act-openalarm">
<i class="fa fa-check"></i>
开启提醒
</button>

View File

@ -85,7 +85,7 @@
[new Date(2015, 1, 20), new Date(2014, 2, 18)]
];
exports.startTrainStationSuggestQueryLimit = 10;
exports.startTrainStationSuggestQueryLimit = 2;
exports.maxSuggestStationTimeRange = {
"": 300,
"G": 180,

View File

@ -6,6 +6,11 @@
exports.events = new ev();
//记录参数
var queryTicketUrl = null;
var lastLogTime = null;
var isLogOpen = false;
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);
@ -39,13 +44,13 @@
return data;
};
var transferData = function (data, noProcess) {
var transferData = function (data, noProcess, noAction) {
var result = data;
if (!Array.isArray(result.data) || !result.data.length || !result.data[0].queryLeftNewDTO) {
exports.events.fireEvent("requestFailed");
};
var trainData = { raw: result, rawTrainMap: {}, trainMap: {}, auto: null, nextTime: null };
var trainData = { raw: result, rawTrainMap: {}, trainMap: {}, auto: null, nextTime: null, noProcess: noProcess, noAction: noAction };
_.each(result.data, function (t) {
var train = {
@ -143,24 +148,62 @@
return trainData;
};
exports.queryTicket = function (from, to, date, student, noProcess) {
exports.queryTicket = function (from, to, date, student, noProcess, noAction) {
var def = new $.Deferred();
ajax.sendGet("leftTicket/query", "leftTicket/init", {
"leftTicketDTO.train_date": date,
"leftTicketDTO.from_station": from,
"leftTicketDTO.to_station": to,
"purpose_codes": student ? "0X00" : "ADULT"
}, "json", function () {
var data = transferData(this.model, noProcess);
def.resolveWith(data);
}, function () {
def.reject();
});
var sendQueryRequest = function () {
var p = {
"leftTicketDTO.train_date": date,
"leftTicketDTO.from_station": from,
"leftTicketDTO.to_station": to,
"purpose_codes": student ? "0X00" : "ADULT"
};
exports.log(p);
ajax.sendGet(queryTicketUrl, "leftTicket/init", p, "json", function () {
var data = transferData(this.model, noProcess, noAction);
def.resolveWith(data);
}, function () {
def.reject();
});
};
var fetchTicketUrl = function () {
ajax.sendGet("leftTicket/init", "/otn/", null, "text")
.done(function () {
var html = this.model;
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 sendQueryRequest();
}).fail(function () {
def.reject();
});
};
if (queryTicketUrl) {
sendQueryRequest();
}
else {
fetchTicketUrl();
}
return def.promise();
};
exports.log = function (data) {
if (!isLogOpen || (lastLogTime && (new Date() - lastLogTime) < 5000))
return;
lastLogTime = new Date();
ajax.sendGet("leftTicket/log", "leftTicket/init", data, "json");
};
exports.queryTrainStop = function (id, from, to, date) {
var data = {
train_no: id,

View File

@ -40,6 +40,7 @@
});
data.available = _.difference(data.available, ft);
}
data.include = data.available;
if (currentProfile.hideNoTicket) {
//隐藏无票车次

View File

@ -1,10 +1,11 @@
define(function (require, exports, module) {
var data = require("../data.js");
var parser = require("../platform/parser.js");
var sessmgr = require("../account/sessionMgr.js");
var EventObject = require("../platform/EventObject.js");
var cp = sessmgr.currentProfile;
var query = require("../otn/queryticket.js");
var suggestion = null;
var utility = require("../utility.js");
sessmgr.on("currentProfileChanged", function () {
cp = sessmgr.currentProfile;
@ -15,26 +16,6 @@
var isFullyQualifiedTrainCode = function (code) {
return /^[A-Z]?\d+$/.test(code);
};
var getHours = function (t1, t2) {
var x1 = /^0?(\d+):(\d+)$/.exec(t1) ? parseInt(RegExp.$1) * 60 + parseInt(RegExp.$2) : null;
var x2 = /^0?(\d+):(\d+)$/.exec(t2) ? parseInt(RegExp.$1) * 60 + parseInt(RegExp.$2) : null;
if (x1 == null || x2 == null)
return null;
//这里不考虑两个时刻之间跨度超越1天的情况。按理说应该不会一趟车跑了一天都没停过一个站吧。。
return x1 <= x2 ? x2 - x1 : x2 + 24 * 60 - x1;
};
var isSpaceTimeInRange = function (code, t1, t2) {
var limit = data.maxSuggestStationTimeRange[code[0]] || data.maxSuggestStationTimeRange[""];
var space = getHours(t1, t2);
return space != null && space <= limit;
};
var sortFunc = function (x, y) {
if (x.ep ^ y.ep)
return x.ep ? -1 : 1;
return y.st - x.st;
};
function TSS() {
EventObject.apply(this);
@ -45,7 +26,7 @@
var loadTrainStops = function () {
if (!alltrains || !alltrains.length) {
checkSuggestion();
requestSuggestionData();
return;
}
@ -55,7 +36,7 @@
t = _.findWhere(queryResult.original, { code: tcode });
} while (!t && alltrains.length)
if (!t) {
checkSuggestion();
requestSuggestionData();
return;
}
query.queryTrainStop(t.id, t.from.code, t.to.code, t.date)
@ -67,104 +48,103 @@
loadTrainStops();
}).fail(loadTrainStops);
};
var getStopTime = function (stop) {
return /^(\d+)分钟$/.exec(stop.stopover_time) && parseInt(RegExp.$1) || 0;
};
var checkSuggestion = function () {
var requestSuggestionData = function () {
var requestData = {
from: cp.fromCode,
to: cp.toCode,
date: cp.depDate,
stops: trainStops
data: JSON.stringify({
key: "stupid360",
from: cp.fromCode,
to: cp.toCode,
date: cp.depDate,
stops: trainStops
})
};
console.log(JSON.stringify(requestData));
var suggestion = [];
for (var ts in trainStops) {
var tinfo = trainStops[ts].info;
var tstops = trainStops[ts].stops;
//查找在当前站的停止时间
var stopFrom = _.findWhere(tstops, { station_name: tinfo.from.name });
var stopTo = _.findWhere(tstops, { station_name: tinfo.to.name });
var stopTimeFrom = getStopTime(stopFrom);
var stopTimeTo = getStopTime(stopTo);
//发站?
var maybeAltStart = [];
if (!tinfo.from.endpoint) {
if (isSpaceTimeInRange(tinfo.code, tstops[0].start_time, stopFrom.arrive_time)) {
maybeAltStart.push({ code: tinfo.start.code, name: tinfo.start.name, ep: true });
}
//检查各站
for (var i = 1; i < tstops.length; i++) {
var stop = tstops[i];
var stopStatCode = data.citynameMap[stop.station_name];
//无法推荐当前站、过路站以及无法查到的站
if (stop.isEnabled || !stopStatCode || stop === stopFrom)
break;
stopStatCode = stopStatCode.c;
//如果停靠时间比当前的站小,也不考虑,因为这个很可能一样限售
if (getStopTime(stop) < stopTimeFrom)
continue;
//如果时间过久,那么也不考虑,因为会需要太久或加价太多
if (!isSpaceTimeInRange(tinfo.code, stop.start_time, stopFrom.arrive_time))
continue;
//考虑
maybeAltStart.push({ code: stopStatCode, name: stop.station_name, st: getStopTime(stop) });
}
}
//到站?
var maybeAltEnd = [];
if (!tinfo.to.endpoint) {
var lastStop = tstops[tstops.length - 1];
if (isSpaceTimeInRange(tinfo.code, stopTo.start_time, lastStop.arrive_time)) {
maybeAltEnd.push({ code: tinfo.end.code, name: tinfo.end.name, ep: true });
}
for (var j = tinfo.length - 2; j >= 0; j--) {
var eStop = tstops[i];
var eStopStatCode = data.citynameMap[eStop.station_name];
//无法推荐当前站、过路站以及无法查到的站
if (eStop.isEnabled || !eStopStatCode || eStop === stopTo)
break;
eStopStatCode = eStopStatCode.c;
//如果停靠时间比当前的站小,也不考虑,因为这个很可能一样限售
if (getStopTime(eStop) < stopTimeTo)
continue;
//如果时间过久,那么也不考虑,因为会需要太久或加价太多
if (!isSpaceTimeInRange(tinfo.code, stopTo.start_time, eStop.arrive_time))
continue;
//考虑
maybeAltEnd.push({ code: eStopStatCode, name: eStop.station_name, st: getStopTime(stop) });
}
}
if (maybeAltEnd.length || maybeAltStart.length) {
maybeAltEnd.sort(sortFunc);
maybeAltStart.sort(sortFunc);
suggestion.push({
code: tinfo.code,
suggestion: {
startAlt: maybeAltStart,
endAlt: maybeAltEnd
}
});
}
$.ajax({
url: "/getsuggestion.ashx?key=stupid360",
dataType: "json",
method: "POST",
data: requestData
}).done(loadTrainTickets).fail(function () {
setTimeout(function () {
that.clearQueryResultCache();
//如果服务器出现错误,则等待十秒后再恢复状态
}, 10000);
});
};
var loadTrainTickets = function (serverSuggestion) {
if (!serverSuggestion || !serverSuggestion.length) {
//木有结果。
suggestion = {};
isInQuery = false;
}
serverSuggestion = _.filter(serverSuggestion, function (s) {
//预处理数据
s.key = s.key.split('-');
s.fromText = s.key[0];
s.toText = s.key[1];
s.fromCode = data.citynameMap[s.fromText];
s.toCode = data.citynameMap[s.toText];
if (!s.toCode || !s.fromCode)
return false;
s.toCode = s.toCode.c;
s.fromCode = s.fromCode.c;
return true;
});
var loaded = [];
var loadTrain = function () {
if (!serverSuggestion.length) {
checkSuggestion(loaded);
return;
}
var t = serverSuggestion.pop();
query.queryTicket(t.fromCode, t.toCode, cp.depDate, cp.studentTicket, false, true)
.done(function () {
loaded.unshift(t);
var trainAvailable = this.original;
if (!trainAvailable) {
loadTrain();
return;
}
var trainFilter = new RegExp("^(" + _.map(t.value, function (v) {
return v.trainCode;
}).join("|") + ")$", "i");
trainAvailable = _.filter(trainAvailable, function (x) {
return trainFilter.test(x.code);
});
var seatType = cp.selectedSeatType || data.seatOrder;
//seatType=['O']
var count = Math.max(!cp.partialSubmitEnabled && cp.passengers ? cp.passengers.length : 0, 1);
var selectedTrain = _.filter(trainAvailable, function (x) {
if (x.available !== 1)
return false;
return _.some(seatType, function (s) {
return x.ticketMap[s] && x.ticketMap[s].count >= count;
});
});
if (selectedTrain.length)
t.train = selectedTrain;
loadTrain();
}).fail(loadTrain);
};
loadTrain();
};
var checkSuggestion = function (suggestData) {
suggestion = suggestData;
if (suggestion.length)
that.fireEvent("trainSuggestion", suggestion);
};
this.setQueryResult = function (result, queryCount) {
if (queryCount < startQueryLimit || isInQuery)
if (queryCount < startQueryLimit || isInQuery || suggestion)
return;
isInQuery = true;
@ -177,13 +157,15 @@
isInQuery = false;
trainStops = null;
alltrains = null;
suggestion = null;
};
this.checkTrainSuggestion = function () {
alltrains = _.filter(cp.selectedTrain || [], function (code) {
if (!isFullyQualifiedTrainCode(code))
return false;
var t = _.findWhere(queryResult.original, { code: code });
if (!t || (t.from.endpoint && t.to.endpoint))
//始发终到、未起售的车不考虑
if (!t || (t.from.endpoint && t.to.endpoint) || t.available < 0)
return false;
return true;

View File

@ -8,6 +8,7 @@
var query = require("../otn/queryticket.js");
var trainSuggest = require("../otn/trainstationsuggest.js");
var port = require("../platform/extensionPort.js");
var utility = require("../utility.js");
sessMgr.on("sessionChanged", function () {
session = sessMgr.current;
@ -40,34 +41,14 @@
var st = currentProfile.selectedSeatType;
if (!st || !st.length)
st = expdata.seatDisplayOrder;
if (currentProfile.selectSeatFirst) {
//席别优先
seat = _.find(st, function (s) {
var _treg = _.find(trainRegCache, function (t) {
entry = _.find(data.available, function (item) {
return t.test(item.code) && (_.findWhere(item.tickets, { code: s }) || {}).count >= count;
});
return typeof (entry) != 'undefined';
});
return typeof (_treg) != 'undefined';
});
} else {
//车次优先
_.find(trainRegCache, function (t) {
seat = _.find(st, function (s) {
entry = _.find(data.available, function (item) {
return t.test(item.code) && (_.findWhere(item.tickets, { code: s }) || {}).count >= count;
});
return typeof (entry) != 'undefined';
});
return typeof (seat) != 'undefined';
});
var selectResult = utility.selectTrain(data.available, trainRegCache, st, currentProfile.selectSeatFirst, count);
if (selectResult) {
seat = selectResult.seat;
train = selectResult.train.code;
}
//查找结果
if (seat) {
train = entry.code;
//乘客
var ticketCount = _.findWhere(entry.tickets, { code: seat }).count;
var pcount = Math.min(ticketCount, currentProfile.passengers ? currentProfile.passengers.length : 0);
@ -83,6 +64,9 @@
data.auto = null;
}
if (data.noAction)
return;
//判断是否需要继续刷新
if (data.auto !== null) {
//找到车次了
@ -139,12 +123,14 @@
if (inAutoRefresh)
trainSuggest.setQueryResult(d, refreshCount);
if (!d.auto && (d.nextTime && inAutoRefresh)) {
countdownTime = d.nextTime;
that.start();
d.inAutoRefresh = inAutoRefresh;
} else if (d.auto) {
that.stop();
if (!d.noAction) {
if (!d.auto && (d.nextTime && inAutoRefresh)) {
countdownTime = d.nextTime;
that.start();
d.inAutoRefresh = inAutoRefresh;
} else if (d.auto) {
that.stop();
}
}
}).on("requestFailed", function () {
//失败?不做任何处理

View File

@ -115,7 +115,10 @@
document.addEventListener("requestSupportError", function () {
exports.showMessagePopup("error", "无法执行网络请求似乎没有安装12306订票助手哦。", { closeAfter: null });
});
document.addEventListener("verifyCodeLoadFailed", function() {
document.addEventListener("verifyCodeLoadFailed", function () {
exports.showMessagePopup("error", "验证码加载失败,请点击验证码图片刷新哦。");
});
document.addEventListener("platformError", function () {
exports.alert("错误", "未能执行请求可能12306已改版导致订票助手出错");
});
});

View File

@ -42,9 +42,10 @@
var sellTimes = stationData.sellTime[code] || "----";
var times = container.find("time");
times.filter(":lt(2)").html(utility.formatDate(time, "yyyy年MM月dd日"));
times.filter(":eq(2)").html(utility.formatDate(bs, "yyyy年MM月dd日") + utility.format24hTo12h(sellTimes));
container.find("address").html(cp.fromText + "站");
times.filter(":eq(0)").html(utility.formatDate(time, "MM月dd日"));
times.filter(":eq(1)").html(utility.formatDate(bs, "MM月dd日"));
times.filter(":eq(2)").html(utility.format24hTo12h(sellTimes));
container.find("span:eq(0)").html(cp.fromText + "站");
container[0].dataset.sellday = bs.getTime();
container[0].dataset.timeset = sellTimes;
@ -201,7 +202,7 @@
container.find(">header>a").click(function () {
container.hide();
});
container.find(".button-primary").click(function () {
container.find(".act-openalarm").click(function () {
initShowAlarmUi();
});
$("#selltip_selection :checkbox:eq(0)").change(function () {

View File

@ -81,4 +81,34 @@
var h = parseInt(RegExp.$1, 10);
return h < 13 ? "上午" + h + ":" + RegExp.$2 : "下午" + (h - 12) + ":" + RegExp.$2;
};
exports.selectTrain = function (trainList, trainFilter, seatOrder, seatFirst, count) {
var seat, train;
if (seatFirst) {
//席别优先
seat = _.find(seatOrder, function (s) {
var treg = _.find(trainFilter, function (t) {
train = _.find(trainList, function (item) {
return t.test(item.code) && (_.findWhere(item.tickets, { code: s }) || {}).count >= count;
});
return typeof (train) != 'undefined';
});
return typeof (treg) != 'undefined';
});
} else {
//车次优先
_.find(trainFilter, function (t) {
seat = _.find(seatOrder, function (s) {
train = _.find(trainList, function (item) {
return t.test(item.code) && (_.findWhere(item.tickets, { code: s }) || {}).count >= count;
});
return typeof (train) != 'undefined';
});
return typeof (seat) != 'undefined';
});
}
if (seat)
return { seat: seat, train: train };
return null;
}
});

File diff suppressed because one or more lines are too long