最后
好了,这就是整理的前端从入门到放弃的学习笔记,还有很多没有整理到,我也算是边学边去整理,后续还会慢慢完善,这些相信够你学一阵子了。
做程序员,做前端工程师,真的是一个学习就会有回报的职业,不看出身高低,不看学历强弱,只要你的技术达到应有的水准,就能够得到对应的回报。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
学习从来没有一蹴而就,都是持之以恒的,正所谓活到老学到老,真正懂得学习的人,才不会被这个时代的洪流所淘汰。
- 小方块的结构为:
<tdclass=“b”>
这里为日期
如果有余票则显示余票数量
-
td的样式名为
a
代表不可选 -
样式名为
e
代表已满 -
样式名为
d
代表已购 -
样式名为
b
则是我们要找的,代表可选,也就是有余票
到这一步,整个购票流程就清楚了
到时候我们通过Node.js请求的时候,处理返回数据,用正则去判断是否有余票的class名 b
,有余票的话,在获取div里面的余票数量内容就Ok了
Node.js 请求目标接口
==============
分析需要开发的功能点
写代码之前我们需要想好功能点,我们需要什么功能:
-
请求余票接口
-
定时请求任务
-
有余票则自动请求购票接口下订单
-
调用腾讯云短信api接口发送短信通知
-
多个用户抢票功能
-
抢某个日期的票
首先 mkdir ticket
创建名为ticket的文件夹,接着 cd ticket
进入文件夹 npm init
一路瞎几把回车也无妨。下面开始安装依赖,根据上面的功能需求,我们大概需要:
- 请求工具,这里看个人习惯,你也可以使用原生的
http.request
,我这里选择用的是axios
,毕竟axios
在node端底层也是调用http.request
cnpm install axios --save
- 定时任务
node-schedule
cnpm install node-schedule --save
- node端选择dom节点工具
cheerio
cnpm install cheerio --save
- 腾讯发短信的依赖包
qcloudsms_js
cnpm install qcloudsms_js
- 热更新包,诺豆的妈妈,
nodemon
(其实不用也可以)
cnpm install nodemon --save-dev
开发请求余票接口
接着 touch index.js
创建核心js文件,开始编码:
首先引入所有依赖
const axios = require(‘axios’)
const querystring = require(“querystring”); //序列化对象,用qs也行,都一样
let QcloudSms= require(“qcloudsms_js”);
let cheerio = require(‘cheerio’);
let schedule = require(‘node-schedule’);
然后我们先定义请求参数,来一个obj
let obj = {
data: {
lineId: 111130, //路线id
vehTime: 0722, //发车时间,
startTime: 0751, //预计上车时间
onStationId: 564492, //预定的站点id
offStationId: 17990,//到站id
onStationName: ‘宝安交通运输局③’, //预定的站点名称
offStationName: “深港产学研基地”,//预定到站名称
tradePrice: 0,//总金额
saleDates: ‘17’,//车票日期
beginDate: ‘’,//订票时间,滞空,用于抓取到余票后填入数据
},
phoneNumber: 123123123, //用户手机号,接收短信的手机号
cookie: ‘JSESSIONID=TESTCOOKIE’, // 抓取到的cookie
day: “17”//定17号的票,这个主要是用于抢指定日期的票,滞空则为抢当月所有余票
}
接着声明一个名为 queryTicket
的类,为啥要用类呢,因为基于第五个需求点,多个用户抢票的时候,我们分别 new
一下就行了,
同时我们希望能够记录请求余票的次数,和当抢到票后自动停止查询余票的操作,所以给他加上个计数变量 times
和是否停止的变量,布尔值 stop
编写代码:
class QueryTicket{
/**
*Creates an instance of QueryTicket.
-
@param {Object} { data, phoneNumber, cookie, day }
-
@param data {Object} 请求余票接口的requery参数
-
@param phoneNumber {Number} 用户手机号,短信需要用到
-
@param cookie {String} cookie信息
-
@params day {String} 某日的票,如’18’
-
@memberof QueryTicket 请求余票接口
*/
constructor({ data, phoneNumber, cookie, day }) {
this.data = data
this.cookie = cookie
this.day = day
this.phoneNumber = phoneNumber
this.postData = querystring.stringify(data)
this.times = 0; //记录次数
let stop = false //通过特定接口才能修改stop值,防止外部随意串改
this.getStop = function () { //获取是否停止
return stop
}
this.setStop = function (ifStop) { //设置是否停止
stop = ifStop
}
}
}
下面开始定义原型方法,为了方便维护,我们把逻辑拆分成各个函数
class QueryTicket{
constructor({ data, phoneNumber, cookie, day }) {
//constructor代码…
}
init(){}//初始化
handleQueryTicket(){}//查询余票的逻辑
requestTicket(){} //调用查询余票接口
handleBuyTicket(){} //购票相关逻辑
requestOrder(){}//调用购票接口
handleInfoUser(){}//通知用户的逻辑
sendMSg(){} //发短信接口
}
所有数据都是基于查询余票的操作,因此我们先开发这部分功能
class QueryTicket{
constructor({ data, phoneNumber, cookie, day }) {
//constructor代码…
}
//初始化,因为涉及到异步请求,所以我们使用async await
async init(){
let ticketList = await this.handleQueryTicket() //返回查询到的余票数组
}
//查询余票的逻辑
handleQueryTicket(){
let ticketList = [] //余票数组
let res = await this.requestTicket()
this.times++ //计数器,记录请求查询多少次
let str = res.data.replace(/\/g, “”) //格式化返回值
let $ = cheerio.load(<div class="main">${str}</div>
) // cheerio载入查询接口response的html节点数据
let list = $(“.main”).find(“.b”) //查找是否有余票的dom节点
// 如果没有余票,打印出请求多少次,然后返回,不执行下面的代码
if (!list.length) {
console.log(用户${this.phoneNumber}:无票,已进行${this.times}次
)
return
}
// 如果有余票
list.each((idx, item) => {
let str = KaTeX parse error: Expected 'EOF', got '&' at position 46: …21</span><span>&̲x4F59;0
//最后一个span 的内容其实"余0",也就是无票,只不过是被转码了而已
//因此要在下一步对其进行格式化
let arr = str.split(/|</span>|余/).filter(item => !!item === true)
let data = {
day: arr[0],
ticketLeft: arr[1]
}
//如果是要抢指定日期的票
if (this.day) {
//如果有指定日期的余票
if (parseInt(data.day) === parseInt(data.day)) {
ticketList.push(data)
}
} else {
//如果不是,则返回查询到的所有余票
ticketList.push(data)
}
})
return ticketList
}
//调用查询余票接口
requestTicket(){
return axios.post(‘http://weixin.xxxx.net/ebus/front/wxQueryController.do?BcTicketCalendar’, this.postData, {
headers: {
‘Content-Type’: ‘application/x-www-form-urlencoded’,
‘User-Agent’: “Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365 MicroMessenger/5.4.1 NetType/WIFI”,
“Cookie”: this.cookie
}
})
}
handleBuyTicket(){} //购票相关逻辑
requestOrder(){}//调用购票接口
handleInfoUser(){}//通知用户的逻辑
sendMSg(){} //发短信接口
}
来解释下那行正则, cheerio
抓取到的dom是长这样的,第一个 span
内容是日期,第二个是余票数量 所以我们要把它格式化变成这种数组,也就是
ticketList
开发购票功能
首先我们在 init
方法里做个判断,如果有余票才去购票,没有余票购个毛
class QueryTicket{
constructor({ data, phoneNumber, cookie, day }) {
//constructor代码…
}
//初始化
async init(){
let ticketList = await this.handleQueryTicket()
//如果有余票
if (ticketList.length) {
//把余票传入购票逻辑方法,返回短信通知所需要的数据
let resParse = await this.handleBuyTicket(ticketList)
}
}
//查询余票的逻辑
async handleQueryTicket(){
// 查询余票代码…
}
//调用查询余票接口
requestTicket(){
//调用查询余票接口代码…
}
//购票相关逻辑
async handleBuyTicket(ticketList){
let year = new Date().getFullYear() //年份,
let month = new Date().getMonth() + 1//月份,拼接购票日期用得上,因为余票接口只返回几号
let {
onStationName,//起始站点名
offStationName,//结束站点名
lineId,//线路id
vehTime,//发车时间
startTime,//预计上车时间
onStationId,//上车的站台id
offStationId //到站的站台id
} = this.data // 初始化的数据
let station = ${onStationName}-${offStationName}
//站点,发短信时候用到:“宝安交通局-深港产学研基地”
let dateStr = “”; //车票日期
let tickAmount = “”//总张数
ticketList.forEach(item => {
dateStr = dateStr + ${year}-${month}-${item.day},
tickAmount = tickAmount + ${item.ticketLeft}张,
})
let buyTicket = {
lineId,//线路id
vehTime,//发车时间
startTime,//预计上车时间
onStationId,//上车的站点id
offStationId,//目标站点id
tradePrice: ‘5’, //金额
saleDates: dateStr.slice(0, -1),
payType: ‘2’//支付方式,微信支付
}
// 调用购票接口
let data = querystring.stringify(buyTicket)
let res = await this.requestOrder(data) //返回json数据,是否购票成功等等
//把发短信所需要数据都要传入
return Object.assign({}, JSON.parse(res.data), { queryParam: { dateStr, tickAmount, startTime, station } })
}//购票相关逻辑
//调用购票接口
requestOrder(obj){
return axios.post(‘http://weixin.xxxx.net/ebus/front/wxQueryController.do?BcTicketBuy’, obj, {
headers: {
‘Content-Type’: ‘application/x-www-form-urlencoded’,
‘User-Agent’: “Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365 MicroMessenger/5.4.1 NetType/WIFI”,
“Cookie”: this.cookie
}
})
}
handleInfoUser(){}//通知用户的逻辑
sendMSg(){} //发短信接口
}
到这里,查询余票,购票这两个核心操作已经完成。
目前还剩下,如何通知用户是否购票成功。
之前我尝试过使用qq邮箱的smtp服务,抢票成功后发送邮件通知,但是我觉得吧,并不好用,主要是我没有打开邮箱的习惯,没网也收不到,所以,并没有采纳这个方案。
加上之前我注册过企业认证的公众号,腾讯云免费送了我1000条短信通知,而且短信也比较直观,所以我这里就安装腾讯云的SDK,部署了一套发短信的功能。
腾讯云短信的相关内容
其实看看文档就行了,我也是copy文档,注意看短信单发那部分
https://cloud.tencent.com/document/product/382/3772
如果跟我一样有企业认证的话,看快速入门这里就行了,一步步跟着操作 看下短信正文,
{Number}
这些里面的数字是变量。
就是说短信的模板是固定的,但是里面有 {Number}
的内容可以自定义
调用的时候,里面的数字对应着传过去的参数数组序号,{1}代表数组[0]参数,以此类推
提交审核,审核一般很快就通过,也就是几十万毫秒吧
开发通知功能
class QueryTicket{
constructor({ data, phoneNumber, cookie, day }) {
//constructor代码…
}
//初始化
async init(){
let ticketList = await this.handleQueryTicket()
//如果有余票
if (ticketList.length) {
//把余票传入购票逻辑方法,返回短信通知所需要的数据
let resParse = await this.handleBuyTicket(ticketList)
//执行通知逻辑
this.handleInfoUser(resParse)
}
}
//查询余票的逻辑
async handleQueryTicket(){
// 查询余票代码…
}
//调用查询余票接口
requestTicket(){
//调用查询余票接口代码…
}
//购票相关逻辑
async handleBuyTicket(ticketList){
//购票代码…
}
//调用购票接口
requestOrder(obj){
//购票接口请求代码…
}
//通知用户的逻辑
async handleInfoUser(parseData){
//获取上一步购票的response数据和我们拼接的数据
let { returnCode, returnData: { main: { lineName, tradePrice } }, queryParam: { dateStr, tickAmount, startTime, station } } = parseData
//如果购票成功,则返回500
if (returnCode === “500”) {
let res = await this.sendMsg({
dateStr, //日期
tickAmount: tickAmount.slice(0, -1), //总张数
station, //站点
lineName, //巴士名称/路线名称
tradePrice,//总价
startTime,//出发时间
phoneNumber: this.phoneNumber,//手机号
})
//如果发信成功,则不再进行抢票操作
if (res.result === 0&& res.errmsg === “OK”) {
this.setStop(true)
} else {
//失败不做任何操作
console.log(res.errmsg)
}
} else {
//失败不做任何操作
console.log(resParse[‘returnInfo’])
}
}
//发短信接口
sendMSg(){
let { dateStr, tickAmount, station, lineName, phoneNumber, startTime, tradePrice } = obj
let appid = 140034324; // SDK AppID 以1400开头
// 短信应用 SDK AppKey
let appkey = “asdfdsvajwienin23493nadsnzxc”;
// 短信模板 ID,需要在短信控制台中申请
let templateId = 7839; // NOTE: 这里的模板ID7839
只是示例,真实的模板 ID 需要在短信控制台中申请
// 签名
let smsSign = “测试短信”; // NOTE: 签名参数使用的是签名内容
,而不是签名ID
。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台申请
// 实例化 QcloudSms
let qcloudsms = QcloudSms(appid, appkey);
let ssender = qcloudsms.SmsSingleSender();
// 这里的params就是短信里面可以自定义的内容,也就是填入{1}{2}…的内容
let params = [dateStr, station, lineName, startTime, tickAmount, tradePrice];
//用promise来封装下异步操作
return new Promise((resolve, reject) => {
ssender.sendWithParam(86, phoneNumber, templateId, params, smsSign, “”, “”, function (err, res, resData) {
if (err) {
reject(err)
} else {
resolve(resData)
}
});
})
}
}
总结
=============================================================
从转行到现在,差不多两年的时间,虽不能和大佬相比,但也是学了很多东西。我个人在学习的过程中,习惯简单做做笔记,方便自己复习的时候能够快速理解,现在将自己的笔记分享出来,和大家共同学习。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
个人将这段时间所学的知识,分为三个阶段:
第一阶段:HTML&CSS&JavaScript基础
第二阶段:移动端开发技术
第三阶段:前端常用框架
-
推荐学习方式:针对某个知识点,可以先简单过一下我的笔记,如果理解,那是最好,可以帮助快速解决问题;如果因为我的笔记太过简陋不理解,可以关注我以后我还会继续分享。
-
大厂的面试难在,针对一个基础知识点,比如JS的事件循环机制,不会上来就问概念,而是换个角度,从题目入手,看你是否真正掌握。所以对于概念的理解真的很重要。