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 { 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=1.1051", 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); var curTrainList = curStorage.SelectValue(s => s.Trains); //所有的车次 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; }).Except(curTrainList ?? new List()).ExceptNull().ToList(); //加载车次停靠站信息 var index = 0; foreach (var alltrain in alltrains) { LoadTrainStopInfo(++index, alltrains.Count, date, alltrain); } if (curStorage == null) { curStorage = new TrainInfoStorage(alltrains); indexStorage.TrainInfoStorages.Add(date, curStorage); } else { curTrainList.AddRange(alltrains); curStorage.Init(); } //缓存 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] 车次数据缓存成功!"); 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); } } }