Light12306/Web12306/Servers/TrainSuggestion/TrainSuggestionHandler.cs

418 lines
12 KiB
C#
Raw Normal View History

2015-07-30 16:01:49 +08:00
namespace Web12306.Servers.TrainSuggestion
2015-03-13 19:25:08 +08:00
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Hosting;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public class TrainSuggestionHandler : IHttpHandler
{
#region Implementation of IHttpHandler
/// <summary>
2015-07-30 16:01:49 +08:00
/// 通过实现 <see cref="T:System.Web.IHttpHandler"/> 接口的自定义 HttpHandler 启用 HTTP Web 请求的处理。
2015-03-13 19:25:08 +08:00
/// </summary>
2015-07-30 16:01:49 +08:00
/// <param name="context"><see cref="T:System.Web.HttpContext"/> 对象,它提供对用于为 HTTP 请求提供服务的内部服务器对象(如 Request、Response、Session 和 Server的引用。</param>
2015-03-13 19:25:08 +08:00
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
{
2015-07-30 16:01:49 +08:00
//非法提交
2015-03-13 19:25:08 +08:00
return;
}
}
else
{
return;
}
context.Response.ContentType = string.IsNullOrEmpty(callback) ? "application/json" : "application/javascript";
context.Response.BufferOutput = true;
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)
{
//log error
var sb = new StringBuilder();
2015-07-30 16:01:49 +08:00
sb.AppendLine(string.Format("请求URL {0}", context.Request.Url));
sb.AppendLine("数据:");
2015-03-13 19:25:08 +08:00
sb.AppendLine(data);
sb.AppendLine();
2015-07-30 16:01:49 +08:00
sb.AppendLine("错误信息:");
2015-03-13 19:25:08 +08:00
sb.AppendLine(ex.ToString());
var log = DateTime.Now.Ticks + ".log";
var path = HostingEnvironment.MapPath("~/errors/tr/");
path = PathUtility.Combine(path, log);
Directory.CreateDirectory(Path.GetDirectoryName(path));
File.WriteAllText(path, sb.ToString());
}
}
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>
2015-07-30 16:01:49 +08:00
/// 获取一个值,该值指示其他请求是否可以使用 <see cref="T:System.Web.IHttpHandler"/> 实例。
2015-03-13 19:25:08 +08:00
/// </summary>
/// <returns>
2015-07-30 16:01:49 +08:00
/// 如果 <see cref="T:System.Web.IHttpHandler"/> 实例可再次使用,则为 true否则为 false。
2015-03-13 19:25:08 +08:00
/// </returns>
public bool IsReusable { get { return true; } }
#endregion
2015-07-30 16:01:49 +08:00
#region
2015-03-13 19:25:08 +08:00
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)
{
2015-07-30 16:01:49 +08:00
//预处理
2015-03-13 19:25:08 +08:00
PreProcessRequestData(ri);
2015-07-30 16:01:49 +08:00
//获得所有的可替换站点
2015-03-13 19:25:08 +08:00
var alllines = ri.Stops.Values.SelectMany(s => GetAlternativeLines(s, ri.Date) ?? new List<AlternativeLine>()).Where(s => s != null).ToArray();
2015-07-30 16:01:49 +08:00
//对线路进行分组排序
2015-03-13 19:25:08 +08:00
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();
2015-07-30 16:01:49 +08:00
//过滤
2015-03-13 19:25:08 +08:00
FilteAlternativeLine(ri, lineGrouped);
2015-07-30 16:01:49 +08:00
//填充数据
2015-03-13 19:25:08 +08:00
FillExtraInfo(ri, lineGrouped);
2015-07-30 16:01:49 +08:00
//排序
2015-03-13 19:25:08 +08:00
SortLineRecommand(ri, lineGrouped);
return new SuggestionResponse()
{
2015-07-30 16:01:49 +08:00
Key = "盗用可耻鄙视无耻的各个国产IT同行",
2015-03-13 19:25:08 +08:00
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];
2015-07-30 16:01:49 +08:00
//如果同时是发站和到站,则删除不推荐
2015-03-13 19:25:08 +08:00
if (current.From.IsOriginal && current.To.IsOriginal)
line[i].Lines.RemoveAt(j);
2015-07-30 16:01:49 +08:00
//如果时间会超过原来最大的允许值,则排除
2015-03-13 19:25:08 +08:00
else if (current.ElapsedTime > maxAllowElapsedTime)
line[i].Lines.RemoveAt(j);
}
if (line[i].Lines.Count == 0)
{
line.RemoveAt(i);
}
}
}
/// <summary>
2015-07-30 16:01:49 +08:00
/// 根据停靠站获得权重
2015-03-13 19:25:08 +08:00
/// </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;
2015-07-30 16:01:49 +08:00
//再看停靠时间
2015-03-13 19:25:08 +08:00
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;
2015-07-30 16:01:49 +08:00
//再看价格
2015-03-13 19:25:08 +08:00
var ap1 = x.Lines.Min(s => s.Radio);
var ap2 = y.Lines.Min(s => s.Radio);
if (ap1 != ap2)
return ap1 - ap2;
return 0;
});
2015-07-30 16:01:49 +08:00
//最多推荐5条线路
2015-03-13 19:25:08 +08:00
if (line.Count > 5)
line.RemoveRange(5, line.Count - 5);
2015-07-30 16:01:49 +08:00
//再对组内排序
2015-03-13 19:25:08 +08:00
foreach (var current in line)
{
current.Lines.Sort((x, y) =>
{
2015-07-30 16:01:49 +08:00
//再看停靠时间
2015-03-13 19:25:08 +08:00
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;
2015-07-30 16:01:49 +08:00
//再看价格
2015-03-13 19:25:08 +08:00
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;
2015-07-30 16:01:49 +08:00
//如果时间会超过原来最大的允许值,则排除
2015-03-13 19:25:08 +08:00
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++)
{
2015-07-30 16:01:49 +08:00
//如果isEnabled为true则说明会停靠这个站点。不能推荐更短的。
//如果时间过久,则不做推荐
2015-03-13 19:25:08 +08:00
if (stops[i].isEnabled || stops[i].StartTime - arriveTime > maxAddTime)
continue;
var stopTime = i == 0 ? TimeSpan.Zero : stops[i].StopTime;
if (i != 0)
{
2015-07-30 16:01:49 +08:00
//对于非始发站,停靠时间相同或更短,则不做推荐。
2015-03-13 19:25:08 +08:00
if (stopTime < stopTimeFrom && i < indexFrom - 3)
continue;
}
2015-07-30 16:01:49 +08:00
//添加到推荐列表中
2015-03-13 19:25:08 +08:00
altFrom.Add(new AlternativeStation()
{
ArriveTime = stops[i].StartTime,
IsEndPoint = i == 0,
IsFromStation = true,
Name = stops[i].station_name,
StopTime = stopTime,
TrainID = info.id,
TrainCode = info.code,
Date = stops[i].StartTime,
IsCrossDate = requestDate != stops[i].StartTime.Date
});
}
2015-07-30 16:01:49 +08:00
//排序
2015-03-13 19:25:08 +08:00
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--)
{
2015-07-30 16:01:49 +08:00
//如果isEnabled为true则说明会停靠这个站点。不能推荐更短的。
//如果时间过久,则不做推荐
2015-03-13 19:25:08 +08:00
if (stops[i].isEnabled || stops[i].ArriveTime - startTime > maxAddTime)
continue;
var stopTime = i == 0 ? TimeSpan.Zero : stops[i].StopTime;
if (i != stops.Length - 1)
{
2015-07-30 16:01:49 +08:00
//对于非终到站,停靠时间相同或更短,则不做推荐。
2015-03-13 19:25:08 +08:00
if (stopTime < stopTimeTo && i > indexTo + 3)
continue;
}
2015-07-30 16:01:49 +08:00
//添加到推荐列表中
2015-03-13 19:25:08 +08:00
altTo.Add(new AlternativeStation()
{
ArriveTime = stops[i].ArriveTime,
IsEndPoint = i == stops.Length - 1,
IsFromStation = false,
Name = stops[i].station_name,
TrainID = info.id,
StopTime = stopTime,
TrainCode = info.code
});
}
altTo.Sort(new AlternativeStationComparer());
if (altTo.Count > 5)
altTo.RemoveRange(5, altTo.Count - 5);
}
2015-07-30 16:01:49 +08:00
//plus 原始线路
2015-03-13 19:25:08 +08:00
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,
TrainID = info.id,
IsCrossDate = false,
Date = stops[indexFrom].StartTime
});
altTo.Add(new AlternativeStation
{
ArriveTime = stops[indexTo].ArriveTime,
IsEndPoint = indexTo == stops.Length - 1,
IsFromStation = false,
TrainID = info.id,
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, TrainId = s.Key.TrainID }).ToList();
return lines;
}
#endregion
}
}