Light12306/Web12306/TrainSuggestion.cs
2014-11-21 20:32:36 +08:00

937 lines
25 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.ComponentModel;
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
{
public class TrainSuggestion : IHttpHandler
{
#region Implementation of IHttpHandler
/// <summary>
/// 通过实现 <see cref="T:System.Web.IHttpHandler"/> 接口的自定义 HttpHandler 启用 HTTP Web 请求的处理。
/// </summary>
/// <param name="context"><see cref="T:System.Web.HttpContext"/> 对象,它提供对用于为 HTTP 请求提供服务的内部服务器对象(如 Request、Response、Session 和 Server的引用。</param>
public void ProcessRequest(HttpContext context)
{
var request = context.Request;
context.Response.StatusCode = 404;
if (request.UrlReferrer == null || !Regex.IsMatch(request.UrlReferrer.Host, @"^.*?\.(fishlee\.net|liebao\.cn)$"))
return;
//check code
var r = request.QueryString["r"];
if (string.IsNullOrEmpty(r))
return;
var data = request.Form["data"];
if (string.IsNullOrEmpty(data))
return;
var crc = GetTextCrc(data);
if (!r.EndsWith(crc + ""))
{
return;
}
var origin = request.Headers["Origin"];
var callback = request.QueryString["calllback"];
if (!string.IsNullOrEmpty(origin))
{
if (Regex.IsMatch(origin, @"^(https?://.*?\.(fishlee\.net|liebao\.cn)|chrome-extension://.*)$"))
context.Response.AddHeader("Access-Control-Allow-Origin", origin);
else
{
//非法提交
return;
}
}
else
{
return;
}
context.Response.ContentType = string.IsNullOrEmpty(callback) ? "application/json" : "application/javascript";
context.Response.BufferOutput = false;
context.Response.StatusCode = 200;
try
{
var ri = JsonConvert.DeserializeObject<RequestInfo>(data);
var suggestion = GetSuggestionResponseContent(ri);
if (!string.IsNullOrEmpty(callback))
{
context.Response.Write(callback + "(");
}
context.Response.Write(suggestion);
if (!string.IsNullOrEmpty(callback))
{
context.Response.Write(callback + ");");
}
}
catch (Exception ex)
{
return;
}
}
int GetTextCrc(string txt)
{
var crc = 0;
for (var i = 0; i < txt.Length - 1; i += 2)
{
crc += txt[i] ^ txt[i + 1];
if (crc > 48360)
crc -= 36048;
}
if (txt.Length % 2 == 1)
crc += txt[txt.Length - 1];
if (crc > 48360)
crc -= 36048;
crc = crc ^ 5299;
return crc;
}
/// <summary>
/// 获取一个值,该值指示其他请求是否可以使用 <see cref="T:System.Web.IHttpHandler"/> 实例。
/// </summary>
/// <returns>
/// 如果 <see cref="T:System.Web.IHttpHandler"/> 实例可再次使用,则为 true否则为 false。
/// </returns>
public bool IsReusable { get { return true; } }
#endregion
#region
static JsonSerializerSettings _camelJsonSetting = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
string GetSuggestionResponseContent(RequestInfo ri)
{
if (ri.CheckIllegal())
return string.Empty;
return JsonConvert.SerializeObject(GetSuggestionResponseContentCore(ri), Formatting.None, _camelJsonSetting);
}
SuggestionResponse GetSuggestionResponseContentCore(RequestInfo ri)
{
//预处理
PreProcessRequestData(ri);
//获得所有的可替换站点
var alllines = ri.Stops.Values.SelectMany(s => GetAlternativeLines(s, ri.Date)).Where(s => s != null).ToArray();
//对线路进行分组排序
var lineGrouped = alllines.GroupBy(s => s.LineName).Select(s =>
{
var list = s.ToList();
var f = list[0];
return new AlternativeLineGroup(f.From.Name, f.To.Name, f.Date, list, f.IsCrossDate);
}).ToList();
//过滤
FilteAlternativeLine(ri, lineGrouped);
//填充数据
FillExtraInfo(ri, lineGrouped);
//排序
SortLineRecommand(ri, lineGrouped);
return new SuggestionResponse()
{
Key = "盗用可耻鄙视无耻的各个国产IT同行",
Accepted = true,
Groups = lineGrouped
};
}
void PreProcessRequestData(RequestInfo ri)
{
ri.PreprocessData();
}
void FilteAlternativeLine(RequestInfo ri, List<AlternativeLineGroup> line)
{
for (var i = line.Count - 1; i >= 0; i--)
{
var train = ri.Stops[line[i].Lines[0].TrainCode];
var maxAllowElapsedTime = train.TrainInfo.elapsedTime.Value.Add(train.TrainInfo.elapsedTime.MaxAddTime);
for (var j = line[i].Lines.Count - 1; j >= 0; j--)
{
var current = line[i].Lines[j];
//如果同时是发站和到站,则删除不推荐
if (current.From.IsOriginal && current.To.IsOriginal)
line[i].Lines.RemoveAt(j);
//如果时间会超过原来最大的允许值,则排除
else if (current.ElapsedTime > maxAllowElapsedTime)
line[i].Lines.RemoveAt(j);
}
if (line[i].Lines.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<AlternativeLineGroup> line)
{
line.Sort((x, y) =>
{
var ceo1 = GetLineStationEndpointWeight(x.Lines);
var ceo2 = GetLineStationEndpointWeight(y.Lines);
if (ceo1 != ceo2)
return ceo2 - ceo1;
if (x.Lines.Count != y.Lines.Count)
return y.Lines.Count - x.Lines.Count;
//再看停靠时间
var ep1 = (int)x.Lines.Min(s => s.From.StopTime + s.To.StopTime).TotalSeconds;
var ep2 = (int)y.Lines.Min(s => s.From.StopTime + s.To.StopTime).TotalSeconds;
if (ep1 != ep2)
return ep2 - ep1;
//再看价格
var ap1 = x.Lines.Min(s => s.Radio);
var ap2 = y.Lines.Min(s => s.Radio);
if (ap1 != ap2)
return ap1 - ap2;
return 0;
});
//最多推荐5条线路
if (line.Count > 5)
line.RemoveRange(5, line.Count - 5);
//再对组内排序
foreach (var current in line)
{
current.Lines.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<AlternativeLineGroup> line)
{
for (var i = line.Count - 1; i >= 0; i--)
{
for (var j = line[i].Lines.Count - 1; j >= 0; j--)
{
var train = ri.Stops[line[i].Lines[j].TrainCode];
var current = line[i].Lines[j];
var originalTime = train.TrainInfo.elapsedTime.Value;
//如果时间会超过原来最大的允许值,则排除
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);
current.Radio = (int)(priceExtraRadio * 100);
}
}
}
List<AlternativeLine> GetAlternativeLines(TrainInfoItem train, DateTime requestDate)
{
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;
if (indexFrom != 0)
{
var stopTimeFrom = stops[indexFrom].StopTime;
var arriveTime = stops[indexFrom].ArriveTime;
for (var i = 0; i < indexFrom; i++)
{
//如果isEnabled为true则说明会停靠这个站点。不能推荐更短的。
//如果时间过久,则不做推荐
if (stops[i].isEnabled || stops[i].StartTime - arriveTime > maxAddTime)
continue;
var stopTime = i == 0 ? TimeSpan.Zero : stops[i].StopTime;
if (i != 0)
{
//对于非始发站,停靠时间相同或更短,则不做推荐。
if (stopTime < stopTimeFrom && i < indexFrom - 3)
continue;
}
//添加到推荐列表中
altFrom.Add(new AlternativeStation()
{
ArriveTime = stops[i].StartTime,
IsEndPoint = i == 0,
IsFromStation = true,
Name = stops[i].station_name,
StopTime = stopTime,
TrainCode = info.code,
Date = stops[i].StartTime,
IsCrossDate = requestDate != stops[i].StartTime.Date
});
}
//排序
altFrom.Sort(new AlternativeStationComparer());
if (altFrom.Count > 5)
altFrom.RemoveRange(5, altFrom.Count - 5);
}
if (indexTo != stops.Length - 1)
{
var stopTimeTo = stops[indexTo].StopTime;
var startTime = stops[indexTo].StartTime;
for (var i = stops.Length - 1; i >= indexTo; i--)
{
//如果isEnabled为true则说明会停靠这个站点。不能推荐更短的。
//如果时间过久,则不做推荐
if (stops[i].isEnabled || stops[i].ArriveTime - startTime > maxAddTime)
continue;
var stopTime = i == 0 ? TimeSpan.Zero : stops[i].StopTime;
if (i != stops.Length - 1)
{
//对于非终到站,停靠时间相同或更短,则不做推荐。
if (stopTime < stopTimeTo && i > indexTo + 3)
continue;
}
//添加到推荐列表中
altTo.Add(new AlternativeStation()
{
ArriveTime = stops[i].ArriveTime,
IsEndPoint = i == stops.Length - 1,
IsFromStation = false,
Name = stops[i].station_name,
StopTime = stopTime,
TrainCode = info.code
});
}
altTo.Sort(new AlternativeStationComparer());
if (altTo.Count > 5)
altTo.RemoveRange(5, altTo.Count - 5);
}
//plus 原始线路
altFrom.Add(new AlternativeStation
{
ArriveTime = stops[indexFrom].StartTime,
IsEndPoint = indexFrom == 0,
IsFromStation = true,
Name = stops[indexFrom].station_name,
StopTime = indexFrom == 0 ? TimeSpan.Zero : stops[indexFrom].StopTime,
TrainCode = info.code,
IsOriginal = true,
IsCrossDate = false,
Date = stops[indexFrom].StartTime
});
altTo.Add(new AlternativeStation
{
ArriveTime = stops[indexTo].ArriveTime,
IsEndPoint = indexTo == stops.Length - 1,
IsFromStation = false,
Name = stops[indexTo].station_name,
StopTime = indexTo == stops.Length - 1 ? TimeSpan.Zero : stops[indexFrom].StopTime,
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, s.Key.Date, s.Key.IsCrossDate) { 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;
}
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;
}
public static int GetTimeSpace(string time1, string time2)
{
var x1 = GetTimeValue(time1);
var x2 = GetTimeValue(time2);
return x1 <= x2 ? x2 - x1 : x2 + 24 * 60 - x1;
}
}
public class SuggestionResponse
{
public bool Accepted { get; set; }
public string Key { get; set; }
public List<AlternativeLineGroup> Groups { get; set; }
}
public class AlternativeLineGroup
{
public string FromText { get; set; }
public string ToText { get; set; }
public string Date { get; set; }
public List<AlternativeLine> Lines { get; set; }
public bool IsCrossDate { get; set; }
/// <summary>
/// 创建 <see cref="AlternativeLineGroup" /> 的新实例(AlternativeLineGroup)
/// </summary>
/// <param name="fromText"></param>
/// <param name="toText"></param>
/// <param name="date"></param>
/// <param name="lines"></param>
public AlternativeLineGroup(string fromText, string toText, string date, List<AlternativeLine> lines, bool isCrossDate)
{
FromText = fromText;
ToText = toText;
Date = date;
Lines = lines;
IsCrossDate = isCrossDate;
}
}
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 DateTime ArriveTime { get; set; }
public TimeSpan StopTime { get; set; }
public bool IsOriginal { get; set; }
/// <summary>
/// 是否跨天
/// </summary>
public bool IsCrossDate { get; set; }
/// <summary>
/// 日期
/// </summary>
public DateTime Date { 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 int Radio { get; set; }
public string Date { get; set; }
public bool IsCrossDate { get; set; }
public TimeSpan ElapsedTime
{
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 _elTimeSpan.Value;
}
}
/// <summary>
/// 创建 <see cref="AlternativeLine" /> 的新实例(AlternativeLine)
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
public AlternativeLine(AlternativeStation from, AlternativeStation to, DateTime date, bool isCrossDate)
{
From = from;
To = to;
IsCrossDate = isCrossDate;
Date = date.ToString("yyyy-MM-dd");
LineName = From.Name + ";" + To.Name + (isCrossDate ? ";" + Date : "");
}
}
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>
{
#region Implementation of IComparer<in StopInfo>
/// <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(SuggestItem x, SuggestItem y)
{
if (x.EndPoint ^ y.EndPoint)
return x.EndPoint ? -1 : 1;
return x.StopTime - y.StopTime;
}
#endregion
}
public class SuggestItem
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("code")]
public string Code { get; set; }
[JsonProperty("ep")]
public bool EndPoint { get; set; }
[JsonProperty("st")]
public int StopTime { get; set; }
}
public class RequestInfo
{
public string Key { get; set; }
public string From { get; set; }
public string To { get; set; }
public DateTime Date { get; set; }
[JsonProperty("tt")]
public string[] SelectedTrain { get; set; }
[JsonProperty("ts")]
public string[] SelectedSeats { get; set; }
public Dictionary<string, TrainInfoItem> Stops { get; set; }
/// <summary>
/// 检测是否有非法请求有则返回true
/// </summary>
/// <returns></returns>
public bool CheckIllegal()
{
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)))
return true;
//add 2014年9月4日 - 检测非法席别
if (SelectedSeats == null)
return true;
if (SelectedSeats.Length > 0)
{
if (Stops.Values.Any(s => !SelectedSeats.Any(x => s.TrainInfo.ticketMap.ContainsKey(x[0]))))
return true;
}
//added 2014-9-9 检测非法车次
if (SelectedTrain != null && SelectedTrain.Length > 0)
{
var reg = new Regex("^(" + string.Join("|", SelectedTrain) + ")$", RegexOptions.IgnoreCase);
if (Stops.Keys.Any(s => !reg.IsMatch(s)))
return true;
}
return false;
}
public void PreprocessData()
{
foreach (var trainInfoItem in Stops.Values)
{
trainInfoItem.PreprocessData(Date);
}
}
}
public class TrainInfoItem
{
[JsonProperty("info")]
public TrainInfo TrainInfo { get; set; }
[JsonProperty("stops")]
public StopInfo[] StopInfos { get; set; }
public void PreprocessData(DateTime requestDate)
{
TrainInfo.from.TimePoint = requestDate.Date.Add(Cn12306SuggestionUtility.GetTimeValueSpan(TrainInfo.from.time));
//查找开始站
var startIndex = Array.FindIndex(StopInfos, s => s.isEnabled);
TrainInfo.from.Index = startIndex;
TrainInfo.to.Index = Array.FindIndex(StopInfos, startIndex + 1, s => !s.isEnabled);
if (TrainInfo.to.Index == -1)
TrainInfo.to.Index = StopInfos.Length - 1;
StopInfos[startIndex].StartTime = TrainInfo.from.TimePoint;
if (startIndex > 0)
{
StopInfos[startIndex].ArriveTime = requestDate.Add(StopInfos[startIndex].ArriveTimePoint);
if (StopInfos[startIndex].ArriveTime > StopInfos[startIndex].StartTime)
StopInfos[startIndex].ArriveTime = StopInfos[startIndex].ArriveTime.AddDays(-1);
}
var tempDate = requestDate;
for (var i = startIndex - 1; i >= 0; i--)
{
var sto = StopInfos[i];
var stn = StopInfos[i + 1];
sto.StartTime = tempDate.Add(sto.StartTimePoint);
if (sto.StartTime > stn.StartTime)
{
tempDate = tempDate.AddDays(-1);
sto.StartTime = sto.StartTime.AddDays(-1);
}
if (i > 0)
{
sto.ArriveTime = tempDate.Add(sto.ArriveTimePoint);
if (sto.ArriveTime > sto.StartTime)
{
tempDate = tempDate.AddDays(-1);
sto.ArriveTime = sto.ArriveTime.AddDays(-1);
}
}
}
tempDate = requestDate;
for (var i = startIndex + 1; i < StopInfos.Length; i++)
{
var sto = StopInfos[i];
var stn = StopInfos[i - 1];
sto.ArriveTime = tempDate.Add(sto.ArriveTimePoint);
if (sto.ArriveTime < stn.StartTime)
{
tempDate = tempDate.AddDays(1);
sto.ArriveTime = sto.ArriveTime.AddDays(1);
}
if (i < StopInfos.Length - 1)
{
sto.StartTime = tempDate.Add(sto.StartTimePoint);
if (sto.StartTime < sto.ArriveTime)
{
tempDate = tempDate.AddDays(1);
sto.StartTime = sto.StartTime.AddDays(1);
}
}
}
//var startTime = StopInfos.Select(s => s.StartTime).ToArray();
//var stopTime = StopInfos.Select(s => s.StopTime).ToArray();
}
}
public class StopInfo
{
public string start_station_name { get; set; }
public string arrive_time { get; set; }
public string station_train_code { get; set; }
public string station_name { get; set; }
public string train_class_name { get; set; }
public string service_type { get; set; }
public string start_time { get; set; }
public string stopover_time { get; set; }
public string end_station_name { get; set; }
public string station_no { get; set; }
public bool isEnabled { get; set; }
/// <summary>
/// 到达时间
/// </summary>
public DateTime ArriveTime { get; set; }
/// <summary>
/// 发车时间
/// </summary>
public DateTime StartTime { get; set; }
TimeSpan? _stopTimeSpan;
public TimeSpan StopTime
{
get
{
if (!_stopTimeSpan.HasValue)
{
_stopTimeSpan = Cn12306SuggestionUtility.GetStopTimeSpan(stopover_time);
}
return _stopTimeSpan.Value;
}
}
TimeSpan? _arriveTime, _startTime;
public TimeSpan ArriveTimePoint
{
get { return (_arriveTime ?? (_arriveTime = Cn12306SuggestionUtility.GetTimeValueSpan(arrive_time))).Value; }
}
public TimeSpan StartTimePoint
{
get { return (_startTime ?? (_startTime = Cn12306SuggestionUtility.GetTimeValueSpan(start_time))).Value; }
}
}
public class SeatTicketInfo
{
public string code { get; set; }
public string name { get; set; }
public int price { get; set; }
public int count { get; set; }
}
public class SimpleStationInfo
{
public string code { get; set; }
public string name { get; set; }
}
public class DetailStationInfo : SimpleStationInfo
{
public string fromStationNo { get; set; }
public bool endpoint { get; set; }
public string time { get; set; }
/// <summary>
/// 具体时间
/// </summary>
public DateTime TimePoint { get; set; }
/// <summary>
/// 在停靠站中的索引
/// </summary>
public int Index { get; set; }
}
public class TrainInfo
{
public string id { get; set; }
public string code { get; set; }
public int available { get; set; }
public SimpleStationInfo start { get; set; }
public DetailStationInfo from { get; set; }
public DetailStationInfo to { get; set; }
public Elapsedtime elapsedTime { get; set; }
public SimpleStationInfo end { get; set; }
public string ypinfo { get; set; }
public string ypinfo_ex { get; set; }
public string locationCode { get; set; }
public int controlDay { get; set; }
public string supportCard { get; set; }
public string saleTime { get; set; }
public string secureStr { get; set; }
public DateTime? selltime { get; set; }
public string date { get; set; }
public string limitSellInfo { get; set; }
public SeatTicketInfo[] tickets { get; set; }
public Dictionary<char, SeatTicketInfo> ticketMap { get; set; }
}
public class Elapsedtime
{
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;
}
}
}
}