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; class WebDataProvider { class TrainListInfoItem { public string station_train_code { get; set; } public string train_no { get; set; } } class TrainListData : Dictionary>> { } 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; /// /// 创建 的新实例(WebDataProvider) /// public WebDataProvider() { _dataFolder = PathUtility.Combine(RunTimeContext.DataStorageRoot, "trains"); _sourceArchiveFolder = PathUtility.Combine(_dataFolder, "sources"); Directory.CreateDirectory(_dataFolder); Directory.CreateDirectory(_sourceArchiveFolder); } public void LoadTrainInfo(IndexStorage indexStorage) { Trace.TraceInformation("[TRAIN_DATA_WEB_PROVIDER] 正在获得最新车次信息"); var html = new HttpClient().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(body); var station = StationManager.Instance.Storage.StationNameMap; foreach (var date in data.Keys) { 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; } //加载车次停靠站信息 LoadTrainStopInfo(++index, alltrains.Length, date, train); if (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); } 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] 车次数据缓存成功!"); } else { Trace.TraceInformation("[TRAIN_DATA_WEB_PROVIDER] 车次 {0} 数据无变化", date); } indexStorage.DateIndices.SafeAdd(date); indexStorage.TrainInfoStorages.AddOrUpdate(date, curStorage); 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(File.ReadAllText(file)); } return null; } internal void 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 HttpClient().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() } }).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); } } }