2024年前端最全[实战] 实现抢票小工具&短信通知提醒(1),手把手教你在前端-Studio上分析内存泄漏

部署运行你感兴趣的模型镜像

前端面试题汇总

JavaScript

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

性能

linux

前端资料汇总

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>|&#x4F59;/).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)

}

});

})

}

}

如果发信成功,返回 result:0 

到这里,大部分需求已经完成了,还剩下一个定时任务

定时任务


也声明一个类,这里我们用到的是 schedule

// 定时任务

class SetInter{

constructor({ timer, fn }) {

this.timer = timer // 每几秒执行

this.fn = fn //执行的回调

this.rule = new schedule.RecurrenceRule(); //实例化一个对象

this.rule.second = this.setRule() // 调用原型方法,schedule的语法而已

this.init()

}

setRule() {

let rule = [];

let i = 1;

while (i < 60) {

rule.push(i)

i += this.timer

}

return rule //假设传入的timer为5,则表示定时任务每5秒执行一次

// [1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56]

}

init() {

schedule.scheduleJob(this.rule, () => {

this.fn() // 定时调用传入的回调方法

});

}

}

多个用户抢票


假设我们有两个用户要抢票,所以定义两个obj,实例化下 QueryTicket

data: { //用户1

lineId: 111130,

vehTime: 0722,

startTime: 0751,

onStationId: 564492,

offStationId: 17990,

onStationName: ‘宝安交通运输局③’,

offStationName: “深港产学研基地”,

tradePrice: 0,

saleDates: ‘’,

beginDate: ‘’,

},

phoneNumber: 123123123,

cookie: ‘JSESSIONID=TESTCOOKIE’,

day: “17”

}

let obj2 = { //用户2

data: {

lineId: 134423,

vehTime: 1820,

startTime: 1855,

onStationId: 4322,

offStationId: 53231,

onStationName: ‘百度国际大厦’,

offStationName: “裕安路口”,

tradePrice: 0,

saleDates: ‘’,

beginDate: ‘’,

},

phoneNumber: 175932123124,

cookie: ‘JSESSIONID=TESTCOOKIE’,

day: “”

}

总结

我在成长过程中也是一路摸爬滚打,没有任何人的指点,所以走的很艰难。例如在大三的时候,如果有个学长可以阶段性的指点一二,如果有已经工作的师兄可以告诉我工作上需要什么,我应该前面的三年可以缩短一半;后来去面试bat,失败了有5、6次,每次也不知道具体是什么原因,都是靠面试回忆去猜测可能是哪方面的问题,回来学习和完善,当你真正去招人的时候,你就会知道面试记录是多么重要,面试官可以从面试记录里看到你的成长,总是去面试,总是没有成长,就会被定义为缺乏潜力。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

image
image

StationName: “深港产学研基地”,

tradePrice: 0,

saleDates: ‘’,

beginDate: ‘’,

},

phoneNumber: 123123123,

cookie: ‘JSESSIONID=TESTCOOKIE’,

day: “17”

}

let obj2 = { //用户2

data: {

lineId: 134423,

vehTime: 1820,

startTime: 1855,

onStationId: 4322,

offStationId: 53231,

onStationName: ‘百度国际大厦’,

offStationName: “裕安路口”,

tradePrice: 0,

saleDates: ‘’,

beginDate: ‘’,

},

phoneNumber: 175932123124,

cookie: ‘JSESSIONID=TESTCOOKIE’,

day: “”

}

总结

我在成长过程中也是一路摸爬滚打,没有任何人的指点,所以走的很艰难。例如在大三的时候,如果有个学长可以阶段性的指点一二,如果有已经工作的师兄可以告诉我工作上需要什么,我应该前面的三年可以缩短一半;后来去面试bat,失败了有5、6次,每次也不知道具体是什么原因,都是靠面试回忆去猜测可能是哪方面的问题,回来学习和完善,当你真正去招人的时候,你就会知道面试记录是多么重要,面试官可以从面试记录里看到你的成长,总是去面试,总是没有成长,就会被定义为缺乏潜力。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

[外链图片转存中…(img-GphovdFd-1714990354092)]
[外链图片转存中…(img-1fywfSz4-1714990354093)]

您可能感兴趣的与本文相关的镜像

AutoGPT

AutoGPT

AI应用

AutoGPT于2023年3月30日由游戏公司Significant Gravitas Ltd.的创始人Toran Bruce Richards发布,AutoGPT是一个AI agent(智能体),也是开源的应用程序,结合了GPT-4和GPT-3.5技术,给定自然语言的目标,它将尝试通过将其分解成子任务,并在自动循环中使用互联网和其他工具来实现这一目标

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值