Light12306/TrainInfomationProviderService/TrainInfo/WebDataProvider.cs
2015-12-07 02:30:22 +08:00

271 lines
8.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using FSLib.Network.Http;
using Newtonsoft.Json;
using TrainInfomationProviderService.StationInfo;
using TrainInfomationProviderService.TrainInfo.Entities;
namespace TrainInfomationProviderService.TrainInfo
{
using System.Runtime.Serialization;
using TrainInfomationProviderService.Web;
class WebDataProvider
{
class TrainListInfoItem
{
public string station_train_code { get; set; }
public string train_no { get; set; }
}
class TrainListData : Dictionary<string, Dictionary<string, List<TrainListInfoItem>>>
{
}
public class TrainStopWebInfo
{
public string arrive_time { get; set; }
public string station_name { get; set; }
public string start_time { get; set; }
public string stopover_time { get; set; }
public string station_no { get; set; }
public bool isEnabled { get; set; }
public TrainStop ToTrainStop(int index, ref TimeSpan? prevTimeSpan, ref int days)
{
var st = new TrainStop();
st.Left = start_time[0] == '-' ? null : start_time.ToTimeSpanNullable();
st.Arrive = arrive_time[0] == '-' ? null : arrive_time.ToTimeSpanNullable();
if (days > 0)
{
if (st.Left != null)
st.Left = st.Left.Value.AddDays(days);
if (st.Arrive != null)
st.Arrive = st.Arrive.Value.AddDays(days);
}
if (prevTimeSpan != null && st.Arrive != null)
{
if (st.Arrive.Value < prevTimeSpan.Value)
{
st.Arrive = st.Arrive.Value.Add(new TimeSpan(24, 0, 0));
if (st.Left != null)
st.Left = st.Left.Value.Add(new TimeSpan(24, 0, 0));
days++;
}
prevTimeSpan = st.Arrive;
}
st.Days = days;
if (prevTimeSpan != null && st.Left != null)
{
if (st.Left.Value < prevTimeSpan.Value)
{
st.Left = st.Left.Value.Add(new TimeSpan(24, 0, 0));
days++;
}
}
prevTimeSpan = st.Left;
st.Index = index;
st.Code = StationManager.Instance.Storage.StationNameMap.GetValue(station_name).SelectValue(s => s.Code) ?? "";
return st;
}
}
private string _dataFolder;
private string _sourceArchiveFolder;
/// <summary>
/// 创建 <see cref="WebDataProvider" /> 的新实例(WebDataProvider)
/// </summary>
public WebDataProvider()
{
_dataFolder = PathUtility.Combine(RunTimeContext.DataStorageRoot, "trains");
_sourceArchiveFolder = PathUtility.Combine(_dataFolder, "sources");
Directory.CreateDirectory(_dataFolder);
Directory.CreateDirectory(_sourceArchiveFolder);
}
public void LoadTrainInfo(IndexStorage indexStorage, Action saveCallback)
{
Trace.TraceInformation("[TRAIN_DATA_WEB_PROVIDER] 正在获得最新车次信息");
var html = new HttpWebClient().Create(HttpMethod.Get, "https://kyfw.12306.cn/otn/resources/js/query/train_list.js?scriptVersion=" + (new Random().NextDouble() + 1).ToString("#0.00000"), null, null, "").Send();
if (!html.IsValid())
{
Trace.TraceError("[TRAIN_DATA_WEB_PROVIDER] 车次信息获得失败。错误:{0}", html.Exception);
return;
}
Trace.TraceInformation("[TRAIN_DATA_WEB_PROVIDER] 正在缓存原始来源");
File.WriteAllText(PathUtility.Combine(_sourceArchiveFolder, "train_list_" + indexStorage.Version + ".js"), html.Result);
Trace.TraceInformation("[TRAIN_DATA_WEB_PROVIDER] 原始来源缓存成功");
Trace.TraceInformation("[TRAIN_DATA_WEB_PROVIDER] 正在分析车次信息");
//移除非JSON部分
var fromIndex = html.Result.IndexOf('{');
var endIndex = html.Result.LastIndexOf('}');
var body = html.Result.Substring(fromIndex, endIndex - fromIndex + 1);
var data = JsonConvert.DeserializeObject<TrainListData>(body);
var station = StationManager.Instance.Storage.StationNameMap;
var nowDate = DateTime.Now.Date;
foreach (var date in data.Keys)
{
if (DateTime.Parse(date) < nowDate)
{
Trace.TraceInformation($"[TRAIN_DATA_WEB_PROVIDER] 忽略过期日期:{date}");
continue;
}
var dateSt = data[date];
var curStorage = indexStorage.TrainInfoStorages.GetValue(date);
if (curStorage == null)
{
curStorage = new TrainInfoStorage();
curStorage.Init();
curStorage.InitStationTrainData();
indexStorage.TrainInfoStorages.Add(date, curStorage);
}
//fix duplicate key
//var dd = curStorage.Trains.GroupBy(s => s.Key).Select(s => new {s.Key, list = s.ToArray()}).Where(s => s.list.Length > 1).ToArray();
//if (dd.Length > 0)
//{
// foreach (var x1 in dd)
// {
// var dfrist = x1.list[0];
// foreach (var dt in x1.list.Skip(1).ToArray())
// {
// dfrist.Code = dt.Code;
// curStorage.Trains.Remove(dt);
// }
// }
//}
var allkeys = curStorage.Trains.ToDictionary(s => s.Key, StringComparer.OrdinalIgnoreCase);
//所有的车次
var alltrains = dateSt.Values.SelectMany(s => s).Select(s =>
{
var i = new Train();
var m = Regex.Match(s.station_train_code, @"([A-Z]\d+)\((.*?)\-(.*?)\)");
i.Code = m.GetGroupValue(1);
i.From = station.GetValue(m.Groups[2].Value.Trim()).SelectValue(x => x.Code);
i.To = station.GetValue(m.Groups[3].Value.Trim()).SelectValue(x => x.Code);
i.Id = s.train_no;
return !string.IsNullOrEmpty(i.From) && !string.IsNullOrEmpty(i.To) ? i : null;
}).ExceptNull().ToArray();
var index = 0;
foreach (var train in alltrains)
{
if (allkeys.ContainsKey(train.Key))
{
//增加车次编号
allkeys[train.Key].Code = train.Code;
continue;
}
//加载车次停靠站信息
if (!LoadTrainStopInfo(++index, alltrains.Length, date, train) || curStorage.HashStore.Contains(train.TrainHash))
continue;
train.Init();
curStorage.HashStore.Add(train.TrainHash);
allkeys.Add(train.Key, train);
TrainInfoManager.Instance.DataStore.TrainData.AddOrUpdate(train.TrainHash, train);
}
indexStorage.DateIndices.SafeAdd(date);
indexStorage.TrainInfoStorages.AddOrUpdate(date, curStorage);
if (index > 0)
{
curStorage.Init();
curStorage.InitStationTrainData();
//缓存
Trace.TraceInformation("[TRAIN_DATA_WEB_PROVIDER] 开始缓存车次数据 {0}", date);
var filepath = PathUtility.Combine(_dataFolder, date + ".json");
File.WriteAllText(filepath, JsonConvert.SerializeObject(curStorage));
Trace.TraceInformation("[TRAIN_DATA_WEB_PROVIDER] 车次数据缓存成功!");
if (saveCallback != null)
saveCallback();
}
else
{
Trace.TraceInformation("[TRAIN_DATA_WEB_PROVIDER] 车次 {0} 数据无变化", date);
}
GC.Collect();
}
Trace.TraceInformation("[TRAIN_DATA_WEB_PROVIDER] 车次信息分析完成,已加载{0}日车次信息", indexStorage.DateIndices.Count);
return;
}
public TrainInfoStorage LoadCache(string date)
{
Trace.TraceInformation("[TRAIN_DATA_WEB_PROVIDER] 正在加载文件缓存 {0}", date);
var file = PathUtility.Combine(_dataFolder, date + ".json");
if (File.Exists(file))
{
return JsonConvert.DeserializeObject<TrainInfoStorage>(File.ReadAllText(file));
}
return null;
}
internal bool LoadTrainStopInfo(int index, int totalcount, string date, Train train)
{
Trace.TraceInformation("[TRAIN_STOP_INFO_LOAER] [{2:#0}/{3:#0}] 正在加载车次 {1}/{0} 信息", train.Code, date, index, totalcount);
var tryCount = 0;
while (tryCount++ < 50)
{
var ctx = new HttpWebClient().Create(
HttpMethod.Get,
string.Format("https://kyfw.12306.cn/otn/czxx/queryByTrainNo?train_no={0}&from_station_telecode={1}&to_station_telecode={2}&depart_date={3}", train.Id, train.From, train.To, date),
null,
null,
new
{
data = new { data = new List<TrainStopWebInfo>() }
}).Send();
if (!ctx.IsValid() || ctx.Result.data == null)
{
Trace.TraceWarning("[TRAIN_STOP_INFO_LOAER] 第 {0} 次加载失败!", tryCount);
Thread.Sleep(1000);
continue;
}
TimeSpan? prevTimeSpan = null;
int days = 0;
train.TrainStops = ctx.Result.data.data.Select((obj, idx) => obj.ToTrainStop(idx + 1, ref prevTimeSpan, ref days)).ToList();
Thread.Sleep(200);
break;
}
Trace.TraceInformation("[TRAIN_STOP_INFO_LOAER] 车次 {1}/{0} 信息加载过程结束", train.Code, date);
return train.TrainStops != null && train.TrainStops.Count > 0;
}
}
}