271 lines
8.4 KiB
C#
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;
|
|
}
|
|
|
|
}
|
|
}
|