我是路标
本系列文章
- 12306订票客户端 FOR .NET 演示项目 【7】登录9年前 (2015-08-18)
- 12306订票客户端 FOR .NET 演示项目 【6】验证码输入9年前 (2015-08-12)
- 12306订票客户端 FOR .NET 演示项目 【5】获得余票数据9年前 (2015-06-10)
- 12306订票客户端 FOR .NET 演示项目 【4】界面框架&基础数据初始化9年前 (2015-06-08)
- 12306订票客户端 FOR .NET 演示项目 【3】流程分析和项目规划9年前 (2015-05-28)
- 12306订票客户端 FOR .NET 演示项目 【2】准备工具9年前 (2015-05-22)
- 12306订票客户端 FOR .NET 演示项目 【1】项目概况9年前 (2015-05-19)
FSLIB.NETWORK 网络库系列文章
- 12306订票助手.NET V10.6.1 发布8年前 (2016-09-01)
- 开源 FSLIB.NETWORK 库 2.2.0.08年前 (2016-08-02)
- FSLIB.NETWORK手册(1) · 基本概念和流程8年前 (2016-05-05)
- 原创FSLib.Network库更新 2.0.0 版8年前 (2016-04-05)
- 原创FSLib.Network库更新 1.6.0版(目前专注于HTTP的高性能高易用性网络库)9年前 (2015-12-13)
- 玩具系列:批量QQ群签到工具v2 (暂时屏蔽自定义位置功能)9年前 (2015-08-29)
- 玩具系列:批量QQ群签到工具(支持自定义位置)9年前 (2015-08-28)
- 12306订票助手.NET 8.0.8 发布9年前 (2015-08-21)
- 12306订票客户端 FOR .NET 演示项目 【7】登录9年前 (2015-08-18)
- 12306订票客户端 FOR .NET 演示项目 【6】验证码输入9年前 (2015-08-12)
- 12306订票客户端 FOR .NET 演示项目 【5】获得余票数据9年前 (2015-06-10)
- 原创FSLib.Network库发布 1.5 版9年前 (2015-06-09)
- 12306订票客户端 FOR .NET 演示项目 【4】界面框架&基础数据初始化9年前 (2015-06-08)
- 12306订票客户端 FOR .NET 演示项目 【3】流程分析和项目规划9年前 (2015-05-28)
- 12306订票客户端 FOR .NET 演示项目 【2】准备工具9年前 (2015-05-22)
- 12306订票客户端 FOR .NET 演示项目 【1】项目概况9年前 (2015-05-19)
- 原创FSLib.Network库发布 1.4 版9年前 (2015-05-08)
- 放一个抓取网页的信息监控小工具源码9年前 (2015-04-27)
- FSLib.Network网络库使用教程[2] 实例教程·美女们快到硬盘里来!9年前 (2015-01-30)
- FSLib.Network网络库使用教程[1] 基本使用9年前 (2015-01-19)
- 原创FSLib.Network库(目前专注于HTTP的高性能高易用性网络库)9年前 (2015-01-18)
3.1 本章前言
从本章开始,将会涉及到具体的开发过程。工欲善其事,必先利其器。话虽如此,但准备工具之前知己知彼还是比较重要的……
3.2 12306流程分析
这里我们从一个典型的用户订票流程来考虑并设计流程。
首先用户进入了查票入口,选择日期和地点。这里以经典的北京–上海为例,时间选择的是7月20日,这里的信息不是关键的信息,可以根据你自己的需要来设置。
点击查询后,会跳转到查询页面,并显示查询结果。
这里假定我们要定G1次列车,点击预定按钮,此时会发现弹出了登录对话框。
输入用户名和密码并选择验证码后登录,相信我你也许会看到这个对话框。
这时候你要做的事情应该是刷新页面后重试。这里也看到,虽然提示你如果正在使用XXXX……但其实是废话,在12306的官网上这种事情也司空见惯。
登录成功后会跳转到订单页面。
选择乘客并选择验证码后,点击提交订单,会弹出来提交确认。
点击确认后,提交订单,此时进入排队界面。
排队如果成功的话,那么就会自动跳转到付款页面。
至此,一个比较常规的订单流程就完成了。
3.3 保存Fiddler抓包记录
从Fiddler的菜单“File”-“Save”中,保存所有的记录以便于后续分析。
3.4 需要的基础数据和流程分析
从之前的流程记录中,我们可以看到大概的流程是这样的。
用户可能有两种操作,先登录再查票,或先查票再登录,登录后提交订单。为了简便起见,这里始终直接允许查票,点击预定的时候再看看是否已经登录,没登录的话提示登录后再提交。
从图中右侧可以看到需要使用的数据。在不同的阶段需要用到不同的数据对象,使用时机和准备时机大致分为以下几种:
- 车站数据:属于基础数据,使用过程中不会变。因此我们把这部分数据的初始化放在程序一开始就进行
- 余票信息:用来提交订单并供用户查看的车票信息,在查票的过程中出现,每次查询均会获得余票信息
- 联系人数据:提交订单的时候需要使用,和账户有关系,因此这部分数据在登录后加载一次,并且使用延迟加载(需要的时候再加载)
- 预定信息:选中准备提交的联系人以及其票种、席别信息,提交订单的时候用到,并且需要用户交互选择输入
- 排队信息:在订单提交成功后、系统出票前出现,用于检查排队进度
3.5 系统结构和模块设计
为了让开发简便流程清晰,对各功能组件进行划分,做到职责分明。根据以上的流程分析,我们把整个系统分为以下各个模块。
分类 | 模块名 | 功能说明 |
---|---|---|
界面(UI) | UI界面 | 负责所有数据的展示、用户交互操作等 |
网络(Network) | 网络模块 | 负责所有请求的发送和接收,以及HTTP请求状态的维护,将会被会话模块使用 |
服务(Service) | 会话模块 | 维护用户的会话以及相关联的数据发送、接受等操作,同时维护在中间操作所需要的额外数据信息(如动态密钥等) |
服务(Service) | 查票模块 | 完成查票的请求发送、接收和结果转换 |
服务(Service) | 订单提交模块 | 负责获得用户输入并组织数据以提交订单 |
服务(Service) | 联系人模块 | 负责加载当前账户的联系人信息 |
服务(Service) | 订单排队模块 | 在订单提交成功后,完成对排队信息的检测和等待,并反馈最终结果 |
服务(Service) | 车站数据管理模块 | 负责加载、检索车站信息 |
服务(Service) | 验证码模块 | 负责加载验证码并验证用户验证码是否输入正确 |
服务(Service) | 动态密钥模块 | 负责维护整个会话中所需要使用的任何额外状态数据 |
3.6 项目结构初始化
有了以上的划分后,先暂时把所有的项目结构规划出来。
3.6.1 Network/NetClient
namespace Ticket12306Demo.Network { using FSLib.Network.Http; /// <summary> /// 基于FSLIB.NETWORK的网络层扩展,进一步抽象以便于后期提供功能 /// </summary> class NetClient : HttpClient { /// <summary> /// 默认构造函数 /// </summary> public NetClient() { } } }
3.6.2 Service/Session
这是会话模块,自己实现并维护了一个NetClient
对象,封装了当前的状态和HTTP会话模态。
namespace Ticket12306Demo.Service { using Ticket12306Demo.Network; /// <summary> /// 表示当前的12306登录会话,以及一些必须的状态信息。 /// </summary> class Session { public Session() { NetClient = new NetClient(); } /// <summary> /// 获得当前使用的网络对象,每个网络对象都是会话关联的。 /// </summary> public NetClient NetClient { get; private set; } /// <summary> /// 获得当前是否已经登录 /// </summary> public bool IsLogined { get; private set; } } }
3.6.3 Service/ServiceBase
这是一个抽象基类,用作其它服务的基础对象,用来提供一些常规的状态信息和交互服务。
namespace Ticket12306Demo.Service { using Ticket12306Demo.Network; /// <summary> /// 通用的服务基类,提供一些额外的基础服务 /// </summary> abstract class ServiceBase { protected ServiceBase(ServiceContext context) { ServiceContext = context; } /// <summary> /// 获得当前的服务上下文 /// </summary> public ServiceContext ServiceContext { get; private set; } /// <summary> /// 获得当前关联的12306会话 /// </summary> public Session Session { get { return ServiceContext.Session; } } /// <summary> /// 获得当前关联的网络访问对象 /// </summary> public NetClient NetClient { get { return Session.NetClient; } } /// <summary> /// 获得当前是否已经登录 /// </summary> public bool IsLogined { get { return Session.IsLogined; } } } }
3.6.4 Service下的其它服务模块
其它服务模块均基于 ServiceBase,完成各指定的功能。当然,现在还都是空架子。当前所有的模块都类似于以下的内容。
namespace Ticket12306Demo.Service { /// <summary> /// 车票查询服务 /// </summary> class TicketQueryService : ServiceBase { public TicketQueryService(ServiceContext context) : base(context) { } } }
3.6.5 Service/ServiceContext
由于模块粒度较细,为了便于在UI层中使用也为了便于维护状态以及模块间调用,这里使用一个统一的对象进行上下文维护以及所有模块的维护。这个模块的构造函数中创建了所有的默认模块实例。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Ticket12306Demo.Service { /// <summary> /// 服务状态上下文 /// </summary> class ServiceContext { public ServiceContext() { Session = new Session(); DynamicDataService = new DynamicDataService(this); OrderQueueService = new OrderQueueService(this); OrderSubmitService = new OrderSubmitService(this); PassengerService = new PassengerService(this); StationDataService = new StationDataService(this); VerifyCodeService = new VerifyCodeService(this); TicketQueryService = new TicketQueryService(this); } /// <summary> /// 获得当前的会话状态 /// </summary> public Session Session { get; private set; } /// <summary> /// 获得当前的动态密钥服务 /// </summary> public DynamicDataService DynamicDataService { get; private set; } /// <summary> /// 获得当前的订单排队服务 /// </summary> public OrderQueueService OrderQueueService { get; private set; } /// <summary> /// 获得当前的订单提交服务 /// </summary> public OrderSubmitService OrderSubmitService { get; private set; } /// <summary> /// 获得当前的联系人服务 /// </summary> public PassengerService PassengerService { get; private set; } /// <summary> /// 获得当前的车站数据服务 /// </summary> public StationDataService StationDataService { get; private set; } /// <summary> /// 获得当前的验证码服务 /// </summary> public VerifyCodeService VerifyCodeService { get; private set; } /// <summary> /// 获得当前的查票服务 /// </summary> public TicketQueryService TicketQueryService { get; private set; } } }
3.6.6 UI
UI目录下的均为界面的东西,根据类型的不同进行了大致的划分(控件or窗体),现在仅有一个空空如也的MainForm
。
3.7 本章总结
本章到此结束,下章开始正式开工写代码
等着4出来
12306订票客户端 FOR .NET 演示项目 【4】敲代码
那个 Make Javascript Pretty 在哪? 没找到
确定你是最新的Fiddler,在rules菜单下。
Fiddler 4.5.1.0 还是没有
那个是个Fiddler插件,需要装一下
真是效率